@eslint-sh/eslint-plugin 0.5.0 → 0.6.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.
Files changed (256) hide show
  1. package/README.md +223 -17
  2. package/dist/command-harvest/builtins/bash.d.ts +36 -0
  3. package/dist/command-harvest/builtins/bash.js +98 -0
  4. package/dist/command-harvest/builtins/bats.d.ts +45 -0
  5. package/dist/command-harvest/builtins/bats.js +33 -0
  6. package/dist/command-harvest/builtins/index.d.ts +18 -0
  7. package/dist/command-harvest/builtins/index.js +39 -0
  8. package/dist/command-harvest/builtins/posix.d.ts +51 -0
  9. package/dist/command-harvest/builtins/posix.js +93 -0
  10. package/dist/command-harvest/harvest-commands.d.ts +66 -0
  11. package/dist/command-harvest/harvest-commands.js +97 -0
  12. package/dist/command-harvest/wrapper-table.d.ts +24 -0
  13. package/dist/command-harvest/wrapper-table.js +49 -0
  14. package/dist/configs/agent-authored.d.ts +41 -0
  15. package/dist/configs/agent-authored.js +33 -0
  16. package/dist/configs/bash-3-2.d.ts +28 -0
  17. package/dist/configs/bash-3-2.js +50 -0
  18. package/dist/configs/posix-portable.d.ts +40 -0
  19. package/dist/configs/posix-portable.js +52 -0
  20. package/dist/configs/recommended.d.ts +52 -0
  21. package/dist/configs/recommended.js +34 -0
  22. package/dist/declaration-conventions/comment-pragma.d.ts +33 -0
  23. package/dist/declaration-conventions/comment-pragma.js +153 -0
  24. package/dist/declaration-conventions/declaration-convention.d.ts +189 -0
  25. package/dist/declaration-conventions/declaration-convention.js +1 -0
  26. package/dist/declaration-conventions/read-declared-commands.d.ts +70 -0
  27. package/dist/declaration-conventions/read-declared-commands.js +128 -0
  28. package/dist/eslint-plugin-syntax-analyze.d.ts +23 -0
  29. package/dist/eslint-plugin-syntax.d.ts +27 -0
  30. package/dist/eslint-plugin.d.ts +1211 -52
  31. package/dist/index.d.ts +56 -8
  32. package/dist/index.js +53 -6
  33. package/dist/language/command-resolution.d.ts +124 -0
  34. package/dist/language/command-resolution.js +120 -0
  35. package/dist/language/end-of-input-marker.d.ts +29 -0
  36. package/dist/language/end-of-input-marker.js +88 -0
  37. package/dist/language/line-breaks.d.ts +18 -0
  38. package/dist/language/line-breaks.js +22 -0
  39. package/dist/language/lint-view-types.d.ts +104 -0
  40. package/dist/language/lint-view-types.js +1 -0
  41. package/dist/language/non-shell-file-guard.d.ts +35 -0
  42. package/dist/language/non-shell-file-guard.js +59 -0
  43. package/dist/language/selector-classes.d.ts +0 -1
  44. package/dist/language/selector-classes.js +0 -1
  45. package/dist/language/shell-language-options.d.ts +7 -1
  46. package/dist/language/shell-language-options.js +4 -4
  47. package/dist/language/shell-language.d.ts +10 -2
  48. package/dist/language/shell-language.js +59 -8
  49. package/dist/language/shell-source-code.d.ts +342 -5
  50. package/dist/language/shell-source-code.js +559 -30
  51. package/dist/language/source-coordinates.d.ts +13 -2
  52. package/dist/language/source-coordinates.js +68 -14
  53. package/dist/language/syntax-view.d.ts +60 -39
  54. package/dist/language/syntax-view.js +200 -45
  55. package/dist/language/word-resolution.d.ts +4 -3
  56. package/dist/language/word-resolution.js +4 -3
  57. package/dist/rules/compat/create-compat-rule.d.ts +59 -0
  58. package/dist/rules/compat/create-compat-rule.js +65 -0
  59. package/dist/rules/compat/declaration-options.d.ts +19 -0
  60. package/dist/rules/compat/declaration-options.js +110 -0
  61. package/dist/rules/compat/no-append-all-redirect.d.ts +13 -0
  62. package/dist/rules/compat/no-append-all-redirect.js +73 -0
  63. package/dist/rules/compat/no-associative-arrays.d.ts +12 -0
  64. package/dist/rules/compat/no-associative-arrays.js +39 -0
  65. package/dist/rules/compat/no-case-attributes.d.ts +12 -0
  66. package/dist/rules/compat/no-case-attributes.js +63 -0
  67. package/dist/rules/compat/no-case-fallthrough.d.ts +12 -0
  68. package/dist/rules/compat/no-case-fallthrough.js +67 -0
  69. package/dist/rules/compat/no-case-modification.d.ts +35 -0
  70. package/dist/rules/compat/no-case-modification.js +86 -0
  71. package/dist/rules/compat/no-coproc.d.ts +12 -0
  72. package/dist/rules/compat/no-coproc.js +47 -0
  73. package/dist/rules/compat/no-declare-g.d.ts +12 -0
  74. package/dist/rules/compat/no-declare-g.js +59 -0
  75. package/dist/rules/compat/no-double-bracket.d.ts +12 -0
  76. package/dist/rules/compat/no-double-bracket.js +52 -0
  77. package/dist/rules/compat/no-epoch-variables.d.ts +12 -0
  78. package/dist/rules/compat/no-epoch-variables.js +57 -0
  79. package/dist/rules/compat/no-fd-variable.d.ts +12 -0
  80. package/dist/rules/compat/no-fd-variable.js +48 -0
  81. package/dist/rules/compat/no-globstar.d.ts +12 -0
  82. package/dist/rules/compat/no-globstar.js +39 -0
  83. package/dist/rules/compat/no-lastpipe-semantics.d.ts +12 -0
  84. package/dist/rules/compat/no-lastpipe-semantics.js +53 -0
  85. package/dist/rules/compat/no-let.d.ts +12 -0
  86. package/dist/rules/compat/no-let.js +62 -0
  87. package/dist/rules/compat/no-mapfile.d.ts +13 -0
  88. package/dist/rules/compat/no-mapfile.js +122 -0
  89. package/dist/rules/compat/no-nameref.d.ts +12 -0
  90. package/dist/rules/compat/no-nameref.js +44 -0
  91. package/dist/rules/compat/no-negative-array-index.d.ts +13 -0
  92. package/dist/rules/compat/no-negative-array-index.js +123 -0
  93. package/dist/rules/compat/no-nounset-empty-array.d.ts +13 -0
  94. package/dist/rules/compat/no-nounset-empty-array.js +158 -0
  95. package/dist/rules/compat/no-parameter-transformation.d.ts +13 -0
  96. package/dist/rules/compat/no-parameter-transformation.js +123 -0
  97. package/dist/rules/compat/no-pipe-all.d.ts +13 -0
  98. package/dist/rules/compat/no-pipe-all.js +68 -0
  99. package/dist/rules/compat/no-printf-time-format.d.ts +12 -0
  100. package/dist/rules/compat/no-printf-time-format.js +99 -0
  101. package/dist/rules/compat/no-source.d.ts +13 -0
  102. package/dist/rules/compat/no-source.js +103 -0
  103. package/dist/rules/compat/no-test-v.d.ts +12 -0
  104. package/dist/rules/compat/no-test-v.js +280 -0
  105. package/dist/rules/compat/no-unicode-escape.d.ts +14 -0
  106. package/dist/rules/compat/no-unicode-escape.js +120 -0
  107. package/dist/rules/compat/no-wait-n.d.ts +12 -0
  108. package/dist/rules/compat/no-wait-n.js +73 -0
  109. package/dist/rules/compat/operator-range.d.ts +10 -0
  110. package/dist/rules/compat/operator-range.js +15 -0
  111. package/dist/rules/compat/posix-absent-command.d.ts +12 -0
  112. package/dist/rules/compat/posix-absent-command.js +134 -0
  113. package/dist/rules/compat/shopt-options.d.ts +9 -0
  114. package/dist/rules/compat/shopt-options.js +63 -0
  115. package/dist/rules/compat/version-gates.d.ts +76 -0
  116. package/dist/rules/compat/version-gates.js +197 -0
  117. package/dist/rules/create-rule.d.ts +91 -0
  118. package/dist/rules/create-rule.js +51 -0
  119. package/dist/rules/create-wrapper-rule.d.ts +166 -0
  120. package/dist/rules/create-wrapper-rule.js +407 -0
  121. package/dist/rules/define-shell-rule.d.ts +184 -0
  122. package/dist/rules/define-shell-rule.js +56 -0
  123. package/dist/rules/index.d.ts +26 -0
  124. package/dist/rules/index.js +88 -0
  125. package/dist/rules/max-script-complexity.d.ts +28 -0
  126. package/dist/rules/max-script-complexity.js +130 -0
  127. package/dist/rules/no-dynamic-invocation.d.ts +35 -0
  128. package/dist/rules/no-dynamic-invocation.js +117 -0
  129. package/dist/rules/no-eval.d.ts +13 -0
  130. package/dist/rules/no-eval.js +89 -0
  131. package/dist/rules/no-masked-exit-status.d.ts +21 -0
  132. package/dist/rules/no-masked-exit-status.js +667 -0
  133. package/dist/rules/no-secret-env-reads.d.ts +24 -0
  134. package/dist/rules/no-secret-env-reads.js +133 -0
  135. package/dist/rules/no-secret-flags.d.ts +32 -0
  136. package/dist/rules/no-secret-flags.js +162 -0
  137. package/dist/rules/no-unchecked-cd.d.ts +44 -0
  138. package/dist/rules/no-unchecked-cd.js +250 -0
  139. package/dist/rules/no-undeclared-commands.d.ts +20 -0
  140. package/dist/rules/no-undeclared-commands.js +115 -0
  141. package/dist/rules/no-unused-declarations.d.ts +23 -0
  142. package/dist/rules/no-unused-declarations.js +259 -0
  143. package/dist/rules/prefer-local.d.ts +16 -0
  144. package/dist/rules/prefer-local.js +249 -0
  145. package/dist/rules/quote-expansions.d.ts +13 -0
  146. package/dist/rules/quote-expansions.js +229 -0
  147. package/dist/rules/require-network-wrapper.d.ts +40 -0
  148. package/dist/rules/require-network-wrapper.js +244 -0
  149. package/dist/rules/require-shebang.d.ts +29 -0
  150. package/dist/rules/require-shebang.js +101 -0
  151. package/dist/rules/require-strict-mode.d.ts +52 -0
  152. package/dist/rules/require-strict-mode.js +177 -0
  153. package/dist/rules/require-tty-guard.d.ts +13 -0
  154. package/dist/rules/require-tty-guard.js +289 -0
  155. package/dist/rules/resolve-declaration-convention.d.ts +96 -0
  156. package/dist/rules/resolve-declaration-convention.js +109 -0
  157. package/dist/rules/resolve-network-wrapper.d.ts +19 -0
  158. package/dist/rules/resolve-network-wrapper.js +116 -0
  159. package/dist/rules/set-options.d.ts +109 -0
  160. package/dist/rules/set-options.js +201 -0
  161. package/dist/rules/shebang-interpreter.d.ts +18 -0
  162. package/dist/rules/shebang-interpreter.js +81 -0
  163. package/dist/rules/shebang-matches-dialect.d.ts +12 -0
  164. package/dist/rules/shebang-matches-dialect.js +69 -0
  165. package/dist/rules/transparent-wrappers.d.ts +52 -0
  166. package/dist/rules/transparent-wrappers.js +99 -0
  167. package/dist/rules/user-pattern.d.ts +63 -0
  168. package/dist/rules/user-pattern.js +184 -0
  169. package/dist/syntax/analyze.d.ts +19 -0
  170. package/dist/syntax/analyze.js +21 -0
  171. package/dist/syntax/index.d.ts +23 -0
  172. package/dist/syntax/index.js +25 -0
  173. package/dist/version.d.ts +0 -1
  174. package/dist/version.js +0 -1
  175. package/docs/compat.md +347 -0
  176. package/docs/custom-rule-tutorial.md +780 -34
  177. package/docs/declaration-conventions.md +478 -0
  178. package/docs/examples/audit-wrapper-pack.mjs +15 -0
  179. package/docs/examples/dep-line-convention.mjs +22 -0
  180. package/docs/examples/eslint.config.mjs +3 -2
  181. package/docs/examples/no-and-or-as-if-rule.mjs +61 -0
  182. package/docs/examples/no-backticks-rule-tester.mjs +106 -0
  183. package/docs/examples/no-backticks-rule.mjs +60 -0
  184. package/docs/examples/no-curl-in-functions-rule.mjs +53 -0
  185. package/docs/examples/no-function-keyword-rule.mjs +59 -0
  186. package/docs/examples/no-listed-commands-rule.ts +63 -0
  187. package/docs/examples/no-rm-rf-rule.mjs +26 -15
  188. package/docs/examples/no-unquoted-rm-variable-rule.ts +54 -0
  189. package/docs/examples/sidecar-convention.mjs +38 -0
  190. package/docs/examples/sidecar-snapshot-convention.mjs +26 -0
  191. package/docs/examples/sorted-declarations-rule.mjs +52 -0
  192. package/docs/reading-nodes.md +159 -0
  193. package/docs/rules/README.md +78 -0
  194. package/docs/rules/_template.md +83 -0
  195. package/docs/rules/compat/no-append-all-redirect.md +87 -0
  196. package/docs/rules/compat/no-associative-arrays.md +95 -0
  197. package/docs/rules/compat/no-case-attributes.md +82 -0
  198. package/docs/rules/compat/no-case-fallthrough.md +90 -0
  199. package/docs/rules/compat/no-case-modification.md +74 -0
  200. package/docs/rules/compat/no-coproc.md +76 -0
  201. package/docs/rules/compat/no-declare-g.md +80 -0
  202. package/docs/rules/compat/no-double-bracket.md +72 -0
  203. package/docs/rules/compat/no-epoch-variables.md +79 -0
  204. package/docs/rules/compat/no-fd-variable.md +78 -0
  205. package/docs/rules/compat/no-globstar.md +74 -0
  206. package/docs/rules/compat/no-lastpipe-semantics.md +73 -0
  207. package/docs/rules/compat/no-let.md +78 -0
  208. package/docs/rules/compat/no-mapfile.md +118 -0
  209. package/docs/rules/compat/no-nameref.md +81 -0
  210. package/docs/rules/compat/no-negative-array-index.md +87 -0
  211. package/docs/rules/compat/no-nounset-empty-array.md +103 -0
  212. package/docs/rules/compat/no-parameter-transformation.md +94 -0
  213. package/docs/rules/compat/no-pipe-all.md +85 -0
  214. package/docs/rules/compat/no-printf-time-format.md +75 -0
  215. package/docs/rules/compat/no-source.md +98 -0
  216. package/docs/rules/compat/no-test-v.md +82 -0
  217. package/docs/rules/compat/no-unicode-escape.md +91 -0
  218. package/docs/rules/compat/no-wait-n.md +85 -0
  219. package/docs/rules/max-script-complexity.md +109 -0
  220. package/docs/rules/no-dynamic-invocation.md +139 -0
  221. package/docs/rules/no-eval.md +106 -0
  222. package/docs/rules/no-masked-exit-status.md +239 -0
  223. package/docs/rules/no-secret-env-reads.md +173 -0
  224. package/docs/rules/no-secret-flags.md +169 -0
  225. package/docs/rules/no-unchecked-cd.md +146 -0
  226. package/docs/rules/no-undeclared-commands.md +141 -0
  227. package/docs/rules/no-unused-declarations.md +216 -0
  228. package/docs/rules/prefer-local.md +221 -0
  229. package/docs/rules/quote-expansions.md +157 -0
  230. package/docs/rules/require-network-wrapper.md +171 -0
  231. package/docs/rules/require-shebang.md +157 -0
  232. package/docs/rules/require-strict-mode.md +253 -0
  233. package/docs/rules/require-tty-guard.md +128 -0
  234. package/docs/rules/shebang-matches-dialect.md +120 -0
  235. package/docs/selectors.md +2 -2
  236. package/docs/shellcheck.md +86 -0
  237. package/docs/wrapper-rules.md +206 -0
  238. package/package.json +26 -20
  239. package/dist/index.d.ts.map +0 -1
  240. package/dist/index.js.map +0 -1
  241. package/dist/language/selector-classes.d.ts.map +0 -1
  242. package/dist/language/selector-classes.js.map +0 -1
  243. package/dist/language/shell-language-options.d.ts.map +0 -1
  244. package/dist/language/shell-language-options.js.map +0 -1
  245. package/dist/language/shell-language.d.ts.map +0 -1
  246. package/dist/language/shell-language.js.map +0 -1
  247. package/dist/language/shell-source-code.d.ts.map +0 -1
  248. package/dist/language/shell-source-code.js.map +0 -1
  249. package/dist/language/source-coordinates.d.ts.map +0 -1
  250. package/dist/language/source-coordinates.js.map +0 -1
  251. package/dist/language/syntax-view.d.ts.map +0 -1
  252. package/dist/language/syntax-view.js.map +0 -1
  253. package/dist/language/word-resolution.d.ts.map +0 -1
  254. package/dist/language/word-resolution.js.map +0 -1
  255. package/dist/version.d.ts.map +0 -1
  256. 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
- /** Build one reusable coordinate index for the exact source passed to the parser. @internal */
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
- /** Only complete UTF-8 character boundaries have meaningful JavaScript counterparts. */
10
- #byteIndices = new Map();
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
- /** Build one reusable coordinate index for the exact source passed to the parser. @internal */
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
- this.#byteIndices.set(0, 0);
21
- const encoder = new TextEncoder();
22
- for (const character of source) {
23
- bytes += encoder.encode(character).length;
24
- index += character.length;
25
- this.#byteIndices.set(bytes, index);
26
- if (character === '\n') {
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.get(offset);
34
- if (!Number.isInteger(offset) || index === undefined) {
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 = Math.floor((low + high) / 2);
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
- /** An ESLint location counts UTF-16 units, with both line and column starting at one. @public */
3
- export interface ShellPosition {
4
- /** One-based source line, with CRLF treated as one line break. */
5
- readonly line: number;
6
- /** One-based UTF-16 column; tabs and grapheme widths are not expanded. */
7
- readonly column: number;
8
- }
9
- /**
10
- * A node in the consumer's lint view. Scalar/child fields use lowercase upstream
11
- * spellings; syntax fidelity belongs to the associated upstream tree. Auxiliary
12
- * view nodes may have spans derived from children and are not upstream Nodes.
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 interface ShellNode {
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 interface ShellFile extends ShellNode {
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 interface ShellComment extends ShellNode {
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. @internal */
32
- export function isShellNode(value) {
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' || !Object.hasOwn(SYNTAX_SCHEMA, raw.Type)) {
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
- const fields = {};
92
- const children = [];
93
- for (const [name, field] of Object.entries(schema.fields)) {
94
- if (field.kind === 'position') {
95
- continue;
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
- const value = raw[name];
98
- const key = name.toLowerCase();
99
- if (field.kind === 'struct' || field.kind === 'interface') {
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
- const projected = items
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
- const child = project(value, span);
113
- fields[key] = child;
114
- children.push(child);
249
+ view[field.key] = include(project(value, span));
115
250
  }
116
251
  }
117
252
  else {
118
- fields[key] = field.kind === 'string' ? sourceText(value) : value;
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 && children.length > 0) {
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
- const view = {
128
- ...fields,
129
- type: raw.Type,
130
- range: [span.start.index, span.end.index],
131
- loc: {
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(view, raw);
274
+ originalSyntax.set(node, raw);
275
+ views.set(raw, node);
138
276
  }
139
- return view;
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
- /** Consumer traversal visits each projected child, including auxiliary views, without claiming upstream Walk control semantics. @internal */
148
- export function walkShell(root, visit) {
149
- visit(root);
150
- for (const key of shellVisitorKeys[root.type] ?? []) {
151
- const value = root[key];
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
- walkShell(child, visit);
303
+ children.push(child);
156
304
  }
157
305
  }
158
306
  }
159
307
  else if (isShellNode(value)) {
160
- walkShell(value, visit);
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 { ShellNode } from './syntax-view.js';
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: ShellNode, options?: ResolveWordOptions): WordResolution;
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