@eslint-sh/eslint-plugin 0.5.0 → 0.7.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 +228 -17
- package/dist/command-harvest/builtins/bash.d.ts +36 -0
- package/dist/command-harvest/builtins/bash.js +98 -0
- package/dist/command-harvest/builtins/bats.d.ts +45 -0
- package/dist/command-harvest/builtins/bats.js +33 -0
- package/dist/command-harvest/builtins/index.d.ts +18 -0
- package/dist/command-harvest/builtins/index.js +39 -0
- package/dist/command-harvest/builtins/posix.d.ts +51 -0
- package/dist/command-harvest/builtins/posix.js +93 -0
- package/dist/command-harvest/harvest-commands.d.ts +66 -0
- package/dist/command-harvest/harvest-commands.js +97 -0
- package/dist/command-harvest/wrapper-table.d.ts +24 -0
- package/dist/command-harvest/wrapper-table.js +49 -0
- package/dist/configs/agent-authored.d.ts +41 -0
- package/dist/configs/agent-authored.js +33 -0
- package/dist/configs/bash-3-2.d.ts +28 -0
- package/dist/configs/bash-3-2.js +51 -0
- package/dist/configs/posix-portable.d.ts +40 -0
- package/dist/configs/posix-portable.js +53 -0
- package/dist/configs/recommended.d.ts +52 -0
- package/dist/configs/recommended.js +34 -0
- package/dist/declaration-conventions/comment-pragma.d.ts +33 -0
- package/dist/declaration-conventions/comment-pragma.js +153 -0
- package/dist/declaration-conventions/declaration-convention.d.ts +189 -0
- package/dist/declaration-conventions/declaration-convention.js +1 -0
- package/dist/declaration-conventions/read-declared-commands.d.ts +70 -0
- package/dist/declaration-conventions/read-declared-commands.js +128 -0
- package/dist/eslint-plugin-syntax-analyze.d.ts +23 -0
- package/dist/eslint-plugin-syntax.d.ts +27 -0
- package/dist/eslint-plugin.d.ts +1211 -52
- package/dist/index.d.ts +56 -8
- package/dist/index.js +53 -6
- package/dist/language/command-resolution.d.ts +124 -0
- package/dist/language/command-resolution.js +120 -0
- package/dist/language/end-of-input-marker.d.ts +29 -0
- package/dist/language/end-of-input-marker.js +88 -0
- package/dist/language/line-breaks.d.ts +18 -0
- package/dist/language/line-breaks.js +22 -0
- package/dist/language/lint-view-types.d.ts +104 -0
- package/dist/language/lint-view-types.js +1 -0
- package/dist/language/non-shell-file-guard.d.ts +35 -0
- package/dist/language/non-shell-file-guard.js +59 -0
- package/dist/language/selector-classes.d.ts +0 -1
- package/dist/language/selector-classes.js +0 -1
- package/dist/language/shell-language-options.d.ts +7 -1
- package/dist/language/shell-language-options.js +4 -4
- package/dist/language/shell-language.d.ts +10 -2
- package/dist/language/shell-language.js +59 -8
- package/dist/language/shell-source-code.d.ts +342 -5
- package/dist/language/shell-source-code.js +559 -30
- package/dist/language/source-coordinates.d.ts +13 -2
- package/dist/language/source-coordinates.js +68 -14
- package/dist/language/syntax-view.d.ts +60 -39
- package/dist/language/syntax-view.js +200 -45
- package/dist/language/word-resolution.d.ts +4 -3
- package/dist/language/word-resolution.js +4 -3
- package/dist/rules/compat/create-compat-rule.d.ts +59 -0
- package/dist/rules/compat/create-compat-rule.js +65 -0
- package/dist/rules/compat/declaration-options.d.ts +19 -0
- package/dist/rules/compat/declaration-options.js +110 -0
- package/dist/rules/compat/no-append-all-redirect.d.ts +13 -0
- package/dist/rules/compat/no-append-all-redirect.js +73 -0
- package/dist/rules/compat/no-associative-arrays.d.ts +12 -0
- package/dist/rules/compat/no-associative-arrays.js +39 -0
- package/dist/rules/compat/no-brace-sequence-padding-step.d.ts +15 -0
- package/dist/rules/compat/no-brace-sequence-padding-step.js +287 -0
- package/dist/rules/compat/no-case-attributes.d.ts +12 -0
- package/dist/rules/compat/no-case-attributes.js +63 -0
- package/dist/rules/compat/no-case-fallthrough.d.ts +12 -0
- package/dist/rules/compat/no-case-fallthrough.js +67 -0
- package/dist/rules/compat/no-case-modification.d.ts +35 -0
- package/dist/rules/compat/no-case-modification.js +86 -0
- package/dist/rules/compat/no-coproc.d.ts +12 -0
- package/dist/rules/compat/no-coproc.js +47 -0
- package/dist/rules/compat/no-declare-g.d.ts +12 -0
- package/dist/rules/compat/no-declare-g.js +59 -0
- package/dist/rules/compat/no-double-bracket.d.ts +12 -0
- package/dist/rules/compat/no-double-bracket.js +52 -0
- package/dist/rules/compat/no-epoch-variables.d.ts +12 -0
- package/dist/rules/compat/no-epoch-variables.js +57 -0
- package/dist/rules/compat/no-fd-variable.d.ts +12 -0
- package/dist/rules/compat/no-fd-variable.js +48 -0
- package/dist/rules/compat/no-globstar.d.ts +12 -0
- package/dist/rules/compat/no-globstar.js +39 -0
- package/dist/rules/compat/no-lastpipe-semantics.d.ts +12 -0
- package/dist/rules/compat/no-lastpipe-semantics.js +53 -0
- package/dist/rules/compat/no-let.d.ts +12 -0
- package/dist/rules/compat/no-let.js +62 -0
- package/dist/rules/compat/no-mapfile.d.ts +13 -0
- package/dist/rules/compat/no-mapfile.js +122 -0
- package/dist/rules/compat/no-nameref.d.ts +12 -0
- package/dist/rules/compat/no-nameref.js +44 -0
- package/dist/rules/compat/no-negative-array-index.d.ts +13 -0
- package/dist/rules/compat/no-negative-array-index.js +123 -0
- package/dist/rules/compat/no-nounset-empty-array.d.ts +13 -0
- package/dist/rules/compat/no-nounset-empty-array.js +158 -0
- package/dist/rules/compat/no-parameter-transformation.d.ts +13 -0
- package/dist/rules/compat/no-parameter-transformation.js +123 -0
- package/dist/rules/compat/no-pipe-all.d.ts +13 -0
- package/dist/rules/compat/no-pipe-all.js +68 -0
- package/dist/rules/compat/no-printf-time-format.d.ts +12 -0
- package/dist/rules/compat/no-printf-time-format.js +99 -0
- package/dist/rules/compat/no-source.d.ts +13 -0
- package/dist/rules/compat/no-source.js +103 -0
- package/dist/rules/compat/no-test-v.d.ts +12 -0
- package/dist/rules/compat/no-test-v.js +280 -0
- package/dist/rules/compat/no-unicode-escape.d.ts +14 -0
- package/dist/rules/compat/no-unicode-escape.js +120 -0
- package/dist/rules/compat/no-wait-n.d.ts +12 -0
- package/dist/rules/compat/no-wait-n.js +73 -0
- package/dist/rules/compat/operator-range.d.ts +10 -0
- package/dist/rules/compat/operator-range.js +15 -0
- package/dist/rules/compat/posix-absent-command.d.ts +12 -0
- package/dist/rules/compat/posix-absent-command.js +134 -0
- package/dist/rules/compat/shopt-options.d.ts +9 -0
- package/dist/rules/compat/shopt-options.js +63 -0
- package/dist/rules/compat/version-gates.d.ts +76 -0
- package/dist/rules/compat/version-gates.js +205 -0
- package/dist/rules/create-rule.d.ts +91 -0
- package/dist/rules/create-rule.js +51 -0
- package/dist/rules/create-wrapper-rule.d.ts +166 -0
- package/dist/rules/create-wrapper-rule.js +407 -0
- package/dist/rules/define-shell-rule.d.ts +184 -0
- package/dist/rules/define-shell-rule.js +56 -0
- package/dist/rules/index.d.ts +26 -0
- package/dist/rules/index.js +90 -0
- package/dist/rules/max-script-complexity.d.ts +28 -0
- package/dist/rules/max-script-complexity.js +130 -0
- package/dist/rules/no-dynamic-invocation.d.ts +35 -0
- package/dist/rules/no-dynamic-invocation.js +117 -0
- package/dist/rules/no-eval.d.ts +13 -0
- package/dist/rules/no-eval.js +89 -0
- package/dist/rules/no-masked-exit-status.d.ts +21 -0
- package/dist/rules/no-masked-exit-status.js +667 -0
- package/dist/rules/no-secret-env-reads.d.ts +24 -0
- package/dist/rules/no-secret-env-reads.js +133 -0
- package/dist/rules/no-secret-flags.d.ts +32 -0
- package/dist/rules/no-secret-flags.js +162 -0
- package/dist/rules/no-unchecked-cd.d.ts +44 -0
- package/dist/rules/no-unchecked-cd.js +250 -0
- package/dist/rules/no-undeclared-commands.d.ts +20 -0
- package/dist/rules/no-undeclared-commands.js +115 -0
- package/dist/rules/no-unused-declarations.d.ts +23 -0
- package/dist/rules/no-unused-declarations.js +259 -0
- package/dist/rules/prefer-local.d.ts +16 -0
- package/dist/rules/prefer-local.js +249 -0
- package/dist/rules/quote-expansions.d.ts +13 -0
- package/dist/rules/quote-expansions.js +229 -0
- package/dist/rules/require-network-wrapper.d.ts +40 -0
- package/dist/rules/require-network-wrapper.js +244 -0
- package/dist/rules/require-shebang.d.ts +29 -0
- package/dist/rules/require-shebang.js +101 -0
- package/dist/rules/require-strict-mode.d.ts +52 -0
- package/dist/rules/require-strict-mode.js +177 -0
- package/dist/rules/require-tty-guard.d.ts +13 -0
- package/dist/rules/require-tty-guard.js +318 -0
- package/dist/rules/resolve-declaration-convention.d.ts +96 -0
- package/dist/rules/resolve-declaration-convention.js +109 -0
- package/dist/rules/resolve-network-wrapper.d.ts +19 -0
- package/dist/rules/resolve-network-wrapper.js +116 -0
- package/dist/rules/set-options.d.ts +109 -0
- package/dist/rules/set-options.js +201 -0
- package/dist/rules/shebang-interpreter.d.ts +18 -0
- package/dist/rules/shebang-interpreter.js +81 -0
- package/dist/rules/shebang-matches-dialect.d.ts +12 -0
- package/dist/rules/shebang-matches-dialect.js +69 -0
- package/dist/rules/transparent-wrappers.d.ts +52 -0
- package/dist/rules/transparent-wrappers.js +99 -0
- package/dist/rules/user-pattern.d.ts +63 -0
- package/dist/rules/user-pattern.js +184 -0
- package/dist/syntax/analyze.d.ts +19 -0
- package/dist/syntax/analyze.js +21 -0
- package/dist/syntax/index.d.ts +23 -0
- package/dist/syntax/index.js +25 -0
- package/dist/version.d.ts +0 -1
- package/dist/version.js +0 -1
- package/docs/compat.md +354 -0
- package/docs/custom-rule-tutorial.md +780 -34
- package/docs/declaration-conventions.md +478 -0
- package/docs/examples/audit-wrapper-pack.mjs +15 -0
- package/docs/examples/dep-line-convention.mjs +22 -0
- package/docs/examples/eslint.config.mjs +3 -2
- package/docs/examples/no-and-or-as-if-rule.mjs +61 -0
- package/docs/examples/no-backticks-rule-tester.mjs +106 -0
- package/docs/examples/no-backticks-rule.mjs +60 -0
- package/docs/examples/no-curl-in-functions-rule.mjs +53 -0
- package/docs/examples/no-function-keyword-rule.mjs +59 -0
- package/docs/examples/no-listed-commands-rule.ts +63 -0
- package/docs/examples/no-rm-rf-rule.mjs +26 -15
- package/docs/examples/no-unquoted-rm-variable-rule.ts +54 -0
- package/docs/examples/sidecar-convention.mjs +38 -0
- package/docs/examples/sidecar-snapshot-convention.mjs +26 -0
- package/docs/examples/sorted-declarations-rule.mjs +52 -0
- package/docs/reading-nodes.md +159 -0
- package/docs/rules/README.md +79 -0
- package/docs/rules/_template.md +83 -0
- package/docs/rules/compat/no-append-all-redirect.md +87 -0
- package/docs/rules/compat/no-associative-arrays.md +95 -0
- package/docs/rules/compat/no-brace-sequence-padding-step.md +90 -0
- package/docs/rules/compat/no-case-attributes.md +82 -0
- package/docs/rules/compat/no-case-fallthrough.md +90 -0
- package/docs/rules/compat/no-case-modification.md +74 -0
- package/docs/rules/compat/no-coproc.md +76 -0
- package/docs/rules/compat/no-declare-g.md +80 -0
- package/docs/rules/compat/no-double-bracket.md +72 -0
- package/docs/rules/compat/no-epoch-variables.md +79 -0
- package/docs/rules/compat/no-fd-variable.md +78 -0
- package/docs/rules/compat/no-globstar.md +74 -0
- package/docs/rules/compat/no-lastpipe-semantics.md +73 -0
- package/docs/rules/compat/no-let.md +78 -0
- package/docs/rules/compat/no-mapfile.md +118 -0
- package/docs/rules/compat/no-nameref.md +81 -0
- package/docs/rules/compat/no-negative-array-index.md +87 -0
- package/docs/rules/compat/no-nounset-empty-array.md +103 -0
- package/docs/rules/compat/no-parameter-transformation.md +94 -0
- package/docs/rules/compat/no-pipe-all.md +85 -0
- package/docs/rules/compat/no-printf-time-format.md +75 -0
- package/docs/rules/compat/no-source.md +98 -0
- package/docs/rules/compat/no-test-v.md +82 -0
- package/docs/rules/compat/no-unicode-escape.md +91 -0
- package/docs/rules/compat/no-wait-n.md +85 -0
- package/docs/rules/max-script-complexity.md +109 -0
- package/docs/rules/no-dynamic-invocation.md +139 -0
- package/docs/rules/no-eval.md +106 -0
- package/docs/rules/no-masked-exit-status.md +239 -0
- package/docs/rules/no-secret-env-reads.md +173 -0
- package/docs/rules/no-secret-flags.md +169 -0
- package/docs/rules/no-unchecked-cd.md +146 -0
- package/docs/rules/no-undeclared-commands.md +141 -0
- package/docs/rules/no-unused-declarations.md +216 -0
- package/docs/rules/prefer-local.md +221 -0
- package/docs/rules/quote-expansions.md +157 -0
- package/docs/rules/require-network-wrapper.md +171 -0
- package/docs/rules/require-shebang.md +157 -0
- package/docs/rules/require-strict-mode.md +253 -0
- package/docs/rules/require-tty-guard.md +131 -0
- package/docs/rules/shebang-matches-dialect.md +120 -0
- package/docs/selectors.md +2 -2
- package/docs/shellcheck.md +87 -0
- package/docs/untrusted-code.md +440 -0
- package/docs/wrapper-rules.md +206 -0
- package/package.json +26 -20
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/language/selector-classes.d.ts.map +0 -1
- package/dist/language/selector-classes.js.map +0 -1
- package/dist/language/shell-language-options.d.ts.map +0 -1
- package/dist/language/shell-language-options.js.map +0 -1
- package/dist/language/shell-language.d.ts.map +0 -1
- package/dist/language/shell-language.js.map +0 -1
- package/dist/language/shell-source-code.d.ts.map +0 -1
- package/dist/language/shell-source-code.js.map +0 -1
- package/dist/language/source-coordinates.d.ts.map +0 -1
- package/dist/language/source-coordinates.js.map +0 -1
- package/dist/language/syntax-view.d.ts.map +0 -1
- package/dist/language/syntax-view.js.map +0 -1
- package/dist/language/word-resolution.d.ts.map +0 -1
- package/dist/language/word-resolution.js.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
|
@@ -13,11 +13,22 @@ export interface ISourceCoordinate {
|
|
|
13
13
|
/** Source-bound conversion shared by tree projection and diagnostic adaptation. @internal */
|
|
14
14
|
export declare class SourceCoordinates {
|
|
15
15
|
#private;
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Build one reusable coordinate index for the exact source passed to the parser.
|
|
18
|
+
* UTF-8 widths are computed from UTF-16 code units rather than by encoding.
|
|
19
|
+
* @internal
|
|
20
|
+
*/
|
|
17
21
|
constructor(source: string);
|
|
18
22
|
/** Reject mismatched/out-of-source/interior-byte positions rather than inventing a fix boundary. @internal */
|
|
19
23
|
fromByteOffset(offset: number): ISourceCoordinate;
|
|
20
24
|
/** Derive a one-based location from a known JavaScript source index. @internal */
|
|
21
25
|
atIndex(index: number): ISourceCoordinate;
|
|
26
|
+
/**
|
|
27
|
+
* Derive the JavaScript source index of a one-based `line`/`column`. The
|
|
28
|
+
* column may name any position on the line up to and including its line
|
|
29
|
+
* break (or the end of the source on the last line), the same range a
|
|
30
|
+
* location from {@link SourceCoordinates.atIndex} can take.
|
|
31
|
+
* @internal
|
|
32
|
+
*/
|
|
33
|
+
indexAt(line: number, column: number): number;
|
|
22
34
|
}
|
|
23
|
-
//# sourceMappingURL=source-coordinates.d.ts.map
|
|
@@ -4,34 +4,71 @@
|
|
|
4
4
|
* mutating the raw syntax position. Display widths, graphemes, and tab expansion
|
|
5
5
|
* are deliberately outside this contract.
|
|
6
6
|
*/
|
|
7
|
+
/**
|
|
8
|
+
* Sentinel in the byte table for an offset that falls inside a multi-byte
|
|
9
|
+
* UTF-8 sequence: such an offset has no JavaScript string counterpart.
|
|
10
|
+
*/
|
|
11
|
+
const INTERIOR_BYTE = -1;
|
|
7
12
|
/** Source-bound conversion shared by tree projection and diagnostic adaptation. @internal */
|
|
8
13
|
export class SourceCoordinates {
|
|
9
|
-
/**
|
|
10
|
-
|
|
14
|
+
/**
|
|
15
|
+
* UTF-16 index for every UTF-8 byte offset from 0 through the encoded
|
|
16
|
+
* length; {@link INTERIOR_BYTE} marks offsets that are not character
|
|
17
|
+
* boundaries. Byte offsets are the encoding `TextEncoder` produces for the
|
|
18
|
+
* source, so a lone surrogate occupies the three bytes of U+FFFD.
|
|
19
|
+
*/
|
|
20
|
+
#byteIndices;
|
|
11
21
|
/** UTF-16 start indices of lines; CRLF becomes one line break at its LF. */
|
|
12
22
|
#lineStarts = [0];
|
|
13
23
|
/** The source extent constrains all derived ranges, including empty-file fallback. */
|
|
14
24
|
#length;
|
|
15
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Build one reusable coordinate index for the exact source passed to the parser.
|
|
27
|
+
* UTF-8 widths are computed from UTF-16 code units rather than by encoding.
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
16
30
|
constructor(source) {
|
|
17
31
|
this.#length = source.length;
|
|
32
|
+
// No UTF-16 code unit needs more than three UTF-8 bytes (a surrogate pair
|
|
33
|
+
// is two units and four bytes), so this bounds the encoded length.
|
|
34
|
+
const byteIndices = new Int32Array(source.length * 3 + 1).fill(INTERIOR_BYTE);
|
|
35
|
+
byteIndices[0] = 0;
|
|
18
36
|
let bytes = 0;
|
|
19
37
|
let index = 0;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
38
|
+
while (index < source.length) {
|
|
39
|
+
const unit = source.charCodeAt(index);
|
|
40
|
+
let width = 3;
|
|
41
|
+
let units = 1;
|
|
42
|
+
if (unit < 0x80) {
|
|
43
|
+
width = 1;
|
|
44
|
+
}
|
|
45
|
+
else if (unit < 0x800) {
|
|
46
|
+
width = 2;
|
|
47
|
+
}
|
|
48
|
+
else if (unit >= 0xd800 && unit <= 0xdbff && index + 1 < source.length) {
|
|
49
|
+
const next = source.charCodeAt(index + 1);
|
|
50
|
+
if (next >= 0xdc00 && next <= 0xdfff) {
|
|
51
|
+
// A surrogate pair is one supplementary character: four bytes.
|
|
52
|
+
width = 4;
|
|
53
|
+
units = 2;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Every other unit, including a lone surrogate (encoded as U+FFFD), is three bytes.
|
|
57
|
+
bytes += width;
|
|
58
|
+
index += units;
|
|
59
|
+
byteIndices[bytes] = index;
|
|
60
|
+
if (unit === 0x0a) {
|
|
27
61
|
this.#lineStarts.push(index);
|
|
28
62
|
}
|
|
29
63
|
}
|
|
64
|
+
this.#byteIndices = byteIndices.subarray(0, bytes + 1);
|
|
30
65
|
}
|
|
31
66
|
/** Reject mismatched/out-of-source/interior-byte positions rather than inventing a fix boundary. @internal */
|
|
32
67
|
fromByteOffset(offset) {
|
|
33
|
-
const index = this.#byteIndices.
|
|
34
|
-
|
|
68
|
+
const index = Number.isInteger(offset) && offset >= 0 && offset < this.#byteIndices.length
|
|
69
|
+
? this.#byteIndices[offset]
|
|
70
|
+
: INTERIOR_BYTE;
|
|
71
|
+
if (index === INTERIOR_BYTE) {
|
|
35
72
|
throw new RangeError(`byte offset ${String(offset)} is not a boundary in this source`);
|
|
36
73
|
}
|
|
37
74
|
return this.atIndex(index);
|
|
@@ -44,7 +81,7 @@ export class SourceCoordinates {
|
|
|
44
81
|
let low = 0;
|
|
45
82
|
let high = this.#lineStarts.length;
|
|
46
83
|
while (low + 1 < high) {
|
|
47
|
-
const middle =
|
|
84
|
+
const middle = (low + high) >>> 1;
|
|
48
85
|
if (this.#lineStarts[middle] <= index) {
|
|
49
86
|
low = middle;
|
|
50
87
|
}
|
|
@@ -54,5 +91,22 @@ export class SourceCoordinates {
|
|
|
54
91
|
}
|
|
55
92
|
return { index, line: low + 1, column: index - this.#lineStarts[low] + 1 };
|
|
56
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Derive the JavaScript source index of a one-based `line`/`column`. The
|
|
96
|
+
* column may name any position on the line up to and including its line
|
|
97
|
+
* break (or the end of the source on the last line), the same range a
|
|
98
|
+
* location from {@link SourceCoordinates.atIndex} can take.
|
|
99
|
+
* @internal
|
|
100
|
+
*/
|
|
101
|
+
indexAt(line, column) {
|
|
102
|
+
if (!Number.isInteger(line) || line < 1 || line > this.#lineStarts.length) {
|
|
103
|
+
throw new RangeError(`line ${String(line)} is outside this source (lines 1-${String(this.#lineStarts.length)})`);
|
|
104
|
+
}
|
|
105
|
+
const start = this.#lineStarts[line - 1];
|
|
106
|
+
const last = line === this.#lineStarts.length ? this.#length : this.#lineStarts[line] - 1;
|
|
107
|
+
if (!Number.isInteger(column) || column < 1 || start + column - 1 > last) {
|
|
108
|
+
throw new RangeError(`column ${String(column)} is outside line ${String(line)} (columns 1-${String(last - start + 1)})`);
|
|
109
|
+
}
|
|
110
|
+
return start + column - 1;
|
|
111
|
+
}
|
|
57
112
|
}
|
|
58
|
-
//# sourceMappingURL=source-coordinates.js.map
|
|
@@ -1,52 +1,66 @@
|
|
|
1
1
|
import type { File, Node as SyntaxNode, PositionSnapshot } from 'sh-ast';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
*
|
|
2
|
+
import type { WordResolution } from 'sh-ast/analyze';
|
|
3
|
+
import type { ShellNodeMap, ShellNodeOf, ShellNodeType } from './lint-view-types.js';
|
|
4
|
+
import { SourceCoordinates } from './source-coordinates.js';
|
|
5
|
+
/**
|
|
6
|
+
* The lint view's node types are computed from `sh-ast`'s generated interfaces
|
|
7
|
+
* (see `lint-view-types.ts`); this module builds the values and owns the
|
|
8
|
+
* runtime projection those types describe.
|
|
9
|
+
*/
|
|
10
|
+
export type { ShellNodeField, ShellNodeMap, ShellNodeOf, ShellNodeType, ShellPosition, } from './lint-view-types.js';
|
|
11
|
+
/**
|
|
12
|
+
* A node in the consumer's lint view: the union of every node type, so
|
|
13
|
+
* narrowing on `type` gives that node's fields with no cast. Scalar/child fields
|
|
14
|
+
* use lowercase upstream spellings; syntax fidelity belongs to the associated
|
|
15
|
+
* upstream tree. Auxiliary view nodes may have spans derived from children and
|
|
16
|
+
* are not upstream Nodes.
|
|
13
17
|
* @public
|
|
14
18
|
*/
|
|
15
|
-
export
|
|
16
|
-
/** Original syntax kind retained as the discriminator for lint selectors. */
|
|
17
|
-
readonly type: string;
|
|
18
|
-
/** Half-open UTF-16 span used to slice source and place fixes. */
|
|
19
|
-
readonly range: readonly [number, number];
|
|
20
|
-
/** Source-derived endpoints for ESLint diagnostics. */
|
|
21
|
-
readonly loc: {
|
|
22
|
-
readonly start: ShellPosition;
|
|
23
|
-
readonly end: ShellPosition;
|
|
24
|
-
};
|
|
25
|
-
/** Lowercase syntax fields containing consumer children or decoded scalar values. */
|
|
26
|
-
readonly [field: string]: unknown;
|
|
27
|
-
}
|
|
19
|
+
export type ShellNode = ShellNodeMap[ShellNodeType];
|
|
28
20
|
/** The lint root has a statement collection even when upstream represents it as nil. @public */
|
|
29
|
-
export
|
|
30
|
-
/** Root discriminator retained for selectors. */
|
|
31
|
-
readonly type: 'File';
|
|
32
|
-
/** Top-level statements in source order; nil upstream slices become an empty view. */
|
|
33
|
-
readonly stmts: readonly ShellNode[];
|
|
34
|
-
}
|
|
21
|
+
export type ShellFile = ShellNodeOf<'File'>;
|
|
35
22
|
/** Directive processing uses the comment text without its leading shell hash. @public */
|
|
36
|
-
export
|
|
37
|
-
/** Comment discriminator used by directive collection. */
|
|
38
|
-
readonly type: 'Comment';
|
|
39
|
-
/** Decoded comment content after the leading hash, preserving its whitespace. */
|
|
40
|
-
readonly text: string;
|
|
41
|
-
}
|
|
23
|
+
export type ShellComment = ShellNodeOf<'Comment'>;
|
|
42
24
|
/** ESLint child relationships follow the consumer projection, including auxiliary views; not upstream Walk ordering. @internal */
|
|
43
25
|
export declare const shellVisitorKeys: Readonly<Record<string, readonly string[]>>;
|
|
44
|
-
/** Identifies projected children without confusing raw position records or byte strings with nodes. @internal */
|
|
45
|
-
export declare function isShellNode(value: unknown): value is ShellNode;
|
|
46
26
|
/** Directive processing requires both the comment kind and its decoded text. @internal */
|
|
47
27
|
export declare function isShellComment(node: ShellNode): node is ShellComment;
|
|
28
|
+
/**
|
|
29
|
+
* Returns every comment view projected under `root`, in projection order, or
|
|
30
|
+
* undefined when `root` was not built by {@link adaptSyntaxFile}. Callers that
|
|
31
|
+
* need source order sort the result themselves. Saves a full-tree walk whose
|
|
32
|
+
* only purpose would be to rediscover comments the projection already visited.
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
export declare function projectedComments(root: ShellNode): readonly ShellComment[] | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Returns the coordinate index `root` was projected with: the conversion for
|
|
38
|
+
* the exact source `root`'s original parse came from. Undefined when `root`
|
|
39
|
+
* was not built by {@link adaptSyntaxFile}, which means there is no original
|
|
40
|
+
* parse whose positions could be converted.
|
|
41
|
+
* @internal
|
|
42
|
+
*/
|
|
43
|
+
export declare function projectedCoordinates(root: ShellNode): SourceCoordinates | undefined;
|
|
48
44
|
/** Returns the actual parser node, or undefined for consumer-created/synthetic views. @internal */
|
|
49
45
|
export declare function syntaxForNode(node: ShellNode): SyntaxNode | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Returns the view node projected from `syntax` within the lint view rooted at
|
|
48
|
+
* `root`, or undefined when `syntax` is not a real parser node of that view.
|
|
49
|
+
* This is the syntax → view direction of {@link syntaxForNode}'s association,
|
|
50
|
+
* recorded during projection rather than recovered by walking the view.
|
|
51
|
+
* @internal
|
|
52
|
+
*/
|
|
53
|
+
export declare function viewForSyntax(root: ShellFile, syntax: SyntaxNode): ShellNode | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* `resolution` as its LF equivalent: when `word` is a parser `Word` whose view
|
|
56
|
+
* was ended before a CRLF `\r` (see {@link endsBeforeCrlfNewline}), a static
|
|
57
|
+
* text loses that one trailing `\r`, so `ls\r\n` names the command `ls`, the
|
|
58
|
+
* same as on LF input. Any other resolution is returned unchanged; a word
|
|
59
|
+
* whose own text ends in `\r` (`$'\r'`) is not followed directly by a
|
|
60
|
+
* terminator `\r` and keeps it.
|
|
61
|
+
* @internal
|
|
62
|
+
*/
|
|
63
|
+
export declare function withoutCrlfCarriageReturn(word: SyntaxNode | null, resolution: WordResolution): WordResolution;
|
|
50
64
|
/**
|
|
51
65
|
* Builds a source-bound view immediately from one parse's File and Positions.
|
|
52
66
|
* Snapshot bounds describe that original parse, never subsequent syntax edits.
|
|
@@ -55,6 +69,13 @@ export declare function syntaxForNode(node: ShellNode): SyntaxNode | undefined;
|
|
|
55
69
|
* @internal
|
|
56
70
|
*/
|
|
57
71
|
export declare function adaptSyntaxFile(file: File, source: string, positions: PositionSnapshot): ShellFile;
|
|
72
|
+
/**
|
|
73
|
+
* The projected children of `node` in visitor-key order, whichever of its
|
|
74
|
+
* fields hold them (a single child or a slice). Traversal is keyed by the
|
|
75
|
+
* schema's field names rather than by the typed fields, so this is the one
|
|
76
|
+
* place that reads a view through its dynamic keys; rules read typed fields.
|
|
77
|
+
* @internal
|
|
78
|
+
*/
|
|
79
|
+
export declare function childNodes(node: ShellNode): ShellNode[];
|
|
58
80
|
/** Consumer traversal visits each projected child, including auxiliary views, without claiming upstream Walk control semantics. @internal */
|
|
59
81
|
export declare function walkShell(root: ShellNode, visit: (node: ShellNode) => void): void;
|
|
60
|
-
//# sourceMappingURL=syntax-view.d.ts.map
|
|
@@ -9,10 +9,50 @@ import { SYNTAX_INTERFACES, SYNTAX_SCHEMA } from 'sh-ast';
|
|
|
9
9
|
import { SourceCoordinates } from './source-coordinates.js';
|
|
10
10
|
/** Raw syntax is associated without adding parent cycles or consumer fields to its records. */
|
|
11
11
|
const originalSyntax = new WeakMap();
|
|
12
|
+
/**
|
|
13
|
+
* The inverse association, recorded per projected root: within one lint view,
|
|
14
|
+
* each real parser node maps to the one view node projected from it. Keyed by
|
|
15
|
+
* root so two views projected from the same syntax never answer for each other.
|
|
16
|
+
*/
|
|
17
|
+
const projectedViews = new WeakMap();
|
|
12
18
|
/** The upstream Node union distinguishes real nodes from auxiliary syntax records. */
|
|
13
19
|
const nodeTypes = new Set(SYNTAX_INTERFACES.Node);
|
|
14
20
|
/** Parser input is a JavaScript string; unexpected invalid-byte fields must never be silently replaced. */
|
|
15
21
|
const textDecoder = new TextDecoder('utf-8', { fatal: true });
|
|
22
|
+
/**
|
|
23
|
+
* Projection plan per upstream syntax type, derived once from the schema so
|
|
24
|
+
* projecting a record never re-enumerates or re-lowercases schema fields. Only
|
|
25
|
+
* types present here are projectable; anything else is an unknown record.
|
|
26
|
+
*/
|
|
27
|
+
const projectionPlans = new Map(Object.entries(SYNTAX_SCHEMA).map(([type, schema]) => [
|
|
28
|
+
type,
|
|
29
|
+
Object.entries(schema.fields)
|
|
30
|
+
.filter(([, field]) => field.kind !== 'position')
|
|
31
|
+
.map(([name, field]) => ({
|
|
32
|
+
name,
|
|
33
|
+
key: name.toLowerCase(),
|
|
34
|
+
kind: field.kind === 'struct' || field.kind === 'interface'
|
|
35
|
+
? 'child'
|
|
36
|
+
: field.kind === 'string'
|
|
37
|
+
? 'text'
|
|
38
|
+
: 'value',
|
|
39
|
+
slice: field.slice,
|
|
40
|
+
})),
|
|
41
|
+
]));
|
|
42
|
+
/**
|
|
43
|
+
* Comment views observed while projecting each root, in projection order (not
|
|
44
|
+
* source order). Keyed by root identity so a view carries no extra fields;
|
|
45
|
+
* roots this module did not build have no entry.
|
|
46
|
+
*/
|
|
47
|
+
const projectedCommentsByRoot = new WeakMap();
|
|
48
|
+
/**
|
|
49
|
+
* The coordinate index each root was projected with, keyed by root identity.
|
|
50
|
+
* Upstream byte positions are only meaningful against the exact source they
|
|
51
|
+
* were parsed from, so the one index projection built for that source is the
|
|
52
|
+
* one every later byte → UTF-16 conversion for the file reuses. Roots this
|
|
53
|
+
* module did not build have no entry.
|
|
54
|
+
*/
|
|
55
|
+
const projectedCoordinatesByRoot = new WeakMap();
|
|
16
56
|
/** ESLint child relationships follow the consumer projection, including auxiliary views; not upstream Walk ordering. @internal */
|
|
17
57
|
export const shellVisitorKeys = Object.freeze(Object.fromEntries(Object.entries(SYNTAX_SCHEMA).map(([name, schema]) => [
|
|
18
58
|
name,
|
|
@@ -28,8 +68,8 @@ function isRecord(value) {
|
|
|
28
68
|
function isSyntaxNode(value) {
|
|
29
69
|
return isRecord(value) && typeof value.Type === 'string' && nodeTypes.has(value.Type);
|
|
30
70
|
}
|
|
31
|
-
/** Identifies projected children without confusing raw position records or byte strings with nodes.
|
|
32
|
-
|
|
71
|
+
/** Identifies projected children without confusing raw position records or byte strings with nodes. */
|
|
72
|
+
function isShellNode(value) {
|
|
33
73
|
return isRecord(value) && typeof value.type === 'string' && Array.isArray(value.range);
|
|
34
74
|
}
|
|
35
75
|
/** Directive processing requires both the comment kind and its decoded text. @internal */
|
|
@@ -40,10 +80,40 @@ export function isShellComment(node) {
|
|
|
40
80
|
function isShellFile(node) {
|
|
41
81
|
return node.type === 'File' && Array.isArray(node.stmts) && node.stmts.every(isShellNode);
|
|
42
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Returns every comment view projected under `root`, in projection order, or
|
|
85
|
+
* undefined when `root` was not built by {@link adaptSyntaxFile}. Callers that
|
|
86
|
+
* need source order sort the result themselves. Saves a full-tree walk whose
|
|
87
|
+
* only purpose would be to rediscover comments the projection already visited.
|
|
88
|
+
* @internal
|
|
89
|
+
*/
|
|
90
|
+
export function projectedComments(root) {
|
|
91
|
+
return projectedCommentsByRoot.get(root);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Returns the coordinate index `root` was projected with: the conversion for
|
|
95
|
+
* the exact source `root`'s original parse came from. Undefined when `root`
|
|
96
|
+
* was not built by {@link adaptSyntaxFile}, which means there is no original
|
|
97
|
+
* parse whose positions could be converted.
|
|
98
|
+
* @internal
|
|
99
|
+
*/
|
|
100
|
+
export function projectedCoordinates(root) {
|
|
101
|
+
return projectedCoordinatesByRoot.get(root);
|
|
102
|
+
}
|
|
43
103
|
/** Returns the actual parser node, or undefined for consumer-created/synthetic views. @internal */
|
|
44
104
|
export function syntaxForNode(node) {
|
|
45
105
|
return originalSyntax.get(node);
|
|
46
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Returns the view node projected from `syntax` within the lint view rooted at
|
|
109
|
+
* `root`, or undefined when `syntax` is not a real parser node of that view.
|
|
110
|
+
* This is the syntax → view direction of {@link syntaxForNode}'s association,
|
|
111
|
+
* recorded during projection rather than recovered by walking the view.
|
|
112
|
+
* @internal
|
|
113
|
+
*/
|
|
114
|
+
export function viewForSyntax(root, syntax) {
|
|
115
|
+
return projectedViews.get(root)?.get(syntax);
|
|
116
|
+
}
|
|
47
117
|
/** Converts valid source-associated Go strings; invalid byte sequences have no faithful text view here. */
|
|
48
118
|
function sourceText(value) {
|
|
49
119
|
if (typeof value === 'string') {
|
|
@@ -54,6 +124,50 @@ function sourceText(value) {
|
|
|
54
124
|
}
|
|
55
125
|
throw new TypeError('shell text view expected a Go string');
|
|
56
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Parser nodes whose view span was shortened by {@link endsBeforeCrlfNewline}.
|
|
129
|
+
* Keyed by the raw syntax record, which is what word-resolution results are
|
|
130
|
+
* computed from, so a resolver can tell that a trailing `\r` in its text is
|
|
131
|
+
* the line terminator's and not the word's (see {@link withoutCrlfCarriageReturn}).
|
|
132
|
+
*/
|
|
133
|
+
const crlfTrimmedViews = new WeakSet();
|
|
134
|
+
/**
|
|
135
|
+
* Whether `span` ends just after the `\r` of a `\r\n` line terminator.
|
|
136
|
+
*
|
|
137
|
+
* The parser treats only `\n` as a line break, so on CRLF input it keeps the
|
|
138
|
+
* `\r` before it as an ordinary character of whatever is last on the line: an
|
|
139
|
+
* unbraced parameter expansion or a word-final literal includes it in both its
|
|
140
|
+
* bounds and its value (`ls $z\r\n` puts `$z` at `[3,6]`). sh-ast reports that
|
|
141
|
+
* faithfully and leaves adaptation to the consumer. The lint view is that
|
|
142
|
+
* consumer's surface, and a CRLF file must lint like its LF equivalent, so the
|
|
143
|
+
* view ends such a node before the `\r`: messages never print it, and a fix or
|
|
144
|
+
* suggestion that rewrites the node's range never moves it (`ls "$z"\r\n`,
|
|
145
|
+
* not `ls "$z\r"\n`). A node whose text continues past the line break (a
|
|
146
|
+
* quoted string or here-doc body spanning lines) never ends there, so this
|
|
147
|
+
* touches only nodes the line terminator cuts off. A span made only of the
|
|
148
|
+
* `\r` itself is left alone, so no real node becomes empty.
|
|
149
|
+
*/
|
|
150
|
+
function endsBeforeCrlfNewline(source, span) {
|
|
151
|
+
const end = span.end.index;
|
|
152
|
+
return end - span.start.index > 1 && source[end - 1] === '\r' && source[end] === '\n';
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* `resolution` as its LF equivalent: when `word` is a parser `Word` whose view
|
|
156
|
+
* was ended before a CRLF `\r` (see {@link endsBeforeCrlfNewline}), a static
|
|
157
|
+
* text loses that one trailing `\r`, so `ls\r\n` names the command `ls`, the
|
|
158
|
+
* same as on LF input. Any other resolution is returned unchanged; a word
|
|
159
|
+
* whose own text ends in `\r` (`$'\r'`) is not followed directly by a
|
|
160
|
+
* terminator `\r` and keeps it.
|
|
161
|
+
* @internal
|
|
162
|
+
*/
|
|
163
|
+
export function withoutCrlfCarriageReturn(word, resolution) {
|
|
164
|
+
if (word === null || !crlfTrimmedViews.has(word) || !resolution.static) {
|
|
165
|
+
return resolution;
|
|
166
|
+
}
|
|
167
|
+
return resolution.text.endsWith('\r')
|
|
168
|
+
? { static: true, text: resolution.text.slice(0, -1) }
|
|
169
|
+
: resolution;
|
|
170
|
+
}
|
|
57
171
|
/**
|
|
58
172
|
* Builds a source-bound view immediately from one parse's File and Positions.
|
|
59
173
|
* Snapshot bounds describe that original parse, never subsequent syntax edits.
|
|
@@ -66,18 +180,26 @@ export function adaptSyntaxFile(file, source, positions) {
|
|
|
66
180
|
throw new TypeError('shell view requires a source position snapshot');
|
|
67
181
|
}
|
|
68
182
|
const coordinates = new SourceCoordinates(source);
|
|
183
|
+
const views = new WeakMap();
|
|
69
184
|
const wholeSource = { start: coordinates.atIndex(0), end: coordinates.atIndex(source.length) };
|
|
70
185
|
/** Invalid/recovered positions have no exact text boundary; the enclosing view supplies an explicit consumer fallback. */
|
|
71
186
|
const position = (raw, fallback) => raw.IsValid && !raw.IsRecovered ? coordinates.fromByteOffset(raw.Offset) : fallback;
|
|
187
|
+
/** Comment views in projection order; registered against the root once projection succeeds. */
|
|
188
|
+
const comments = [];
|
|
72
189
|
/** Each projected record owns its derived fields, while real syntax nodes retain their original identity. */
|
|
73
190
|
const project = (raw, fallback) => {
|
|
74
|
-
if (!isRecord(raw) || typeof raw.Type !== 'string'
|
|
191
|
+
if (!isRecord(raw) || typeof raw.Type !== 'string') {
|
|
192
|
+
throw new TypeError('shell view expected a known upstream syntax record');
|
|
193
|
+
}
|
|
194
|
+
const plan = projectionPlans.get(raw.Type);
|
|
195
|
+
if (plan === undefined) {
|
|
75
196
|
throw new TypeError('shell view expected a known upstream syntax record');
|
|
76
197
|
}
|
|
77
|
-
const schema = SYNTAX_SCHEMA[raw.Type];
|
|
78
198
|
const realNode = isSyntaxNode(raw);
|
|
79
199
|
// eslint-disable-next-line @api-extractor-tools/forgotten-export -- This span belongs only to the private projection closure, not the exported signature.
|
|
80
200
|
let span = fallback;
|
|
201
|
+
// Whether this node's span lost a CRLF carriage return (see `endsBeforeCrlfNewline`).
|
|
202
|
+
let trimmedCarriageReturn = false;
|
|
81
203
|
if (realNode) {
|
|
82
204
|
const observed = positions.get(raw);
|
|
83
205
|
if (observed === undefined) {
|
|
@@ -87,78 +209,111 @@ export function adaptSyntaxFile(file, source, positions) {
|
|
|
87
209
|
start: position(observed.Pos, fallback.start),
|
|
88
210
|
end: position(observed.End, fallback.end),
|
|
89
211
|
};
|
|
212
|
+
if (endsBeforeCrlfNewline(source, span)) {
|
|
213
|
+
span = { start: span.start, end: coordinates.atIndex(span.end.index - 1) };
|
|
214
|
+
trimmedCarriageReturn = true;
|
|
215
|
+
crlfTrimmedViews.add(raw);
|
|
216
|
+
}
|
|
90
217
|
}
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
218
|
+
// Schema fields first, in schema order, then `type`, `range` and `loc`.
|
|
219
|
+
const view = {};
|
|
220
|
+
// Auxiliary records take their span from the extent of their projected children.
|
|
221
|
+
let childStart = Number.POSITIVE_INFINITY;
|
|
222
|
+
let childEnd = Number.NEGATIVE_INFINITY;
|
|
223
|
+
const include = (child) => {
|
|
224
|
+
if (child.range[0] < childStart) {
|
|
225
|
+
childStart = child.range[0];
|
|
96
226
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
227
|
+
if (child.range[1] > childEnd) {
|
|
228
|
+
childEnd = child.range[1];
|
|
229
|
+
}
|
|
230
|
+
return child;
|
|
231
|
+
};
|
|
232
|
+
for (const field of plan) {
|
|
233
|
+
const value = raw[field.name];
|
|
234
|
+
if (field.kind === 'child') {
|
|
100
235
|
if (field.slice) {
|
|
101
236
|
const items = value === null ? [] : value;
|
|
102
237
|
if (!Array.isArray(items)) {
|
|
103
|
-
throw new TypeError(`shell view expected ${raw.Type}.${name} to be a slice`);
|
|
238
|
+
throw new TypeError(`shell view expected ${raw.Type}.${field.name} to be a slice`);
|
|
239
|
+
}
|
|
240
|
+
const projected = [];
|
|
241
|
+
for (const item of items) {
|
|
242
|
+
if (item !== null) {
|
|
243
|
+
projected.push(include(project(item, span)));
|
|
244
|
+
}
|
|
104
245
|
}
|
|
105
|
-
|
|
106
|
-
.filter((item) => item !== null)
|
|
107
|
-
.map((item) => project(item, span));
|
|
108
|
-
fields[key] = projected;
|
|
109
|
-
children.push(...projected);
|
|
246
|
+
view[field.key] = projected;
|
|
110
247
|
}
|
|
111
248
|
else if (value !== null) {
|
|
112
|
-
|
|
113
|
-
fields[key] = child;
|
|
114
|
-
children.push(child);
|
|
249
|
+
view[field.key] = include(project(value, span));
|
|
115
250
|
}
|
|
116
251
|
}
|
|
117
252
|
else {
|
|
118
|
-
|
|
253
|
+
const text = field.kind === 'text' ? sourceText(value) : value;
|
|
254
|
+
view[field.key] =
|
|
255
|
+
trimmedCarriageReturn && typeof text === 'string' && text.endsWith('\r')
|
|
256
|
+
? text.slice(0, -1)
|
|
257
|
+
: text;
|
|
119
258
|
}
|
|
120
259
|
}
|
|
121
|
-
if (!realNode &&
|
|
122
|
-
span = {
|
|
123
|
-
start: coordinates.atIndex(Math.min(...children.map((node) => node.range[0]))),
|
|
124
|
-
end: coordinates.atIndex(Math.max(...children.map((node) => node.range[1]))),
|
|
125
|
-
};
|
|
260
|
+
if (!realNode && childStart !== Number.POSITIVE_INFINITY) {
|
|
261
|
+
span = { start: coordinates.atIndex(childStart), end: coordinates.atIndex(childEnd) };
|
|
126
262
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
start: { line: span.start.line, column: span.start.column },
|
|
133
|
-
end: { line: span.end.line, column: span.end.column },
|
|
134
|
-
},
|
|
263
|
+
view.type = raw.Type;
|
|
264
|
+
view.range = [span.start.index, span.end.index];
|
|
265
|
+
view.loc = {
|
|
266
|
+
start: { line: span.start.line, column: span.start.column },
|
|
267
|
+
end: { line: span.end.line, column: span.end.column },
|
|
135
268
|
};
|
|
269
|
+
const node = view;
|
|
270
|
+
if (isShellComment(node)) {
|
|
271
|
+
comments.push(node);
|
|
272
|
+
}
|
|
136
273
|
if (realNode) {
|
|
137
|
-
originalSyntax.set(
|
|
274
|
+
originalSyntax.set(node, raw);
|
|
275
|
+
views.set(raw, node);
|
|
138
276
|
}
|
|
139
|
-
return
|
|
277
|
+
return node;
|
|
140
278
|
};
|
|
141
279
|
const root = project(file, wholeSource);
|
|
142
280
|
if (!isShellFile(root)) {
|
|
143
281
|
throw new TypeError('shell view expected a File with statements');
|
|
144
282
|
}
|
|
283
|
+
projectedViews.set(root, views);
|
|
284
|
+
projectedCommentsByRoot.set(root, comments);
|
|
285
|
+
projectedCoordinatesByRoot.set(root, coordinates);
|
|
145
286
|
return root;
|
|
146
287
|
}
|
|
147
|
-
/**
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
288
|
+
/**
|
|
289
|
+
* The projected children of `node` in visitor-key order, whichever of its
|
|
290
|
+
* fields hold them (a single child or a slice). Traversal is keyed by the
|
|
291
|
+
* schema's field names rather than by the typed fields, so this is the one
|
|
292
|
+
* place that reads a view through its dynamic keys; rules read typed fields.
|
|
293
|
+
* @internal
|
|
294
|
+
*/
|
|
295
|
+
export function childNodes(node) {
|
|
296
|
+
const fields = node;
|
|
297
|
+
const children = [];
|
|
298
|
+
for (const key of shellVisitorKeys[node.type] ?? []) {
|
|
299
|
+
const value = fields[key];
|
|
152
300
|
if (Array.isArray(value)) {
|
|
153
301
|
for (const child of value) {
|
|
154
302
|
if (isShellNode(child)) {
|
|
155
|
-
|
|
303
|
+
children.push(child);
|
|
156
304
|
}
|
|
157
305
|
}
|
|
158
306
|
}
|
|
159
307
|
else if (isShellNode(value)) {
|
|
160
|
-
|
|
308
|
+
children.push(value);
|
|
161
309
|
}
|
|
162
310
|
}
|
|
311
|
+
return children;
|
|
312
|
+
}
|
|
313
|
+
/** Consumer traversal visits each projected child, including auxiliary views, without claiming upstream Walk control semantics. @internal */
|
|
314
|
+
export function walkShell(root, visit) {
|
|
315
|
+
visit(root);
|
|
316
|
+
for (const child of childNodes(root)) {
|
|
317
|
+
walkShell(child, visit);
|
|
318
|
+
}
|
|
163
319
|
}
|
|
164
|
-
//# sourceMappingURL=syntax-view.js.map
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { ResolveWordOptions, WordResolution } from 'sh-ast/analyze';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ShellNodeOf } from './syntax-view.js';
|
|
3
3
|
/**
|
|
4
4
|
* Resolves a parsed lint Word using the upstream-shaped syntax associated with it.
|
|
5
5
|
* The view supplies locations and selector fields; sh-ast/analyze owns the word
|
|
6
6
|
* interpretation. Fabricated or synthetic view nodes have no source syntax and
|
|
7
7
|
* are rejected instead of being treated as known or unknown shell values.
|
|
8
|
+
* A static word that ends a line of a CRLF file resolves without the line
|
|
9
|
+
* terminator's carriage return, as it would on LF input.
|
|
8
10
|
* @public
|
|
9
11
|
*/
|
|
10
|
-
export declare function resolveWord(word:
|
|
11
|
-
//# sourceMappingURL=word-resolution.d.ts.map
|
|
12
|
+
export declare function resolveWord(word: ShellNodeOf<'Word'>, options?: ResolveWordOptions): WordResolution;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/** Bridges custom-rule analysis from ESLint view identity back to faithful syntax. */
|
|
2
2
|
import { resolveWord as resolveSyntaxWord } from 'sh-ast/analyze';
|
|
3
|
-
import { syntaxForNode } from './syntax-view.js';
|
|
3
|
+
import { syntaxForNode, withoutCrlfCarriageReturn } from './syntax-view.js';
|
|
4
4
|
/**
|
|
5
5
|
* Resolves a parsed lint Word using the upstream-shaped syntax associated with it.
|
|
6
6
|
* The view supplies locations and selector fields; sh-ast/analyze owns the word
|
|
7
7
|
* interpretation. Fabricated or synthetic view nodes have no source syntax and
|
|
8
8
|
* are rejected instead of being treated as known or unknown shell values.
|
|
9
|
+
* A static word that ends a line of a CRLF file resolves without the line
|
|
10
|
+
* terminator's carriage return, as it would on LF input.
|
|
9
11
|
* @public
|
|
10
12
|
*/
|
|
11
13
|
export function resolveWord(word, options) {
|
|
@@ -13,6 +15,5 @@ export function resolveWord(word, options) {
|
|
|
13
15
|
if (syntax?.Type !== 'Word') {
|
|
14
16
|
throw new TypeError('resolveWord expects a parsed shell Word from this language adapter');
|
|
15
17
|
}
|
|
16
|
-
return resolveSyntaxWord(syntax, options);
|
|
18
|
+
return withoutCrlfCarriageReturn(syntax, resolveSyntaxWord(syntax, options));
|
|
17
19
|
}
|
|
18
|
-
//# sourceMappingURL=word-resolution.js.map
|