@eslint-sh/eslint-plugin 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/README.md +14 -8
  2. package/dist/command-harvest/builtins/bash.d.ts +1 -1
  3. package/dist/command-harvest/builtins/bash.js +1 -1
  4. package/dist/command-harvest/builtins/bats.d.ts +2 -0
  5. package/dist/command-harvest/builtins/bats.js +3 -0
  6. package/dist/command-harvest/wrapper-table.d.ts +3 -19
  7. package/dist/command-harvest/wrapper-table.js +8 -38
  8. package/dist/configs/bash-3-2.d.ts +7 -6
  9. package/dist/configs/bash-3-2.js +4 -3
  10. package/dist/configs/posix-portable.d.ts +1 -1
  11. package/dist/declaration-conventions/read-declared-commands.d.ts +2 -2
  12. package/dist/declaration-conventions/read-declared-commands.js +2 -2
  13. package/dist/eslint-plugin-syntax.d.ts +4 -0
  14. package/dist/eslint-plugin.d.ts +86 -40
  15. package/dist/index.d.ts +5 -2
  16. package/dist/index.js +4 -2
  17. package/dist/language/shell-language-options.d.ts +18 -15
  18. package/dist/language/shell-language-options.js +33 -26
  19. package/dist/language/shell-language.d.ts +10 -9
  20. package/dist/language/shell-language.js +18 -35
  21. package/dist/language/shell-source-code.d.ts +17 -9
  22. package/dist/language/shell-source-code.js +53 -12
  23. package/dist/rules/bash-variable-semantics.d.ts +22 -0
  24. package/dist/rules/bash-variable-semantics.js +41 -0
  25. package/dist/rules/compat/create-compat-rule.js +1 -1
  26. package/dist/rules/compat/declaration-options.d.ts +2 -2
  27. package/dist/rules/compat/declaration-options.js +5 -2
  28. package/dist/rules/compat/no-append-all-redirect.js +1 -1
  29. package/dist/rules/compat/no-associative-arrays.js +2 -2
  30. package/dist/rules/compat/no-brace-sequence-padding-step.js +4 -4
  31. package/dist/rules/compat/no-case-attributes.js +3 -3
  32. package/dist/rules/compat/no-case-fallthrough.js +2 -2
  33. package/dist/rules/compat/no-case-modification.js +2 -2
  34. package/dist/rules/compat/no-coproc.js +1 -1
  35. package/dist/rules/compat/no-declare-g.js +3 -3
  36. package/dist/rules/compat/no-double-bracket.js +2 -2
  37. package/dist/rules/compat/no-epoch-variables.js +2 -2
  38. package/dist/rules/compat/no-fd-variable.js +1 -1
  39. package/dist/rules/compat/no-globstar.js +1 -1
  40. package/dist/rules/compat/no-lastpipe-semantics.js +2 -2
  41. package/dist/rules/compat/no-let.js +2 -1
  42. package/dist/rules/compat/no-mapfile.js +4 -2
  43. package/dist/rules/compat/no-nameref.js +3 -3
  44. package/dist/rules/compat/no-negative-array-index.js +39 -9
  45. package/dist/rules/compat/no-nounset-empty-array.js +15 -4
  46. package/dist/rules/compat/no-parameter-transformation.js +2 -2
  47. package/dist/rules/compat/no-pipe-all.js +1 -1
  48. package/dist/rules/compat/no-printf-time-format.js +4 -2
  49. package/dist/rules/compat/no-source.js +2 -1
  50. package/dist/rules/compat/no-test-v.js +4 -2
  51. package/dist/rules/compat/no-unicode-escape.js +2 -2
  52. package/dist/rules/compat/no-wait-n.js +4 -2
  53. package/dist/rules/compat/nounset-array-proof.d.ts +8 -0
  54. package/dist/rules/compat/nounset-array-proof.js +563 -0
  55. package/dist/rules/compat/posix-absent-command.d.ts +7 -8
  56. package/dist/rules/compat/posix-absent-command.js +17 -15
  57. package/dist/rules/compat/shopt-options.js +3 -1
  58. package/dist/rules/compat/version-gates.d.ts +9 -9
  59. package/dist/rules/compat/version-gates.js +13 -13
  60. package/dist/rules/create-wrapper-rule.js +12 -7
  61. package/dist/rules/no-dynamic-invocation.d.ts +0 -8
  62. package/dist/rules/no-dynamic-invocation.js +7 -15
  63. package/dist/rules/no-eval.js +3 -1
  64. package/dist/rules/no-masked-exit-status.js +15 -8
  65. package/dist/rules/no-secret-env-reads.d.ts +4 -3
  66. package/dist/rules/no-secret-env-reads.js +37 -14
  67. package/dist/rules/no-secret-flags.d.ts +2 -1
  68. package/dist/rules/no-secret-flags.js +7 -8
  69. package/dist/rules/no-unchecked-cd.js +3 -1
  70. package/dist/rules/no-undeclared-commands.d.ts +1 -15
  71. package/dist/rules/no-undeclared-commands.js +3 -15
  72. package/dist/rules/no-unused-declarations.d.ts +1 -18
  73. package/dist/rules/no-unused-declarations.js +9 -34
  74. package/dist/rules/prefer-local.js +4 -3
  75. package/dist/rules/quote-expansions.js +3 -28
  76. package/dist/rules/require-network-wrapper.d.ts +2 -17
  77. package/dist/rules/require-network-wrapper.js +33 -4
  78. package/dist/rules/resolve-declaration-convention.d.ts +5 -6
  79. package/dist/rules/resolve-declaration-convention.js +5 -6
  80. package/dist/rules/resolve-network-wrapper.d.ts +19 -0
  81. package/dist/rules/resolve-network-wrapper.js +57 -26
  82. package/dist/rules/resolve-transparent-wrappers.d.ts +43 -0
  83. package/dist/rules/resolve-transparent-wrappers.js +98 -0
  84. package/dist/rules/set-options.d.ts +2 -1
  85. package/dist/rules/set-options.js +17 -5
  86. package/dist/rules/shebang-interpreter.js +4 -2
  87. package/dist/rules/transparent-wrappers.d.ts +4 -3
  88. package/dist/rules/transparent-wrappers.js +5 -4
  89. package/dist/rules/user-pattern.d.ts +5 -3
  90. package/dist/rules/user-pattern.js +6 -4
  91. package/dist/syntax/index.d.ts +4 -0
  92. package/dist/syntax/index.js +4 -0
  93. package/docs/compat.md +76 -27
  94. package/docs/custom-rule-tutorial.md +9 -3
  95. package/docs/examples/no-listed-commands-rule.ts +2 -2
  96. package/docs/rules/README.md +36 -4
  97. package/docs/rules/_template.md +2 -2
  98. package/docs/rules/compat/no-append-all-redirect.md +12 -12
  99. package/docs/rules/compat/no-associative-arrays.md +14 -14
  100. package/docs/rules/compat/no-brace-sequence-padding-step.md +12 -12
  101. package/docs/rules/compat/no-case-attributes.md +13 -13
  102. package/docs/rules/compat/no-case-fallthrough.md +12 -12
  103. package/docs/rules/compat/no-case-modification.md +11 -11
  104. package/docs/rules/compat/no-coproc.md +13 -13
  105. package/docs/rules/compat/no-declare-g.md +13 -13
  106. package/docs/rules/compat/no-double-bracket.md +2 -2
  107. package/docs/rules/compat/no-epoch-variables.md +13 -13
  108. package/docs/rules/compat/no-fd-variable.md +12 -12
  109. package/docs/rules/compat/no-globstar.md +13 -13
  110. package/docs/rules/compat/no-lastpipe-semantics.md +13 -13
  111. package/docs/rules/compat/no-let.md +2 -2
  112. package/docs/rules/compat/no-mapfile.md +16 -16
  113. package/docs/rules/compat/no-nameref.md +13 -13
  114. package/docs/rules/compat/no-negative-array-index.md +14 -14
  115. package/docs/rules/compat/no-nounset-empty-array.md +72 -17
  116. package/docs/rules/compat/no-parameter-transformation.md +19 -19
  117. package/docs/rules/compat/no-pipe-all.md +13 -13
  118. package/docs/rules/compat/no-printf-time-format.md +13 -13
  119. package/docs/rules/compat/no-source.md +2 -2
  120. package/docs/rules/compat/no-test-v.md +12 -12
  121. package/docs/rules/compat/no-unicode-escape.md +13 -13
  122. package/docs/rules/compat/no-wait-n.md +13 -13
  123. package/docs/rules/no-dynamic-invocation.md +17 -12
  124. package/docs/rules/no-eval.md +6 -1
  125. package/docs/rules/no-masked-exit-status.md +4 -0
  126. package/docs/rules/no-secret-env-reads.md +34 -3
  127. package/docs/rules/no-secret-flags.md +19 -2
  128. package/docs/rules/no-unchecked-cd.md +9 -2
  129. package/docs/rules/no-undeclared-commands.md +16 -15
  130. package/docs/rules/no-unused-declarations.md +17 -20
  131. package/docs/rules/prefer-local.md +20 -2
  132. package/docs/rules/quote-expansions.md +8 -5
  133. package/docs/rules/require-network-wrapper.md +22 -17
  134. package/docs/rules/require-shebang.md +10 -4
  135. package/docs/rules/require-strict-mode.md +6 -0
  136. package/docs/shellcheck.md +9 -5
  137. package/docs/transparent-wrappers.md +97 -0
  138. package/docs/wrapper-rules.md +9 -0
  139. package/package.json +2 -2
  140. package/dist/language/end-of-input-marker.d.ts +0 -29
  141. package/dist/language/end-of-input-marker.js +0 -88
package/README.md CHANGED
@@ -24,9 +24,10 @@ recipes. Four shared configs ship, each supplying `plugins: { sh }` and `languag
24
24
  `sh/max-script-complexity`, with every rule at `error`. Its convention and wrapper rules report nothing until you bind a declaration
25
25
  convention or a wrapper. The [rules index](./docs/rules/README.md) lists all forty built-in rules,
26
26
  their presets, and
27
- autofix availability. The version-gated rules `sh/compat/no-append-all-redirect`, `sh/compat/no-associative-arrays`, `sh/compat/no-brace-sequence-padding-step`, `sh/compat/no-case-attributes`, `sh/compat/no-case-fallthrough`, `sh/compat/no-case-modification`, `sh/compat/no-coproc`, `sh/compat/no-declare-g`, `sh/compat/no-epoch-variables`, `sh/compat/no-fd-variable`, `sh/compat/no-globstar`, `sh/compat/no-lastpipe-semantics`, `sh/compat/no-mapfile`, `sh/compat/no-nameref`, `sh/compat/no-negative-array-index`, `sh/compat/no-nounset-empty-array`, `sh/compat/no-parameter-transformation`, `sh/compat/no-pipe-all`, `sh/compat/no-printf-time-format`, `sh/compat/no-test-v`, `sh/compat/no-unicode-escape` and `sh/compat/no-wait-n`, report features your target shell version
28
- lacks; `configs['bash-3.2']` (`shellVersion: '3.2'`) and `configs['posix-portable']` (`dialect: 'posix'`, with
29
- `sh/require-strict-mode` as `set -eu`) enable them for those targets. `configs['posix-portable']` also reports `[[`, `let` and `source`, which fail on dash (`sh/compat/no-double-bracket`, `sh/compat/no-let`, `sh/compat/no-source`), but not `local`, `echo -e` or the other constructs it leaves to shellcheck, so a clean run does not prove a script runs under every POSIX `sh`; see [version compatibility](./docs/compat.md). Run shellcheck alongside eslint-sh: shellcheck catches shell correctness
27
+ autofix availability. The version-gated rules `sh/compat/no-append-all-redirect`, `sh/compat/no-associative-arrays`, `sh/compat/no-brace-sequence-padding-step`, `sh/compat/no-case-attributes`, `sh/compat/no-case-fallthrough`, `sh/compat/no-case-modification`, `sh/compat/no-coproc`, `sh/compat/no-declare-g`, `sh/compat/no-epoch-variables`, `sh/compat/no-fd-variable`, `sh/compat/no-globstar`, `sh/compat/no-lastpipe-semantics`, `sh/compat/no-mapfile`, `sh/compat/no-nameref`, `sh/compat/no-negative-array-index`, `sh/compat/no-nounset-empty-array`, `sh/compat/no-parameter-transformation`, `sh/compat/no-pipe-all`, `sh/compat/no-printf-time-format`, `sh/compat/no-test-v`, `sh/compat/no-unicode-escape` and `sh/compat/no-wait-n`, report features the minimum Bash version your scripts must support lacks; `configs['bash-3.2']` (`minBashVersion: '3.2'`) and `configs['posix-portable']` (`dialect: 'posix'`, with
28
+ `sh/require-strict-mode` as `set -eu`) enable them for those targets, except that
29
+ `sh/compat/no-nounset-empty-array` is an explicit opt-in for `bash-3.2`. Enable it for scripts
30
+ that use `set -u` on Bash below 4.4 and have arrays that can really be empty. `configs['posix-portable']` also reports `[[`, `let` and `source`, which fail on dash (`sh/compat/no-double-bracket`, `sh/compat/no-let`, `sh/compat/no-source`), but not `local`, `echo -e` or the other constructs it leaves to shellcheck, so a clean run does not prove a script runs under every POSIX `sh`; see [version compatibility](./docs/compat.md). Run shellcheck alongside eslint-sh: shellcheck catches shell correctness
30
31
  problems; eslint-sh enforces project policy. [Running eslint-sh beside shellcheck](./docs/shellcheck.md)
31
32
  covers who owns what and the rules that overlap it deliberately (see also the
32
33
  [design rationale](https://github.com/mike-north/eslint-sh/blob/main/design/README.md#eslint-sh-and-shellcheck)).
@@ -78,7 +79,7 @@ ShellLanguage }, rules, configs: { recommended, 'agent-authored', 'bash-3.2', 'p
78
79
  - `ShellLanguage` — the `@eslint/core` `Language` implementation: `fileType: "text"`,
79
80
  `lineStart`/`columnStart: 1`, `nodeTypeKey: "type"`, the consumer's schema-derived `visitorKeys`,
80
81
  `validateLanguageOptions`/`normalizeLanguageOptions` for the
81
- `{dialect, shellVersion, directivePrefix}` schema, `parse`/`createSourceCode`, and
82
+ `{dialect, minBashVersion, directivePrefix}` schema, `parse`/`createSourceCode`, and
82
83
  `matchesSelectorClass` for the six launch esquery selector classes (`:command`, `:loop`,
83
84
  `:conditional`, `:expansion`, `:quoted`, `:function` — see the
84
85
  [selector cookbook](./docs/selectors.md)).
@@ -91,9 +92,9 @@ ShellLanguage }, rules, configs: { recommended, 'agent-authored', 'bash-3.2', 'p
91
92
  without following wrappers), and `resolveInvokedCommand(callExpr, { transparentWrappers? })` (the
92
93
  full `Argv0Resolution` once wrappers such as `env`, `sudo`, and `exec` are followed, including the
93
94
  wrapper `chain` and the `effective` command or the reason it is unknowable). The `dialect` and
94
- `shellVersion` accessors return the file's normalized language options.
95
+ `minBashVersion` accessors return the file's normalized language options.
95
96
  - `ShellLanguageOptions` / `NormalizedShellLanguageOptions` / `ShellLanguageOptionsJSON` — the
96
- `languageOptions` types (`dialect`, `shellVersion`, `directivePrefix`). `directivePrefix` is the
97
+ `languageOptions` types (`dialect`, `minBashVersion`, `directivePrefix`). `directivePrefix` is the
97
98
  prefix directive/inline-config comments must start with — e.g. the default `"eslint"` recognizes
98
99
  `# eslint-disable-next-line ...`; consumers who already overload `# eslint-*` comments for
99
100
  something else can configure a different prefix (e.g. `directivePrefix: 'shellcheck-sh'`).
@@ -154,9 +155,14 @@ ShellLanguage }, rules, configs: { recommended, 'agent-authored', 'bash-3.2', 'p
154
155
  `ShellRuleVisitor`. See [Writing your own rules](#writing-your-own-rules).
155
156
  - `resolveDeclarationConvention` / `DeclarationConventionBindingContext` — resolves the declaration
156
157
  convention bound for a rule (its `declarationConvention` option, else
157
- `settings.sh.declarationConvention`, else `null`) with the validation and error messages the
158
+ `settings: { sh: { declarationConvention: commentPragma } }`, else `null`) with the validation and error messages the
158
159
  built-in consumers use, so a rule of your own can read the convention. See [declaration
159
160
  conventions](./docs/declaration-conventions.md#reading-the-convention-in-your-own-rule).
161
+ - `resolveTransparentWrappers` / `TransparentWrappersBindingContext` — resolves project wrapper
162
+ facts from `settings: { sh: { transparentWrappers: ['with-retry'] } }` and the shared network
163
+ wrapper, composed with a rule's built-in scope. Every wrapper-following built-in uses it;
164
+ rule packs can use the same validation, precedence and path matching. See
165
+ [shared transparent wrappers](./docs/transparent-wrappers.md).
160
166
  - `createWrapperRule` / `WrapperRuleConfig` / `WrapperRuleSubcommands` / `WrapperSpec` — the
161
167
  factory for "command X must be run through wrapper Y" rules a rule pack
162
168
  binds to its own commands and wrapper. `sh/require-network-wrapper` is the built-in rule made
@@ -194,7 +200,7 @@ import {
194
200
  `…/syntax/analyze` takes a faithful `Node`. That is why the subpaths are kept apart from the
195
201
  root.
196
202
  - **Versioning.** `…/syntax` and `…/syntax/analyze` track this plugin's pinned `sh-ast` minor
197
- (currently `sh-ast@^0.7.0`). Under 0.x, a different `sh-ast` minor is a breaking change, so
203
+ (currently `sh-ast@^0.9.0`). Under 0.x, a different `sh-ast` minor is a breaking change, so
198
204
  an `sh-ast` 0.x minor bump is a minor bump of this plugin, and the changelog entry names the
199
205
  new `sh-ast` version.
200
206
 
@@ -17,7 +17,7 @@
17
17
  * Reserved words (`if`, `time`, `[[`, `function`, …) are not here: the
18
18
  * parser never produces them as command words.
19
19
  *
20
- * The list is the latest bash's, and is not narrowed by `shellVersion`:
20
+ * The list is the latest bash's, and is not narrowed by `minBashVersion`:
21
21
  * `mapfile`, `readarray` and `compopt` (added in bash 4.0) are builtins here
22
22
  * even for a file configured as bash 3.2. Whether a script may use them on
23
23
  * an old bash is a version-compatibility question, not a dependency one.
@@ -17,7 +17,7 @@
17
17
  * Reserved words (`if`, `time`, `[[`, `function`, …) are not here: the
18
18
  * parser never produces them as command words.
19
19
  *
20
- * The list is the latest bash's, and is not narrowed by `shellVersion`:
20
+ * The list is the latest bash's, and is not narrowed by `minBashVersion`:
21
21
  * `mapfile`, `readarray` and `compopt` (added in bash 4.0) are builtins here
22
22
  * even for a file configured as bash 3.2. Whether a script may use them on
23
23
  * an old bash is a version-compatibility question, not a dependency one.
@@ -32,6 +32,8 @@ export declare const BATS_HELPERS: readonly string[];
32
32
  /**
33
33
  * bats' `run`, as a transparent wrapper: it runs the command after its own
34
34
  * options and captures the result, so `run curl x` uses `curl`.
35
+ * Only the exact function name has these semantics; an executable path such
36
+ * as `/tmp/run` is an external dependency whose arguments remain opaque.
35
37
  *
36
38
  * Its options, all before the command (bats-core manual, "`run`: Test other
37
39
  * commands"): `-N` (expect exit status N, 0–255), `!` (expect a nonzero
@@ -16,6 +16,8 @@ export const BATS_HELPERS = Object.freeze([
16
16
  /**
17
17
  * bats' `run`, as a transparent wrapper: it runs the command after its own
18
18
  * options and captures the result, so `run curl x` uses `curl`.
19
+ * Only the exact function name has these semantics; an executable path such
20
+ * as `/tmp/run` is an external dependency whose arguments remain opaque.
19
21
  *
20
22
  * Its options, all before the command (bats-core manual, "`run`: Test other
21
23
  * commands"): `-N` (expect exit status N, 0–255), `!` (expect a nonzero
@@ -28,6 +30,7 @@ export const BATS_HELPERS = Object.freeze([
28
30
  */
29
31
  export const BATS_RUN_WRAPPER = Object.freeze({
30
32
  names: Object.freeze(['run']),
33
+ shellBuiltin: true,
31
34
  noArgFlags: Object.freeze(['!', '--keep-empty-lines', '--separate-stderr']),
32
35
  noArgFlagPattern: /^-\d+$/u,
33
36
  });
@@ -1,24 +1,8 @@
1
1
  import type { WrapperSpec } from 'sh-ast/analyze';
2
2
  import type { ShellDialect } from '../language/shell-language-options.js';
3
3
  /**
4
- * Returns the transparent-wrapper table for harvesting a `dialect` file
5
- * with the given configured wrapper names, in this order:
6
- *
7
- * 1. `sh-ast/analyze`'s `DEFAULT_TRANSPARENT_WRAPPERS` (`sudo`, `env`, …);
8
- * 2. the dialect's own wrappers (`run` under `bats`);
9
- * 3. the project's network wrapper (`settings.sh.networkWrapper`), when set,
10
- * with the options its spec declares: a wrapper that network commands must
11
- * run through runs the command after it;
12
- * 4. one name-only entry per configured name. A name-only entry recognizes
13
- * no flags, so `with-retry -n 3 curl` stops at `-n` rather than guessing
14
- * past it.
15
- *
16
- * The first entry whose names match is the one used, so a network wrapper
17
- * spec with options wins over a name-only entry for the same name.
18
- *
19
- * @param dialect - The linted file's `sourceCode.dialect`.
20
- * @param wrapperNames - The rule's `transparentWrappers` option names.
21
- * @param networkWrapper - The spec from `settings.sh.networkWrapper`, or `null`.
4
+ * Returns the built-in harvest scope for a dialect, including bats' in-process
5
+ * `run` helper only in bats files. Project wrappers are resolved separately.
22
6
  * @internal
23
7
  */
24
- export declare function harvestWrapperTable(dialect: ShellDialect, wrapperNames: readonly string[], networkWrapper?: WrapperSpec | null): readonly WrapperSpec[];
8
+ export declare function harvestWrapperTable(dialect: ShellDialect): readonly WrapperSpec[];
@@ -1,49 +1,19 @@
1
1
  /**
2
- * The one wrapper table that rules comparing declarations against usage
3
- * pass to `harvestCommands` (`./harvest-commands.ts`).
4
- *
5
- * `no-undeclared-commands` and `no-unused-declarations` must agree exactly on
6
- * which commands a file uses, or one would report a command the other calls
7
- * unused. Usage depends on the wrapper table, so both build it here from the
8
- * same inputs: the file's dialect, the network wrapper from
9
- * `settings.sh.networkWrapper`, and the rule's `transparentWrappers` option
10
- * names.
2
+ * Built-in wrappers used when comparing command declarations with usage.
3
+ * Both declaration rules compose this dialect scope with the shared project
4
+ * facts through resolveTransparentWrappers, so they compute the same usage.
11
5
  */
12
6
  import { DEFAULT_TRANSPARENT_WRAPPERS } from 'sh-ast/analyze';
13
7
  import { BATS_RUN_WRAPPER } from './builtins/bats.js';
14
- /**
15
- * Wrappers a dialect's runtime provides beyond sh-ast's defaults: bats' `run`
16
- * for `bats` files.
17
- */
8
+ /** Wrappers a dialect runtime provides beyond sh-ast's defaults. */
18
9
  const DIALECT_WRAPPERS = {
19
10
  bats: [BATS_RUN_WRAPPER],
20
11
  };
21
12
  /**
22
- * Returns the transparent-wrapper table for harvesting a `dialect` file
23
- * with the given configured wrapper names, in this order:
24
- *
25
- * 1. `sh-ast/analyze`'s `DEFAULT_TRANSPARENT_WRAPPERS` (`sudo`, `env`, …);
26
- * 2. the dialect's own wrappers (`run` under `bats`);
27
- * 3. the project's network wrapper (`settings.sh.networkWrapper`), when set,
28
- * with the options its spec declares: a wrapper that network commands must
29
- * run through runs the command after it;
30
- * 4. one name-only entry per configured name. A name-only entry recognizes
31
- * no flags, so `with-retry -n 3 curl` stops at `-n` rather than guessing
32
- * past it.
33
- *
34
- * The first entry whose names match is the one used, so a network wrapper
35
- * spec with options wins over a name-only entry for the same name.
36
- *
37
- * @param dialect - The linted file's `sourceCode.dialect`.
38
- * @param wrapperNames - The rule's `transparentWrappers` option names.
39
- * @param networkWrapper - The spec from `settings.sh.networkWrapper`, or `null`.
13
+ * Returns the built-in harvest scope for a dialect, including bats' in-process
14
+ * `run` helper only in bats files. Project wrappers are resolved separately.
40
15
  * @internal
41
16
  */
42
- export function harvestWrapperTable(dialect, wrapperNames, networkWrapper = null) {
43
- return [
44
- ...DEFAULT_TRANSPARENT_WRAPPERS,
45
- ...(DIALECT_WRAPPERS[dialect] ?? []),
46
- ...(networkWrapper === null ? [] : [networkWrapper]),
47
- ...wrapperNames.map((name) => ({ names: [name] })),
48
- ];
17
+ export function harvestWrapperTable(dialect) {
18
+ return [...DEFAULT_TRANSPARENT_WRAPPERS, ...(DIALECT_WRAPPERS[dialect] ?? [])];
49
19
  }
@@ -2,14 +2,15 @@
2
2
  * The `sh/bash-3.2` shared config: the preset for scripts that must run on the
3
3
  * Bash 3.2 that macOS ships as `/bin/bash`.
4
4
  *
5
- * The parser accepts every construct its grammar knows whatever target is
6
- * configured, so a Bash 3.2 target is enforced only by the `compat/*` rules.
7
- * This preset names that target once (`languageOptions.shellVersion: '3.2'`)
8
- * and turns on every version-gated rule that reads it, so a project states its
9
- * deployment target as a config choice instead of enumerating rules.
5
+ * The parser accepts every construct its grammar knows whatever floor is
6
+ * configured, so required Bash 3.2 support is enforced only by the `compat/*` rules.
7
+ * This preset names that floor once (`languageOptions.minBashVersion: '3.2'`)
8
+ * and enables the version gates except the opt-in empty-array nounset check,
9
+ * so a project states its minimum supported Bash as a config choice instead of
10
+ * enumerating rules.
10
11
  *
11
12
  * It follows the shape contract in `./recommended.ts`, with one deliberate
12
- * extension: it carries `languageOptions`, because the target version is the
13
+ * extension: it carries `languageOptions`, because the minimum supported version is the
13
14
  * preset's whole meaning and a pack that left it to the user would enable
14
15
  * rules that report nothing. It still carries no `files` or `ignores`, so it
15
16
  * inherits the user's globs, and it composes with the other presets through
@@ -1,7 +1,9 @@
1
1
  /**
2
- * The rules `sh/bash-3.2` enables: every version-gated `compat/*` rule, all at
2
+ * The rules `sh/bash-3.2` enables: twenty-one version-gated `compat/*` rules, all at
3
3
  * `error`, keyed as a user writes them (`sh/<rule-name>`). Each reports a
4
4
  * feature that Bash 3.2 lacks and names the portable alternative.
5
+ * `compat/no-nounset-empty-array` is opt-in: caller and helper contracts can
6
+ * guarantee non-empty arrays beyond the rule's local safety proofs.
5
7
  * `shebang-matches-dialect` is not a version gate and belongs to the
6
8
  * presets whose catalog row names it. The POSIX-only gates
7
9
  * (`compat/no-double-bracket`, `compat/no-let`, `compat/no-source`) are not
@@ -26,7 +28,6 @@ const bash32Rules = {
26
28
  'sh/compat/no-mapfile': 'error',
27
29
  'sh/compat/no-nameref': 'error',
28
30
  'sh/compat/no-negative-array-index': 'error',
29
- 'sh/compat/no-nounset-empty-array': 'error',
30
31
  'sh/compat/no-parameter-transformation': 'error',
31
32
  'sh/compat/no-pipe-all': 'error',
32
33
  'sh/compat/no-printf-time-format': 'error',
@@ -45,7 +46,7 @@ export function createBash32Config(plugin) {
45
46
  name: 'sh/bash-3.2',
46
47
  plugins: { sh: plugin },
47
48
  language: 'sh/sh',
48
- languageOptions: { dialect: 'bash', shellVersion: '3.2' },
49
+ languageOptions: { dialect: 'bash', minBashVersion: '3.2' },
49
50
  rules: { ...bash32Rules },
50
51
  };
51
52
  }
@@ -3,7 +3,7 @@
3
3
  * POSIX `sh` rather than for Bash.
4
4
  *
5
5
  * It sets `languageOptions.dialect: 'posix'`, which makes every Bash-only
6
- * feature unavailable at any `shellVersion`, and enables the `compat/*` rules
6
+ * feature unavailable at any `minBashVersion`, and enables the `compat/*` rules
7
7
  * that report those features. The dialect is also what every dialect-aware
8
8
  * rule reads, so the preset is one switch rather than a list of settings that
9
9
  * could drift apart.
@@ -44,8 +44,8 @@ export declare function toLintedFile(context: LintedFile): LintedFile;
44
44
  * `filename`, `physicalFilename` and `cwd`, never the whole context, so it
45
45
  * sees which file it is reading and nothing it could report through.
46
46
  *
47
- * Throws an `Error` that names the setting, starting
48
- * `Invalid settings.sh.declarationConvention:`, and shows the offending value when
47
+ * Throws an `Error` naming the declaration-convention setting and showing the
48
+ * offending value when
49
49
  * `declaredCommands` does not return an array, or returns a declaration with
50
50
  * a missing or unknown `kind`, without its kind's fields, or with a `range`
51
51
  * outside the linted text.
@@ -88,8 +88,8 @@ function describe(value) {
88
88
  * `filename`, `physicalFilename` and `cwd`, never the whole context, so it
89
89
  * sees which file it is reading and nothing it could report through.
90
90
  *
91
- * Throws an `Error` that names the setting, starting
92
- * `Invalid settings.sh.declarationConvention:`, and shows the offending value when
91
+ * Throws an `Error` naming the declaration-convention setting and showing the
92
+ * offending value when
93
93
  * `declaredCommands` does not return an array, or returns a declaration with
94
94
  * a missing or unknown `kind`, without its kind's fields, or with a `range`
95
95
  * outside the linted text.
@@ -18,6 +18,10 @@
18
18
  * `@eslint-sh/eslint-plugin/syntax/analyze`. That is why these names live on their own subpath
19
19
  * and are not merged into the root.
20
20
  *
21
+ * The low-level `printSync` keeps upstream behavior, including changes to literal vertical tabs
22
+ * and form feeds. It does not apply the high-level content refusal used by `format` and
23
+ * `ShellSourceCode.getPrintedReplacement`; callers printing directly must account for that loss.
24
+ *
21
25
  * @packageDocumentation
22
26
  */
23
27
 
@@ -333,7 +333,7 @@ export declare interface LintedFile {
333
333
  export declare interface NormalizedShellLanguageOptions extends Record<string, unknown> {
334
334
  readonly dialect: ShellDialect;
335
335
  readonly directivePrefix: string;
336
- readonly shellVersion?: string;
336
+ readonly minBashVersion?: string;
337
337
  toJSON(): ShellLanguageOptionsJSON;
338
338
  }
339
339
 
@@ -362,8 +362,9 @@ export declare interface NormalizedShellLanguageOptions extends Record<string, u
362
362
  * machine-written, human-reviewed scripts:
363
363
  * every `recommended` rule plus the contract rules, all at `error`.
364
364
  * `configs['bash-3.2']` and `configs['posix-portable']` are deployment-target
365
- * packs: the first sets `languageOptions.shellVersion: "3.2"` and enables
366
- * every version-gated `compat/*` rule; the second sets
365
+ * packs: the first sets `languageOptions.minBashVersion: "3.2"` and enables
366
+ * the version-gated `compat/*` rules except the opt-in
367
+ * `compat/no-nounset-empty-array`; the second sets
367
368
  * `languageOptions.dialect: "posix"`, enables the `compat/*` rules and the
368
369
  * shebang/dialect check, and requires `set -eu`; it reports named constructs
369
370
  * that fail on dash, not every non-POSIX construct, and leaves `local`,
@@ -407,8 +408,8 @@ export default plugin;
407
408
  * `filename`, `physicalFilename` and `cwd`, never the whole context, so it
408
409
  * sees which file it is reading and nothing it could report through.
409
410
  *
410
- * Throws an `Error` that names the setting, starting
411
- * `Invalid settings.sh.declarationConvention:`, and shows the offending value when
411
+ * Throws an `Error` naming the declaration-convention setting and showing the
412
+ * offending value when
412
413
  * `declaredCommands` does not return an array, or returns a declaration with
413
414
  * a missing or unknown `kind`, without its kind's fields, or with a `range`
414
415
  * outside the linted text.
@@ -437,16 +438,15 @@ export { ResolveArgv0Options }
437
438
  /**
438
439
  * Resolves the declaration convention bound for the rule running in
439
440
  * `context`: the rule's `declarationConvention` option, else
440
- * `settings.sh.declarationConvention`, else `null` (the rule is inert).
441
+ * `settings: { sh: { declarationConvention: convention } }`, else `null`
442
+ * (the rule is inert).
441
443
  *
442
444
  * Throws an `Error` naming the offending setting when the configured value is
443
445
  * present but does not implement `declaredCommands` (or has a non-function
444
446
  * `removeDeclaration`). The message names where the value came from and what
445
- * it was, for example one beginning
446
- * `Invalid settings.sh.declarationConvention: expected a declaration convention`
447
- * and ending `but got string "jq".` Call it from `create()`, so the error
448
- * surfaces as ESLint's
449
- * configuration message for the file being linted.
447
+ * it was, including the expected convention shape and the received value.
448
+ * Call it from `create()`, so the error surfaces as ESLint's configuration
449
+ * message for the file being linted.
450
450
  *
451
451
  * A rule that receives `null` should register no listeners: with no
452
452
  * convention bound, a convention consumer has nothing to check.
@@ -480,6 +480,34 @@ export { ResolveArgv0Options }
480
480
  */
481
481
  export declare function resolveDeclarationConvention(context: DeclarationConventionBindingContext): DeclarationConvention | null;
482
482
 
483
+ /**
484
+ * Returns the shared project wrappers followed by `builtIns` (defaults when
485
+ * omitted). Entries accept command names or JSON-expressible `WrapperSpec`s
486
+ * in `settings: { sh: { transparentWrappers: [...] } }`. The shared network
487
+ * wrapper is also transparent; conflicting specs for the same project name
488
+ * throw rather than allowing policies to disagree. Shared specs compare effective
489
+ * grammars for overlapping names, normalizing matcher defaults and retaining
490
+ * expanded built-in flag patterns; an extra transparent alias does not acquire
491
+ * network approval. Explicit specs remain complete replacements. Duplicate project
492
+ * names are rejected so list order cannot silently choose their meaning.
493
+ *
494
+ * Utility wrappers match absolute and relative paths by basename. Path words
495
+ * remain unchanged in analysis results. `shellBuiltin: true` entries match
496
+ * exact names only: an executable path cannot acquire in-process semantics.
497
+ * An explicit path spec takes precedence over a basename fallback regardless
498
+ * of configuration order.
499
+ * Aliases are built even when only `builtIns` are supplied. Overridden names
500
+ * are removed from lower-priority entries, preserving their unrelated aliases
501
+ * and preventing a utility entry from undoing an exact-name builtin override.
502
+ * The input tables are never mutated.
503
+ *
504
+ * @param context - The consuming rule's context.
505
+ * @param builtIns - The rule's own built-in wrapper scope.
506
+ * @throws Error when a configured wrapper is malformed or contradictory.
507
+ * @public
508
+ */
509
+ export declare function resolveTransparentWrappers(context: TransparentWrappersBindingContext, builtIns?: readonly WrapperSpec[]): readonly WrapperSpec[];
510
+
483
511
  /**
484
512
  * Resolves a parsed lint Word using the upstream-shaped syntax associated with it.
485
513
  * The view supplies locations and selector fields; sh-ast/analyze owns the word
@@ -610,11 +638,12 @@ export declare class ShellLanguage implements Language {
610
638
  readonly defaultLanguageOptions: ShellLanguageOptions;
611
639
  /**
612
640
  * Validates `languageOptions.dialect` (must be one of the five supported
613
- * dialects, if present), `languageOptions.shellVersion` (must be a
614
- * string matching the `MAJOR[.MINOR]` numeric shape, if present), and
641
+ * dialects, if present), `languageOptions.minBashVersion` (must be a
642
+ * string matching the `MAJOR[.MINOR]` numeric shape, if present, and
643
+ * applies only to `bash` and `bats`), and
615
644
  * `languageOptions.directivePrefix` (must be a valid directive-comment
616
- * label, if present). Unknown keys are silently ignored, per the
617
- * `Language` contract. See `shell-language-options.ts`'s
645
+ * label, if present). The removed `shellVersion` key throws with
646
+ * migration guidance; other unknown keys are silently ignored. See `shell-language-options.ts`'s
618
647
  * `validateShellLanguageOptions` for the full rationale.
619
648
  */
620
649
  validateLanguageOptions(languageOptions: ShellLanguageOptions): void;
@@ -648,9 +677,9 @@ export declare class ShellLanguage implements Language {
648
677
  * A file whose extension marks it as a known non-shell source type (for
649
678
  * example `.js` or `.json`) is never shell-parsed: it fails with a single
650
679
  * diagnostic explaining that a `language: 'sh/sh'` config entry without
651
- * `files` reached it, and how to scope that entry. A source in which
652
- * the parser silently drops text at a U+0080 fails at that character; a
653
- * U+0080 the parser reads as an ordinary character does not.
680
+ * `files` reached it, and how to scope that entry. Unicode characters remain
681
+ * source text. A native nesting-budget refusal returns no partial tree and
682
+ * becomes a fatal resource diagnostic at 1:1.
654
683
  */
655
684
  parse(file: File_2, context: {
656
685
  languageOptions: ShellLanguageOptions;
@@ -665,8 +694,8 @@ export declare class ShellLanguage implements Language {
665
694
  * present, but `?? 'eslint'` stays as an explicit, honest fallback for
666
695
  * direct construction (e.g. tests that skip the `Language` normalization
667
696
  * step), mirroring `parse()`'s `dialect` default above. `dialect` and
668
- * `shellVersion` are forwarded so rules read the same normalized values
669
- * through `sourceCode.dialect` / `sourceCode.shellVersion`.
697
+ * `minBashVersion` are forwarded so rules read the same normalized values
698
+ * through `sourceCode.dialect` / `sourceCode.minBashVersion`.
670
699
  */
671
700
  createSourceCode(file: File_2, parseResult: OkParseResult<ShellFile>, context: {
672
701
  languageOptions: ShellLanguageOptions;
@@ -675,7 +704,7 @@ export declare class ShellLanguage implements Language {
675
704
 
676
705
  /**
677
706
  * `languageOptions` accepted by {@link ShellLanguage}: `dialect` selects
678
- * mvdan/sh's `LangVariant` through a consumer-owned mapping (default `"bash"`); `shellVersion`
707
+ * mvdan/sh's `LangVariant` through a consumer-owned mapping (default `"bash"`); `minBashVersion`
679
708
  * is consumed by the rule layer (e.g. the bash-3.2 compatibility pack),
680
709
  * never by the parser itself.
681
710
  *
@@ -691,13 +720,14 @@ export declare class ShellLanguage implements Language {
691
720
  export declare interface ShellLanguageOptions extends Record<string, unknown> {
692
721
  readonly dialect?: ShellDialect;
693
722
  /**
694
- * The Bash version (`MAJOR[.MINOR]`, such as `"3.2"`) the scripts must run
695
- * on. The parser ignores it. The `compat/*` rules read it: unset means the
696
- * newest version, versions compare numerically by major then minor, and
697
- * under `dialect: "posix"` Bash-only features are unavailable at any
698
- * version.
723
+ * The minimum Bash version (`MAJOR[.MINOR]`, such as `"3.2"`) the
724
+ * script must support, including every newer Bash. Only valid with the
725
+ * `bash` or `bats` dialect (the default is `bash`). The parser ignores
726
+ * it; `compat/*` rules report features unavailable at this floor. Unset
727
+ * means no Bash version constraint. Versions compare numerically by
728
+ * major then minor, with no upper version limit.
699
729
  */
700
- readonly shellVersion?: string;
730
+ readonly minBashVersion?: string;
701
731
  /**
702
732
  * The prefix directive/inline-config comments must start with — e.g. the
703
733
  * default `"eslint"` recognizes `# eslint-disable-next-line ...` and
@@ -711,14 +741,14 @@ export declare interface ShellLanguageOptions extends Record<string, unknown> {
711
741
 
712
742
  /**
713
743
  * The plain-data shape {@link NormalizedShellLanguageOptions.toJSON} returns:
714
- * `dialect` and `directivePrefix` always present (both defaulted), `shellVersion`
744
+ * `dialect` and `directivePrefix` always present (both defaulted), `minBashVersion`
715
745
  * present only when the caller supplied one. Keys are always emitted in this
716
746
  * fixed order so that two logically-identical inputs serialize identically
717
747
  * regardless of the key order the caller happened to use — the property this
718
748
  * type exists to guarantee for cache-key stability (see
719
749
  * {@link NormalizedShellLanguageOptions}). `directivePrefix` is included
720
- * unconditionally (like `dialect`, unlike `shellVersion`) because — like
721
- * `dialect` and unlike `shellVersion` — it always has a concrete effect on
750
+ * unconditionally (like `dialect`, unlike `minBashVersion`) because — like
751
+ * `dialect` and unlike `minBashVersion` — it always has a concrete effect on
722
752
  * linting results (which comments the `SourceCode` recognizes as directives)
723
753
  * whether or not the caller supplied one, so two configs that differ only in
724
754
  * an *implicit* default vs. an *explicit* `directivePrefix: "eslint"` must
@@ -729,7 +759,7 @@ export declare interface ShellLanguageOptions extends Record<string, unknown> {
729
759
  export declare interface ShellLanguageOptionsJSON {
730
760
  readonly dialect: ShellDialect;
731
761
  readonly directivePrefix: string;
732
- readonly shellVersion?: string;
762
+ readonly minBashVersion?: string;
733
763
  }
734
764
 
735
765
  /**
@@ -974,7 +1004,7 @@ export declare interface ShellRuleVisitor extends ShellTypedListeners {
974
1004
  * the string `jq`?", "which command does this invocation run?", and "which
975
1005
  * commands does this file run, and where?" by delegating to
976
1006
  * `sh-ast/analyze`. They never reinterpret words themselves, so every rule
977
- * gets the same answer for the same syntax. `dialect` and `shellVersion`
1007
+ * gets the same answer for the same syntax. `dialect` and `minBashVersion`
978
1008
  * expose the file's normalized language options.
979
1009
  *
980
1010
  * Syntax access (`getSyntax`, `getViewNode`, `getIndexFromSyntaxPos`,
@@ -1001,7 +1031,7 @@ export declare class ShellSourceCode extends TextSourceCodeBase<{
1001
1031
  ast: ShellFile;
1002
1032
  directivePrefix?: string;
1003
1033
  dialect?: ShellDialect;
1004
- shellVersion?: string;
1034
+ minBashVersion?: string;
1005
1035
  });
1006
1036
  /**
1007
1037
  * The shell dialect this file was parsed as: the normalized
@@ -1010,11 +1040,12 @@ export declare class ShellSourceCode extends TextSourceCodeBase<{
1010
1040
  */
1011
1041
  get dialect(): ShellDialect;
1012
1042
  /**
1013
- * The configured `languageOptions.shellVersion` (a `MAJOR[.MINOR]` string
1043
+ * The configured `languageOptions.minBashVersion` (a `MAJOR[.MINOR]` string
1014
1044
  * such as `"3.2"`), or `undefined` when the config does not set one. The
1015
- * parser never reads it; it exists for rules that gate on shell version.
1045
+ * script must support this Bash and every newer release. The parser never
1046
+ * reads it; compatibility rules use it as a floor with no upper limit.
1016
1047
  */
1017
- get shellVersion(): string | undefined;
1048
+ get minBashVersion(): string | undefined;
1018
1049
  /**
1019
1050
  * The one-based location of a UTF-16 `index` in this file's text, where
1020
1051
  * `index` may be anywhere from 0 to `text.length`. CRLF counts as one line
@@ -1103,13 +1134,19 @@ export declare class ShellSourceCode extends TextSourceCodeBase<{
1103
1134
  * effective command's name as
1104
1135
  * `res.effective.static ? res.effective.text : null`.
1105
1136
  * `sudo -u x "$prog"` and `exec "$@"` have a non-static `effective` with
1106
- * reason `expansion`; `env -S '…'` has reason `embedded-command`; `chain`
1137
+ * reason `expansion`; `env -S '…'` and `bash -c '…'` have reason
1138
+ * `embedded-command`; `chain`
1107
1139
  * lists every word resolved along the way, outermost wrapper first, and
1108
1140
  * `chainIndices` gives each entry's position in the command's `argv` (as
1109
1141
  * {@link ShellSourceCode.getCommandSites} reports it). When `effective` is
1110
1142
  * static, the effective command's arguments are
1111
1143
  * `argv.slice(chainIndices.at(-1)! + 1)`: every entry after the effective
1112
- * command's own index.
1144
+ * command's own index. For an interpreter command string the final chain
1145
+ * index instead locates the selector flag; the interpreter is in `chain.at(-2)`.
1146
+ * `embeddedCommandInterpreters: []` retains the interpreter as the static
1147
+ * effective command when a policy needs program identity. Utility wrappers
1148
+ * can match absolute-path basenames; `shellBuiltin: true` entries require
1149
+ * exact names, so external paths do not acquire in-process semantics.
1113
1150
  *
1114
1151
  * Enumeration runs once per file, the first time any command helper is
1115
1152
  * called. If the file's nesting exceeds `enumerateCommands`'s recursion
@@ -1270,8 +1307,9 @@ export declare class ShellSourceCode extends TextSourceCodeBase<{
1270
1307
  * @throws TypeError when `node` has no original record in this file (see
1271
1308
  * {@link ShellSourceCode.getEditableSyntax}), when `edited` is not a
1272
1309
  * printable root (the message names its type), or when the printer's output
1273
- * is not valid UTF-8. Non-UTF-8 output comes back from `printSync` as a
1274
- * `Uint8Array` and is never decoded lossily into a fix.
1310
+ * is not valid UTF-8, or the edited syntax contains literal vertical tabs or
1311
+ * form feeds that the printer cannot preserve. Non-UTF-8 output comes back
1312
+ * from `printSync` as a `Uint8Array` and is never decoded lossily into a fix.
1275
1313
  * @throws `PrintError` or `PrintPanicError` (from sh-ast) when upstream's
1276
1314
  * printer fails for `edited` or `options`.
1277
1315
  */
@@ -1400,6 +1438,14 @@ export declare type ShellTypedListeners = {
1400
1438
  [Type in ShellNodeType as Type | `${Type}:exit`]?: ShellListener<ShellNodeOf<Type>>;
1401
1439
  };
1402
1440
 
1441
+ /** The rule-context fields needed to resolve shared wrappers and their path spellings. @public */
1442
+ export declare interface TransparentWrappersBindingContext {
1443
+ /** Shared ESLint configuration containing the project's shell facts. */
1444
+ readonly settings: SettingsConfig;
1445
+ /** The current file supplies path spellings without filesystem inspection or execution. */
1446
+ readonly sourceCode: ShellSourceCode;
1447
+ }
1448
+
1403
1449
  export { WordResolution }
1404
1450
 
1405
1451
  /**
package/dist/index.d.ts CHANGED
@@ -14,6 +14,8 @@ export type { DeclarationConvention, DeclaredCommand, FileDeclaredCommand, Linte
14
14
  export { readDeclaredCommands } from './declaration-conventions/read-declared-commands.js';
15
15
  export type { DeclarationReadContext } from './declaration-conventions/read-declared-commands.js';
16
16
  export { commentPragma } from './declaration-conventions/comment-pragma.js';
17
+ export { resolveTransparentWrappers } from './rules/resolve-transparent-wrappers.js';
18
+ export type { TransparentWrappersBindingContext } from './rules/resolve-transparent-wrappers.js';
17
19
  export { resolveDeclarationConvention } from './rules/resolve-declaration-convention.js';
18
20
  export type { DeclarationConventionBindingContext } from './rules/resolve-declaration-convention.js';
19
21
  export { defineShellRule } from './rules/define-shell-rule.js';
@@ -46,8 +48,9 @@ export type { WrapperSpec } from 'sh-ast/analyze';
46
48
  * machine-written, human-reviewed scripts:
47
49
  * every `recommended` rule plus the contract rules, all at `error`.
48
50
  * `configs['bash-3.2']` and `configs['posix-portable']` are deployment-target
49
- * packs: the first sets `languageOptions.shellVersion: "3.2"` and enables
50
- * every version-gated `compat/*` rule; the second sets
51
+ * packs: the first sets `languageOptions.minBashVersion: "3.2"` and enables
52
+ * the version-gated `compat/*` rules except the opt-in
53
+ * `compat/no-nounset-empty-array`; the second sets
51
54
  * `languageOptions.dialect: "posix"`, enables the `compat/*` rules and the
52
55
  * shebang/dialect check, and requires `set -eu`; it reports named constructs
53
56
  * that fail on dash, not every non-POSIX construct, and leaves `local`,
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ export { ShellSourceCode } from './language/shell-source-code.js';
10
10
  export { resolveWord } from './language/word-resolution.js';
11
11
  export { readDeclaredCommands } from './declaration-conventions/read-declared-commands.js';
12
12
  export { commentPragma } from './declaration-conventions/comment-pragma.js';
13
+ export { resolveTransparentWrappers } from './rules/resolve-transparent-wrappers.js';
13
14
  export { resolveDeclarationConvention } from './rules/resolve-declaration-convention.js';
14
15
  export { defineShellRule } from './rules/define-shell-rule.js';
15
16
  export { createWrapperRule } from './rules/create-wrapper-rule.js';
@@ -40,8 +41,9 @@ export { createWrapperRule } from './rules/create-wrapper-rule.js';
40
41
  * machine-written, human-reviewed scripts:
41
42
  * every `recommended` rule plus the contract rules, all at `error`.
42
43
  * `configs['bash-3.2']` and `configs['posix-portable']` are deployment-target
43
- * packs: the first sets `languageOptions.shellVersion: "3.2"` and enables
44
- * every version-gated `compat/*` rule; the second sets
44
+ * packs: the first sets `languageOptions.minBashVersion: "3.2"` and enables
45
+ * the version-gated `compat/*` rules except the opt-in
46
+ * `compat/no-nounset-empty-array`; the second sets
45
47
  * `languageOptions.dialect: "posix"`, enables the `compat/*` rules and the
46
48
  * shebang/dialect check, and requires `set -eu`; it reports named constructs
47
49
  * that fail on dash, not every non-POSIX construct, and leaves `local`,