Skip to content

Commit 34ac2da

Browse files
authored
Merge branch 'main' into rfc-visited-fragments
2 parents 0d2a3bb + b1a23de commit 34ac2da

21 files changed

+1785
-597
lines changed

.github/algorithm-format-check.mjs

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
import { readFile, readdir } from "node:fs/promises";
2+
3+
const SPEC_DIR = new URL("../spec", import.meta.url).pathname;
4+
5+
/** @see {@link https://spec-md.com/#sec-Value-Literals} */
6+
const valueLiteralsRegexp = /\{((?:[^{}]|(?:\{[^{}]*\}))+)\}/g;
7+
8+
process.exitCode = 0;
9+
const filenames = await readdir(SPEC_DIR);
10+
for (const filename of filenames) {
11+
if (!filename.endsWith(".md")) {
12+
continue;
13+
}
14+
const markdown = await readFile(`${SPEC_DIR}/${filename}`, "utf8");
15+
16+
/**
17+
* Not strictly 'lines' since we try and group indented things together as if
18+
* they were one line. Close enough though.
19+
*/
20+
const lines = markdown.split(/\n(?=[\S\n]|\s*(?:-|[0-9]+\.) )/);
21+
22+
for (let i = 0, l = lines.length; i < l; i++) {
23+
const line = lines[i];
24+
25+
// Check algorithm is consistently formatted
26+
{
27+
// Is it an algorithm definition?
28+
const matches = line.match(/^([a-z0-9A-Z]+)(\s*)\(([^)]*)\)(\s*):(\s*)$/);
29+
const grammarMatches =
30+
filename === "Section 2 -- Language.md" &&
31+
line.match(/^([A-Za-z0-9]+) ::?\s+((\S).*)$/);
32+
if (matches) {
33+
const [, algorithmName, ns1, _args, ns2, ns3] = matches;
34+
if (ns1 || ns2 || ns3) {
35+
console.log(
36+
`Bad whitespace in definition of ${algorithmName} in '${filename}':`
37+
);
38+
console.dir(line);
39+
console.log();
40+
process.exitCode = 1;
41+
}
42+
if (lines[i + 1] !== "") {
43+
console.log(
44+
`No empty space after algorithm ${algorithmName} header in '${filename}'`
45+
);
46+
console.log();
47+
process.exitCode = 1;
48+
}
49+
for (let j = i + 2; j < l; j++) {
50+
const step = lines[j];
51+
if (!step.match(/^\s*(-|[0-9]+\.) /)) {
52+
if (step !== "") {
53+
console.log(
54+
`Bad algorithm ${algorithmName} step in '${filename}':`
55+
);
56+
console.dir(step);
57+
console.log();
58+
process.exitCode = 1;
59+
}
60+
break;
61+
}
62+
if (!step.match(/[.:]$/)) {
63+
console.log(
64+
`Bad formatting for '${algorithmName}' step (does not end in '.' or ':') in '${filename}':`
65+
);
66+
console.dir(step);
67+
console.log();
68+
process.exitCode = 1;
69+
}
70+
if (step.match(/^\s*(-|[0-9]+\.)\s+[a-z]/)) {
71+
console.log(
72+
`Bad formatting of '${algorithmName}' step (should start with a capital) in '${filename}':`
73+
);
74+
console.dir(step);
75+
console.log();
76+
process.exitCode = 1;
77+
}
78+
const assertMatch = step.match(/^\s*(-|[0-9]+\.)\s*Assert([^:])/);
79+
if (assertMatch) {
80+
console.log(
81+
`Bad formatting of '${algorithmName}' step (Assert should be immediately followed by ':'; found '${assertMatch[2]}') in '${filename}':`
82+
);
83+
console.dir(step);
84+
console.log();
85+
process.exitCode = 1;
86+
}
87+
88+
const stepWithoutValueLiterals = step.replace(
89+
valueLiteralsRegexp,
90+
""
91+
);
92+
if (stepWithoutValueLiterals.match(/\b[A-Z][A-Za-z0-9]+\(/)) {
93+
console.log(
94+
`Bad formatting of '${algorithmName}' step (algorithm call should be wrapped in braces: \`{MyAlgorithm(a, b, c)}\`) in '${filename}':`
95+
);
96+
console.dir(step);
97+
console.log();
98+
process.exitCode = 1;
99+
}
100+
101+
const valueLiterals = step.matchAll(valueLiteralsRegexp, "");
102+
for (const lit of valueLiterals) {
103+
const inner = lit[1];
104+
if (inner.includes("{")) {
105+
console.log(
106+
`Bad formatting of '${algorithmName}' step (algorithm call should not contain braces: \`${lit}\`) in '${filename}':`
107+
);
108+
console.dir(step);
109+
console.log();
110+
process.exitCode = 1;
111+
}
112+
}
113+
114+
const trimmedInnerLine = step.replace(/\s+/g, " ");
115+
if (
116+
trimmedInnerLine.match(
117+
/(?:[rR]eturn|is (?:not )?)(true|false|null)\b/
118+
) &&
119+
!trimmedInnerLine.match(/null or empty/)
120+
) {
121+
console.log(
122+
`Potential bad formatting of '${algorithmName}' step (true/false/null should be wrapped in curly braces, e.g. '{true}') in '${filename}':`
123+
);
124+
console.dir(step);
125+
console.log();
126+
process.exitCode = 1;
127+
}
128+
}
129+
} else if (grammarMatches) {
130+
// This is super loosey-goosey
131+
const [, grammarName, rest] = grammarMatches;
132+
if (rest.trim() === "one of") {
133+
// Still grammar, not algorithm
134+
continue;
135+
}
136+
if (rest.trim() === "" && lines[i + 1] !== "") {
137+
console.log(
138+
`No empty space after grammar ${grammarName} header in '${filename}'`
139+
);
140+
console.log();
141+
process.exitCode = 1;
142+
}
143+
while (lines[i + 1].trim() !== "") {
144+
// Continuation of definition
145+
i++;
146+
}
147+
if (!lines[i + 2].startsWith("- ")) {
148+
// Not an algorithm; probably more grammar
149+
continue;
150+
}
151+
for (let j = i + 2; j < l; j++) {
152+
const step = lines[j];
153+
if (!step.match(/^\s*(-|[0-9]+\.) /)) {
154+
if (step !== "") {
155+
console.log(`Bad grammar ${grammarName} step in '${filename}':`);
156+
console.dir(step);
157+
console.log();
158+
process.exitCode = 1;
159+
}
160+
break;
161+
}
162+
if (!step.match(/[.:]$/)) {
163+
console.log(
164+
`Bad formatting for '${grammarName}' step (does not end in '.' or ':') in '${filename}':`
165+
);
166+
console.dir(step);
167+
console.log();
168+
process.exitCode = 1;
169+
}
170+
if (step.match(/^\s*(-|[0-9]\.)\s+[a-z]/)) {
171+
console.log(
172+
`Bad formatting of '${grammarName}' step (should start with a capital) in '${filename}':`
173+
);
174+
console.dir(step);
175+
console.log();
176+
process.exitCode = 1;
177+
}
178+
const trimmedInnerLine = step.replace(/\s+/g, " ");
179+
if (
180+
trimmedInnerLine.match(
181+
/(?:[rR]eturn|is (?:not )?)(true|false|null)\b/
182+
) &&
183+
!trimmedInnerLine.match(/null or empty/)
184+
) {
185+
console.log(
186+
`Potential bad formatting of '${grammarName}' step (true/false/null should be wrapped in curly braces, e.g. '{true}') in '${filename}':`
187+
);
188+
console.dir(step);
189+
console.log();
190+
process.exitCode = 1;
191+
}
192+
const assertMatch = step.match(/^\s*(-|[0-9]+\.)\s*Assert([^:])/);
193+
if (assertMatch) {
194+
console.log(
195+
`Bad formatting of '${grammarName}' step (Assert should be immediately followed by ':'; found '${assertMatch[2]}') in '${filename}':`
196+
);
197+
console.dir(step);
198+
console.log();
199+
process.exitCode = 1;
200+
}
201+
}
202+
}
203+
}
204+
205+
// Check `- ...:` step is followed by an indent
206+
{
207+
const matches = line.match(/^(\s*)- .*:\s*$/);
208+
if (matches) {
209+
const indent = matches[1];
210+
const nextLine = lines[i + 1];
211+
if (!nextLine.startsWith(`${indent} `)) {
212+
console.log(
213+
`Lacking indent in '${filename}' following ':' character:`
214+
);
215+
console.dir(line);
216+
console.dir(nextLine);
217+
console.log();
218+
// TODO: process.exitCode = 1;
219+
}
220+
}
221+
}
222+
}
223+
}
224+
225+
if (process.exitCode === 0) {
226+
console.log(`Everything looks okay!`);
227+
} else {
228+
console.log(`Please resolve the errors detailed above.`);
229+
}

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ jobs:
2121
- uses: actions/setup-node@v3
2222
- run: npm ci
2323
- run: npm run test:format
24+
- run: npm run test:algorithm-format
2425
test-build:
2526
runs-on: ubuntu-latest
2627
steps:

CONTRIBUTING.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ contributions.
66

77
Contributions that do not change the interpretation of the spec but instead
88
improve legibility, fix editorial errors, clear up ambiguity and improve
9-
examples are encouraged and are often merged by a spec editor with little
10-
process.
9+
examples are encouraged. These "editorial changes" will normally be given the
10+
["✏ Editorial" label](https://github.com/graphql/graphql-spec/issues?q=sort%3Aupdated-desc+is%3Aopen+label%3A%22%E2%9C%8F%EF%B8%8F+Editorial%22)
11+
and are often merged by a spec editor with little process.
1112

1213
However, contributions that _do_ meaningfully change the interpretation of the
1314
spec must follow an RFC (Request For Comments) process led by a _champion_

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
[![GraphQLConf 2025 Banner: September 08-10, Amsterdam. Hosted by the GraphQL Foundation](./assets/graphql.org_conf_2025_.png)](https://graphql.org/conf/2025/?utm_source=github&utm_medium=graphql_js&utm_campaign=readme)
2+
13
# GraphQL
24

35
<img alt="GraphQL Logo" align="right" src="https://graphql.org/img/logo.svg" width="15%" />

STYLE_GUIDE.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,52 @@ hyphens) should be capitalized, with the following exceptions:
5555
All elements in hyphenated words follow the same rules, e.g. headings may
5656
contain `Non-Null`, `Context-Free`, `Built-in` (`in` is a preposition, so is not
5757
capitalized).
58+
59+
## Algorithms
60+
61+
A named algorithm definition starts with the name of the algorithm in
62+
`PascalCase`, an open parenthesis, a comma-and-space separated list of
63+
arguments, a close parenthesis and then a colon. It is followed by a blank
64+
newline and a list of steps in the algorithm which may be numbered or bulleted.
65+
66+
Each step in an algorithm should either end in a colon (`:`) with an indented
67+
step on the next line, or a fullstop (`.`). (A step after a step ending in a
68+
full stop may or may not be indented, use your discretion.)
69+
70+
Indentation in algorithms is significant.
71+
72+
Every step in an algorithm should start with a capital letter.
73+
74+
```
75+
MyAlgorithm(argOne, argTwo):
76+
77+
- Let {something} be {true}.
78+
- For each {arg} in {argOne}:
79+
- If {arg} is greater than {argTwo}:
80+
- Let {something} be {false}.
81+
- Otherwise if {arg} is less than {argTwo}:
82+
- Let {something} be {true}.
83+
- Return {something}.
84+
```
85+
86+
## Definitions
87+
88+
For important terms, use
89+
[Spec Markdown definition paragraphs](https://spec-md.com/#sec-Definition-Paragraph).
90+
91+
Definition paragraphs start with `::` and add the matching italicized term to
92+
the [specification index](https://spec.graphql.org/draft/#index), making it easy
93+
to reference them.
94+
95+
## Tone of voice
96+
97+
The GraphQL specification is a reference document and should use neutral and
98+
descriptive tone of voice.
99+
100+
**Favor the present tense**
101+
102+
The present tense is usually clearer and shorter:
103+
104+
✅ Present: The client then sends a request to the server.
105+
106+
❌ Future: The client will then send a request to the server.

assets/graphql.org_conf_2025_.png

488 KB
Loading

cspell.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@ words:
2121
- tatooine
2222
- zuck
2323
- zuckerberg
24+
# Forbid Alternative spellings
25+
flagWords:
26+
- implementor
27+
- implementors

package-lock.json

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,19 @@
1717
"test:spelling": "cspell \"spec/**/*.md\" README.md",
1818
"format": "prettier --write \"**/*.{md,yml,yaml,json}\"",
1919
"test:format": "prettier --check \"**/*.{md,yml,yaml,json}\" || npm run suggest:format",
20+
"test:algorithm-format": "node .github/algorithm-format-check.mjs",
2021
"suggest:format": "echo \"\nTo resolve this, run: $(tput bold)npm run format$(tput sgr0)\" && exit 1",
2122
"build": "./build.sh",
2223
"test:build": "spec-md --metadata spec/metadata.json spec/GraphQL.md > /dev/null",
23-
"watch": "nodemon -e json,md --exec \"npm run build\""
24+
"watch": "nodemon -e json,md --exec \"npm run build\"",
25+
"update-appendix-c": "node scripts/update-appendix-c.mjs; prettier --write \"spec/Appendix C -- Built-in Definitions.md\""
2426
},
2527
"devDependencies": {
2628
"cspell": "5.9.1",
2729
"nodemon": "2.0.20",
2830
"prettier": "2.8.2",
29-
"spec-md": "3.1.0"
31+
"spec-md": "3.1.0",
32+
"graphql": "^17.0.0-alpha.8"
3033
},
3134
"prettier": {
3235
"proseWrap": "always",

0 commit comments

Comments
 (0)