@effected/yaml 0.6.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -7
- package/Yaml.js +87 -39
- package/YamlDocument.js +25 -6
- package/YamlFormat.js +171 -20
- package/YamlLint.js +239 -0
- package/YamlLintRule.js +36 -0
- package/YamlNode.js +36 -2
- package/YamlToken.js +139 -0
- package/YamlVisitor.js +40 -5
- package/index.d.ts +439 -12
- package/index.js +4 -1
- package/internal/composer/anchors.js +2 -27
- package/internal/composer/block.js +246 -58
- package/internal/composer/comments.js +187 -0
- package/internal/composer/document.js +49 -12
- package/internal/composer/flow.js +82 -9
- package/internal/composer/scalars.js +77 -28
- package/internal/composer/state.js +16 -2
- package/internal/cst-parser.js +11 -2
- package/internal/diagnostics.js +12 -3
- package/internal/fold.js +22 -10
- package/internal/lexer.js +23 -17
- package/internal/rules/catalog.js +39 -0
- package/internal/rules/colon-spacing.js +77 -0
- package/internal/rules/comments-spacing.js +68 -0
- package/internal/rules/document-end.js +65 -0
- package/internal/rules/document-start.js +64 -0
- package/internal/rules/empty-lines.js +70 -0
- package/internal/rules/eof-newline.js +33 -0
- package/internal/rules/hyphen-spacing.js +52 -0
- package/internal/rules/indentation.js +103 -0
- package/internal/rules/key-duplicates.js +49 -0
- package/internal/rules/line-length.js +36 -0
- package/internal/rules/parse-validity.js +33 -0
- package/internal/rules/quoted-strings.js +81 -0
- package/internal/rules/trailing-spaces.js +39 -0
- package/internal/rules/truthy.js +84 -0
- package/internal/rules/util.js +92 -0
- package/internal/stringifier.js +285 -69
- package/package.json +4 -3
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { YamlEdit } from "../../YamlEdit.js";
|
|
2
|
+
import { YamlLintDiagnostic, YamlLintSeverity } from "../../YamlLintRule.js";
|
|
3
|
+
import { positionAt, walkScalars } from "./util.js";
|
|
4
|
+
import { Schema } from "effect";
|
|
5
|
+
|
|
6
|
+
//#region src/internal/rules/truthy.ts
|
|
7
|
+
/**
|
|
8
|
+
* Options for `truthy`: the `allowed` boolean spellings (default
|
|
9
|
+
* `["true", "false"]`) and whether mapping keys are checked (`checkKeys`,
|
|
10
|
+
* default `true` — the workflow `on:` key is the point).
|
|
11
|
+
*/
|
|
12
|
+
const truthyOptions = Schema.Struct({
|
|
13
|
+
severity: Schema.optionalKey(YamlLintSeverity),
|
|
14
|
+
allowed: Schema.optionalKey(Schema.Array(Schema.String)),
|
|
15
|
+
checkKeys: Schema.optionalKey(Schema.Boolean)
|
|
16
|
+
});
|
|
17
|
+
/** The YAML 1.1 boolean family, per spelling case the 1.1 grammar admits. */
|
|
18
|
+
const TRUTHY = /* @__PURE__ */ new Set([
|
|
19
|
+
"yes",
|
|
20
|
+
"Yes",
|
|
21
|
+
"YES",
|
|
22
|
+
"no",
|
|
23
|
+
"No",
|
|
24
|
+
"NO",
|
|
25
|
+
"on",
|
|
26
|
+
"On",
|
|
27
|
+
"ON",
|
|
28
|
+
"off",
|
|
29
|
+
"Off",
|
|
30
|
+
"OFF",
|
|
31
|
+
"true",
|
|
32
|
+
"True",
|
|
33
|
+
"TRUE",
|
|
34
|
+
"false",
|
|
35
|
+
"False",
|
|
36
|
+
"FALSE"
|
|
37
|
+
]);
|
|
38
|
+
const TRUE_SET = /* @__PURE__ */ new Set([
|
|
39
|
+
"yes",
|
|
40
|
+
"on",
|
|
41
|
+
"true"
|
|
42
|
+
]);
|
|
43
|
+
/** YAML 1.1 truthy spellings outside the allowed list. */
|
|
44
|
+
const truthy = {
|
|
45
|
+
id: "truthy",
|
|
46
|
+
check: (ctx, options) => {
|
|
47
|
+
const opts = options ?? {};
|
|
48
|
+
const allowed = new Set(opts.allowed ?? ["true", "false"]);
|
|
49
|
+
const checkKeys = opts.checkKeys ?? true;
|
|
50
|
+
const out = [];
|
|
51
|
+
walkScalars(ctx.document.contents, "root", (scalar, role) => {
|
|
52
|
+
if (role === "key" && !checkKeys) return;
|
|
53
|
+
if (scalar.style !== "plain" || scalar.tag !== void 0) return;
|
|
54
|
+
const raw = ctx.text.slice(scalar.offset, scalar.offset + scalar.length);
|
|
55
|
+
if (!TRUTHY.has(raw) || allowed.has(raw)) return;
|
|
56
|
+
const pos = positionAt(ctx.lines, scalar.offset);
|
|
57
|
+
const isBool = typeof scalar.value === "boolean";
|
|
58
|
+
const respell = TRUE_SET.has(raw.toLowerCase()) ? "true" : "false";
|
|
59
|
+
const fix = isBool ? allowed.has(respell) ? YamlEdit.make({
|
|
60
|
+
offset: scalar.offset,
|
|
61
|
+
length: scalar.length,
|
|
62
|
+
content: respell
|
|
63
|
+
}) : void 0 : YamlEdit.make({
|
|
64
|
+
offset: scalar.offset,
|
|
65
|
+
length: scalar.length,
|
|
66
|
+
content: `"${raw}"`
|
|
67
|
+
});
|
|
68
|
+
out.push(new YamlLintDiagnostic({
|
|
69
|
+
rule: "truthy",
|
|
70
|
+
severity: "error",
|
|
71
|
+
message: `Truthy value "${raw}" is not in the allowed spellings`,
|
|
72
|
+
offset: scalar.offset,
|
|
73
|
+
length: scalar.length,
|
|
74
|
+
line: pos.line,
|
|
75
|
+
character: pos.character,
|
|
76
|
+
...fix !== void 0 ? { fix } : {}
|
|
77
|
+
}));
|
|
78
|
+
});
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
//#endregion
|
|
84
|
+
export { truthy, truthyOptions };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { YamlMap, YamlScalar, YamlSeq } from "../../YamlNode.js";
|
|
2
|
+
import { Schema } from "effect";
|
|
3
|
+
|
|
4
|
+
//#region src/internal/rules/util.ts
|
|
5
|
+
/**
|
|
6
|
+
* A non-negative integer — the shape every numeric rule option takes.
|
|
7
|
+
* Rejects NaN, negatives and fractions with a message naming the constraint;
|
|
8
|
+
* the config layer's wrapper names the rule and the field.
|
|
9
|
+
*/
|
|
10
|
+
const nonNegativeIntegerOption = Schema.Number.check(Schema.makeFilter((n) => Number.isInteger(n) && n >= 0 ? void 0 : "Expected a non-negative integer"));
|
|
11
|
+
/**
|
|
12
|
+
* A positive integer — for the `maxSpacesAfter` options, where `0` would make
|
|
13
|
+
* the fix delete the separation space after an indicator and fuse it with its
|
|
14
|
+
* content (`- item` → `-item`, `a: val` → `a:val` — different tokens, not a
|
|
15
|
+
* spacing change).
|
|
16
|
+
*/
|
|
17
|
+
const positiveIntegerOption = Schema.Number.check(Schema.makeFilter((n) => Number.isInteger(n) && n >= 1 ? void 0 : "Expected an integer greater than or equal to 1"));
|
|
18
|
+
/** Depth-first walk over every scalar node with its structural role. */
|
|
19
|
+
function walkScalars(node, role, visit) {
|
|
20
|
+
if (node === null) return;
|
|
21
|
+
if (node instanceof YamlScalar) {
|
|
22
|
+
visit(node, role);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (node instanceof YamlMap) {
|
|
26
|
+
for (const pair of node.items) {
|
|
27
|
+
walkScalars(pair.key, "key", visit);
|
|
28
|
+
walkScalars(pair.value, "value", visit);
|
|
29
|
+
}
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (node instanceof YamlSeq) for (const item of node.items) walkScalars(item, "item", visit);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* True when the first content of a line is the CONTINUATION of a scalar
|
|
36
|
+
* token that began on an earlier line — block-scalar bodies and multi-line
|
|
37
|
+
* plain/quoted scalars. The indentation rule skips such lines: their layout
|
|
38
|
+
* is the value's, not block structure's.
|
|
39
|
+
*/
|
|
40
|
+
function isScalarContinuationLine(tokens, lineOffset, probeOffset) {
|
|
41
|
+
const token = coveringToken(tokens, probeOffset);
|
|
42
|
+
return token !== void 0 && token.kind === "scalar" && token.offset < lineOffset;
|
|
43
|
+
}
|
|
44
|
+
/** The token whose span covers `offset`, when one does. */
|
|
45
|
+
function coveringToken(tokens, offset) {
|
|
46
|
+
let lo = 0;
|
|
47
|
+
let hi = tokens.length - 1;
|
|
48
|
+
while (lo <= hi) {
|
|
49
|
+
const mid = lo + hi >> 1;
|
|
50
|
+
const token = tokens[mid];
|
|
51
|
+
if (offset < token.offset) hi = mid - 1;
|
|
52
|
+
else if (offset >= token.offset + token.length) lo = mid + 1;
|
|
53
|
+
else return token;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* True when `offset` falls inside a scalar token's span. Layout rules use
|
|
58
|
+
* this to stay off scalar CONTENT — trailing whitespace or blank lines
|
|
59
|
+
* inside a block scalar are part of the parsed value, and a lint layer that
|
|
60
|
+
* edits content under the banner of layout is corrupting, not fixing.
|
|
61
|
+
*/
|
|
62
|
+
function insideScalarSpan(tokens, offset) {
|
|
63
|
+
return coveringToken(tokens, offset)?.kind === "scalar";
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The line containing `offset` and the character index within it — a binary
|
|
67
|
+
* search over the ordered `LintLine` array (lines are ordered by `offset`,
|
|
68
|
+
* the same invariant {@link coveringToken} rests on for tokens).
|
|
69
|
+
*/
|
|
70
|
+
function positionAt(lines, offset) {
|
|
71
|
+
let lo = 0;
|
|
72
|
+
let hi = lines.length - 1;
|
|
73
|
+
let found;
|
|
74
|
+
while (lo <= hi) {
|
|
75
|
+
const mid = lo + hi >> 1;
|
|
76
|
+
const line = lines[mid];
|
|
77
|
+
if (line.offset <= offset) {
|
|
78
|
+
found = line;
|
|
79
|
+
lo = mid + 1;
|
|
80
|
+
} else hi = mid - 1;
|
|
81
|
+
}
|
|
82
|
+
return found === void 0 ? {
|
|
83
|
+
line: 0,
|
|
84
|
+
character: 0
|
|
85
|
+
} : {
|
|
86
|
+
line: found.number,
|
|
87
|
+
character: offset - found.offset
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
//#endregion
|
|
92
|
+
export { coveringToken, insideScalarSpan, isScalarContinuationLine, nonNegativeIntegerOption, positionAt, positiveIntegerOption, walkScalars };
|