Skip to content

Commit 80d4854

Browse files
committed
Merge branch 'main' into semantic-non-null
2 parents 90c668c + b7c57ea commit 80d4854

13 files changed

+488
-192
lines changed

.github/algorithm-format-check.mjs

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

.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 2024 Banner: September 10-12, San Francisco. Hosted by the GraphQL Foundation](https://github.com/user-attachments/assets/0203f10b-ae1e-4fe1-9222-6547fa2bbd5d)](https://graphql.org/conf/2024/?utm_source=github&utm_medium=graphql_spec&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.

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.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
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",

0 commit comments

Comments
 (0)