-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
105 lines (93 loc) · 2.53 KB
/
Copy pathindex.js
File metadata and controls
105 lines (93 loc) · 2.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import { createHash } from "node:crypto";
// Normalize a run of GraphQL source that contains no string literals:
// strip comments, collapse insignificant whitespace, and remove whitespace
// around structural punctuation.
function normalizeOutsideStrings(segment) {
return segment
.replaceAll(/#[^\n]*/g, "")
.replaceAll(/\s+/g, " ")
.replaceAll(/\s*([{}(),:@=])\s*/g, "$1");
}
function findBlockStringEnd(query, start) {
let end = start;
while (end < query.length) {
end = query.indexOf('"""', end);
let backslashes = 0;
for (
let cursor = end - 1;
end !== -1 && query[cursor] === "\\";
cursor -= 1
) {
backslashes += 1;
}
if (end === -1 || backslashes % 2 === 0) {
return end;
}
end += 3;
}
return -1;
}
function findRegularStringEnd(query, start) {
let cursor = start;
while (cursor < query.length) {
if (query[cursor] === "\\") {
cursor += 2;
continue;
}
if (query[cursor] === '"') {
return cursor + 1;
}
cursor += 1;
}
return query.length;
}
export function normalizeQuery(query) {
if (typeof query !== "string") {
throw new TypeError("Expected `query` to be a string");
}
let result = "";
let pending = "";
let index = 0;
const { length } = query;
const flush = () => {
result += normalizeOutsideStrings(pending);
pending = "";
};
while (index < length) {
// A quote in a comment is ordinary comment text, not a string opener.
if (query[index] === "#") {
while (index < length && query[index] !== "\n") {
index += 1;
}
pending += "\n";
index += 1;
continue;
}
// Block string ("""..."""): preserve verbatim, including # and whitespace.
if (query.startsWith('"""', index)) {
flush();
const end = findBlockStringEnd(query, index + 3);
const stop = end === -1 ? length : end + 3;
result += query.slice(index, stop);
index = stop;
continue;
}
// Regular string ("..."): preserve verbatim, honoring backslash escapes.
if (query[index] === '"') {
flush();
const cursor = findRegularStringEnd(query, index + 1);
result += query.slice(index, cursor);
index = cursor;
continue;
}
pending += query[index];
index += 1;
}
flush();
return result.trim();
}
export default function graphqlHash(query, options = {}) {
const { algorithm = "sha256" } = options;
const normalized = normalizeQuery(query);
return createHash(algorithm).update(normalized).digest("hex");
}