Skip to content

Commit c6cd857

Browse files
committed
Merge branch 'main' into oneof-v2
2 parents e78d2b5 + 4ab71e3 commit c6cd857

9 files changed

+196
-52
lines changed

.github/algorithm-format-check.mjs

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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+
if (matches) {
27+
const [, algorithmName, ns1, _args, ns2, ns3] = matches;
28+
if (ns1 || ns2 || ns3) {
29+
console.log(
30+
`Bad whitespace in definition of ${algorithmName} in '${filename}':`
31+
);
32+
console.log(line);
33+
console.log();
34+
process.exitCode = 1;
35+
}
36+
if (lines[i + 1] !== "") {
37+
console.log(
38+
`No empty space after algorithm ${algorithmName} header in '${filename}'`
39+
);
40+
console.log();
41+
process.exitCode = 1;
42+
}
43+
for (let j = i + 2; j < l; j++) {
44+
const step = lines[j];
45+
if (!step.match(/^\s*(-|[0-9]+\.) /)) {
46+
if (step !== "") {
47+
console.log(
48+
`Bad algorithm ${algorithmName} step in '${filename}':`
49+
);
50+
console.log(step);
51+
console.log();
52+
process.exitCode = 1;
53+
}
54+
break;
55+
}
56+
if (!step.match(/[.:]$/)) {
57+
console.log(
58+
`Bad formatting for '${algorithmName}' step (does not end in '.' or ':') in '${filename}':`
59+
);
60+
console.log(step);
61+
console.log();
62+
process.exitCode = 1;
63+
}
64+
if (step.match(/^\s*(-|[0-9]\.)\s+[a-z]/)) {
65+
console.log(
66+
`Bad formatting of '${algorithmName}' step (should start with a capital) in '${filename}':`
67+
);
68+
console.log(step);
69+
console.log();
70+
process.exitCode = 1;
71+
}
72+
const trimmedInnerLine = step.replace(/\s+/g, " ");
73+
if (
74+
trimmedInnerLine.match(
75+
/(?:[rR]eturn|is (?:not )?)(true|false|null)\b/
76+
) &&
77+
!trimmedInnerLine.match(/null or empty/)
78+
) {
79+
console.log(
80+
`Potential bad formatting of '${algorithmName}' step (true/false/null should be wrapped in curly braces, e.g. '{true}') in '${filename}':`
81+
);
82+
console.log(step);
83+
console.log();
84+
process.exitCode = 1;
85+
}
86+
}
87+
}
88+
}
89+
90+
// Check `- ...:` step is followed by an indent
91+
{
92+
const matches = line.match(/^(\s*)- .*:\s*$/);
93+
if (matches) {
94+
const indent = matches[1];
95+
const nextLine = lines[i + 1];
96+
if (!nextLine.startsWith(`${indent} `)) {
97+
console.log(
98+
`Lacking indent in '${filename}' following ':' character:`
99+
);
100+
console.dir(line);
101+
console.dir(nextLine);
102+
console.log();
103+
// TODO: process.exitCode = 1;
104+
}
105+
}
106+
}
107+
}
108+
}
109+
110+
if (process.exitCode === 0) {
111+
console.log(`Everything looks okay!`);
112+
} else {
113+
console.log(`Please resolve the errors detailed above.`);
114+
}

.github/workflows/ci.yml

+1
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:

STYLE_GUIDE.md

+27
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,30 @@ 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+
```

package.json

+1
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",

spec/Section 2 -- Language.md

+13-13
Original file line numberDiff line numberDiff line change
@@ -1032,7 +1032,7 @@ BlockStringValue(rawValue):
10321032
- Let {lines} be the result of splitting {rawValue} by {LineTerminator}.
10331033
- Let {commonIndent} be {null}.
10341034
- For each {line} in {lines}:
1035-
- If {line} is the first item in {lines}, continue to the next line.
1035+
- If {line} is the first item in {lines}, continue to the next {line}.
10361036
- Let {length} be the number of characters in {line}.
10371037
- Let {indent} be the number of leading consecutive {WhiteSpace} characters in
10381038
{line}.
@@ -1117,10 +1117,10 @@ ListValue : [ ]
11171117
ListValue : [ Value+ ]
11181118

11191119
- Let {inputList} be a new empty list value.
1120-
- For each {Value+}
1120+
- For each {Value+}:
11211121
- Let {value} be the result of evaluating {Value}.
11221122
- Append {value} to {inputList}.
1123-
- Return {inputList}
1123+
- Return {inputList}.
11241124

11251125
### Input Object Values
11261126

@@ -1164,11 +1164,11 @@ ObjectValue : { }
11641164
ObjectValue : { ObjectField+ }
11651165

11661166
- Let {inputObject} be a new input object value with no fields.
1167-
- For each {field} in {ObjectField+}
1167+
- For each {field} in {ObjectField+}:
11681168
- Let {name} be {Name} in {field}.
11691169
- Let {value} be the result of evaluating {Value} in {field}.
11701170
- Add a field to {inputObject} of name {name} containing value {value}.
1171-
- Return {inputObject}
1171+
- Return {inputObject}.
11721172

11731173
## Variables
11741174

@@ -1247,22 +1247,22 @@ input type.
12471247

12481248
Type : Name
12491249

1250-
- Let {name} be the string value of {Name}
1251-
- Let {type} be the type defined in the Schema named {name}
1252-
- {type} must not be {null}
1253-
- Return {type}
1250+
- Let {name} be the string value of {Name}.
1251+
- Let {type} be the type defined in the Schema named {name}.
1252+
- {type} must not be {null}.
1253+
- Return {type}.
12541254

12551255
Type : [ Type ]
12561256

1257-
- Let {itemType} be the result of evaluating {Type}
1257+
- Let {itemType} be the result of evaluating {Type}.
12581258
- Let {type} be a List type where {itemType} is the contained type.
1259-
- Return {type}
1259+
- Return {type}.
12601260

12611261
Type : Type !
12621262

1263-
- Let {nullableType} be the result of evaluating {Type}
1263+
- Let {nullableType} be the result of evaluating {Type}.
12641264
- Let {type} be a Non-Null type where {nullableType} is the contained type.
1265-
- Return {type}
1265+
- Return {type}.
12661266

12671267
## Directives
12681268

spec/Section 3 -- Type System.md

+12-12
Original file line numberDiff line numberDiff line change
@@ -347,23 +347,23 @@ can only be used as input types. Object, Interface, and Union types can only be
347347
used as output types. Lists and Non-Null types may be used as input types or
348348
output types depending on how the wrapped type may be used.
349349

350-
IsInputType(type) :
350+
IsInputType(type):
351351

352352
- If {type} is a List type or Non-Null type:
353353
- Let {unwrappedType} be the unwrapped type of {type}.
354-
- Return IsInputType({unwrappedType})
354+
- Return IsInputType({unwrappedType}).
355355
- If {type} is a Scalar, Enum, or Input Object type:
356-
- Return {true}
357-
- Return {false}
356+
- Return {true}.
357+
- Return {false}.
358358

359-
IsOutputType(type) :
359+
IsOutputType(type):
360360

361361
- If {type} is a List type or Non-Null type:
362362
- Let {unwrappedType} be the unwrapped type of {type}.
363-
- Return IsOutputType({unwrappedType})
363+
- Return IsOutputType({unwrappedType}).
364364
- If {type} is a Scalar, Object, Interface, Union, or Enum type:
365-
- Return {true}
366-
- Return {false}
365+
- Return {true}.
366+
- Return {false}.
367367

368368
### Type Extensions
369369

@@ -613,7 +613,7 @@ other input values must raise a _request error_ indicating an incorrect type.
613613
The ID scalar type represents a unique identifier, often used to refetch an
614614
object or as the key for a cache. The ID type is serialized in the same way as a
615615
{String}; however, it is not intended to be human-readable. While it is often
616-
numeric, it should always serialize as a {String}.
616+
numeric, it must always serialize as a {String}.
617617

618618
**Result Coercion**
619619

@@ -919,7 +919,7 @@ of rules must be adhered to by every Object type in a GraphQL schema.
919919
3. The argument must accept a type where {IsInputType(argumentType)}
920920
returns {true}.
921921
4. If argument type is Non-Null and a default value is not defined:
922-
- The `@deprecated` directive must not be applied to this argument.
922+
1. The `@deprecated` directive must not be applied to this argument.
923923
3. An object type may declare that it implements one or more unique interfaces.
924924
4. An object type must be a super-set of all interfaces it implements:
925925
1. Let this object type be {objectType}.
@@ -1653,7 +1653,7 @@ The value for an input object should be an input object literal or an unordered
16531653
map supplied by a variable, otherwise a _request error_ must be raised. In
16541654
either case, the input object literal or unordered map must not contain any
16551655
entries with names not defined by a field of this input object type, otherwise a
1656-
response error must be raised.
1656+
request error must be raised.
16571657

16581658
The result of coercion is an unordered map with an entry for each field both
16591659
defined by the input object type and for which a value exists. The resulting map
@@ -1768,7 +1768,7 @@ input ExampleInputTagged @oneOf {
17681768
3. The input field must accept a type where {IsInputType(inputFieldType)}
17691769
returns {true}.
17701770
4. If input field type is Non-Null and a default value is not defined:
1771-
- The `@deprecated` directive must not be applied to this input field.
1771+
1. The `@deprecated` directive must not be applied to this input field.
17721772
5. If the Input Object is a OneOf Input Object then:
17731773
1. The type of the input field must be nullable.
17741774
2. The input field must not have a default value.

spec/Section 4 -- Introspection.md

+8-8
Original file line numberDiff line numberDiff line change
@@ -418,8 +418,8 @@ The `__Field` type represents each field in an Object or Interface type.
418418

419419
Fields\:
420420

421-
- `name` must return a String
422-
- `description` may return a String or {null}
421+
- `name` must return a String.
422+
- `description` may return a String or {null}.
423423
- `args` returns a List of `__InputValue` representing the arguments this field
424424
accepts.
425425
- Accepts the argument `includeDeprecated` which defaults to {false}. If
@@ -437,8 +437,8 @@ The `__InputValue` type represents field and directive arguments as well as the
437437

438438
Fields\:
439439

440-
- `name` must return a String
441-
- `description` may return a String or {null}
440+
- `name` must return a String.
441+
- `description` may return a String or {null}.
442442
- `type` must return a `__Type` that represents the type this input value
443443
expects.
444444
- `defaultValue` may return a String encoding (using the GraphQL language) of
@@ -455,8 +455,8 @@ The `__EnumValue` type represents one of possible values of an enum.
455455

456456
Fields\:
457457

458-
- `name` must return a String
459-
- `description` may return a String or {null}
458+
- `name` must return a String.
459+
- `description` may return a String or {null}.
460460
- `isDeprecated` returns {true} if this enum value should no longer be used,
461461
otherwise {false}.
462462
- `deprecationReason` optionally provides a reason why this enum value is
@@ -493,8 +493,8 @@ supported. All possible locations are listed in the `__DirectiveLocation` enum:
493493

494494
Fields\:
495495

496-
- `name` must return a String
497-
- `description` may return a String or {null}
496+
- `name` must return a String.
497+
- `description` may return a String or {null}.
498498
- `locations` returns a List of `__DirectiveLocation` representing the valid
499499
locations this directive may be placed.
500500
- `args` returns a List of `__InputValue` representing the arguments this

spec/Section 5 -- Validation.md

+8-8
Original file line numberDiff line numberDiff line change
@@ -456,25 +456,25 @@ SameResponseShape(fieldA, fieldB):
456456
- Let {typeA} be the return type of {fieldA}.
457457
- Let {typeB} be the return type of {fieldB}.
458458
- If {typeA} or {typeB} is Non-Null:
459-
- If {typeA} or {typeB} is nullable, return false.
459+
- If {typeA} or {typeB} is nullable, return {false}.
460460
- Let {typeA} be the nullable type of {typeA}.
461461
- Let {typeB} be the nullable type of {typeB}.
462462
- If {typeA} or {typeB} is List:
463-
- If {typeA} or {typeB} is not List, return false.
463+
- If {typeA} or {typeB} is not List, return {false}.
464464
- Let {typeA} be the item type of {typeA}.
465465
- Let {typeB} be the item type of {typeB}.
466466
- Repeat from step 3.
467467
- If {typeA} or {typeB} is Scalar or Enum:
468-
- If {typeA} and {typeB} are the same type return true, otherwise return
469-
false.
468+
- If {typeA} and {typeB} are the same type return {true}, otherwise return
469+
{false}.
470470
- Assert: {typeA} and {typeB} are both composite types.
471471
- Let {mergedSet} be the result of adding the selection set of {fieldA} and the
472472
selection set of {fieldB}.
473473
- Let {fieldsForName} be the set of selections with a given response name in
474474
{mergedSet} including visiting fragments and inline fragments.
475475
- Given each pair of members {subfieldA} and {subfieldB} in {fieldsForName}:
476-
- If {SameResponseShape(subfieldA, subfieldB)} is false, return false.
477-
- Return true.
476+
- If {SameResponseShape(subfieldA, subfieldB)} is {false}, return {false}.
477+
- Return {true}.
478478

479479
**Explanatory Text**
480480

@@ -1035,7 +1035,7 @@ is a validation error if the target of a spread is not defined.
10351035

10361036
- For each {fragmentDefinition} in the document:
10371037
- Let {visited} be the empty set.
1038-
- {DetectFragmentCycles(fragmentDefinition, visited)}
1038+
- {DetectFragmentCycles(fragmentDefinition, visited)}.
10391039

10401040
DetectFragmentCycles(fragmentDefinition, visited):
10411041

@@ -1044,7 +1044,7 @@ DetectFragmentCycles(fragmentDefinition, visited):
10441044
- {visited} must not contain {spread}.
10451045
- Let {nextVisited} be the set including {spread} and members of {visited}.
10461046
- Let {nextFragmentDefinition} be the target of {spread}.
1047-
- {DetectFragmentCycles(nextFragmentDefinition, nextVisited)}
1047+
- {DetectFragmentCycles(nextFragmentDefinition, nextVisited)}.
10481048

10491049
**Explanatory Text**
10501050

0 commit comments

Comments
 (0)