@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.
- package/README.md +223 -17
- package/dist/command-harvest/builtins/bash.d.ts +36 -0
- package/dist/command-harvest/builtins/bash.js +98 -0
- package/dist/command-harvest/builtins/bats.d.ts +45 -0
- package/dist/command-harvest/builtins/bats.js +33 -0
- package/dist/command-harvest/builtins/index.d.ts +18 -0
- package/dist/command-harvest/builtins/index.js +39 -0
- package/dist/command-harvest/builtins/posix.d.ts +51 -0
- package/dist/command-harvest/builtins/posix.js +93 -0
- package/dist/command-harvest/harvest-commands.d.ts +66 -0
- package/dist/command-harvest/harvest-commands.js +97 -0
- package/dist/command-harvest/wrapper-table.d.ts +24 -0
- package/dist/command-harvest/wrapper-table.js +49 -0
- package/dist/configs/agent-authored.d.ts +41 -0
- package/dist/configs/agent-authored.js +33 -0
- package/dist/configs/bash-3-2.d.ts +28 -0
- package/dist/configs/bash-3-2.js +50 -0
- package/dist/configs/posix-portable.d.ts +40 -0
- package/dist/configs/posix-portable.js +52 -0
- package/dist/configs/recommended.d.ts +52 -0
- package/dist/configs/recommended.js +34 -0
- package/dist/declaration-conventions/comment-pragma.d.ts +33 -0
- package/dist/declaration-conventions/comment-pragma.js +153 -0
- package/dist/declaration-conventions/declaration-convention.d.ts +189 -0
- package/dist/declaration-conventions/declaration-convention.js +1 -0
- package/dist/declaration-conventions/read-declared-commands.d.ts +70 -0
- package/dist/declaration-conventions/read-declared-commands.js +128 -0
- package/dist/eslint-plugin-syntax-analyze.d.ts +23 -0
- package/dist/eslint-plugin-syntax.d.ts +27 -0
- package/dist/eslint-plugin.d.ts +1211 -52
- package/dist/index.d.ts +56 -8
- package/dist/index.js +53 -6
- package/dist/language/command-resolution.d.ts +124 -0
- package/dist/language/command-resolution.js +120 -0
- package/dist/language/end-of-input-marker.d.ts +29 -0
- package/dist/language/end-of-input-marker.js +88 -0
- package/dist/language/line-breaks.d.ts +18 -0
- package/dist/language/line-breaks.js +22 -0
- package/dist/language/lint-view-types.d.ts +104 -0
- package/dist/language/lint-view-types.js +1 -0
- package/dist/language/non-shell-file-guard.d.ts +35 -0
- package/dist/language/non-shell-file-guard.js +59 -0
- package/dist/language/selector-classes.d.ts +0 -1
- package/dist/language/selector-classes.js +0 -1
- package/dist/language/shell-language-options.d.ts +7 -1
- package/dist/language/shell-language-options.js +4 -4
- package/dist/language/shell-language.d.ts +10 -2
- package/dist/language/shell-language.js +59 -8
- package/dist/language/shell-source-code.d.ts +342 -5
- package/dist/language/shell-source-code.js +559 -30
- package/dist/language/source-coordinates.d.ts +13 -2
- package/dist/language/source-coordinates.js +68 -14
- package/dist/language/syntax-view.d.ts +60 -39
- package/dist/language/syntax-view.js +200 -45
- package/dist/language/word-resolution.d.ts +4 -3
- package/dist/language/word-resolution.js +4 -3
- package/dist/rules/compat/create-compat-rule.d.ts +59 -0
- package/dist/rules/compat/create-compat-rule.js +65 -0
- package/dist/rules/compat/declaration-options.d.ts +19 -0
- package/dist/rules/compat/declaration-options.js +110 -0
- package/dist/rules/compat/no-append-all-redirect.d.ts +13 -0
- package/dist/rules/compat/no-append-all-redirect.js +73 -0
- package/dist/rules/compat/no-associative-arrays.d.ts +12 -0
- package/dist/rules/compat/no-associative-arrays.js +39 -0
- package/dist/rules/compat/no-case-attributes.d.ts +12 -0
- package/dist/rules/compat/no-case-attributes.js +63 -0
- package/dist/rules/compat/no-case-fallthrough.d.ts +12 -0
- package/dist/rules/compat/no-case-fallthrough.js +67 -0
- package/dist/rules/compat/no-case-modification.d.ts +35 -0
- package/dist/rules/compat/no-case-modification.js +86 -0
- package/dist/rules/compat/no-coproc.d.ts +12 -0
- package/dist/rules/compat/no-coproc.js +47 -0
- package/dist/rules/compat/no-declare-g.d.ts +12 -0
- package/dist/rules/compat/no-declare-g.js +59 -0
- package/dist/rules/compat/no-double-bracket.d.ts +12 -0
- package/dist/rules/compat/no-double-bracket.js +52 -0
- package/dist/rules/compat/no-epoch-variables.d.ts +12 -0
- package/dist/rules/compat/no-epoch-variables.js +57 -0
- package/dist/rules/compat/no-fd-variable.d.ts +12 -0
- package/dist/rules/compat/no-fd-variable.js +48 -0
- package/dist/rules/compat/no-globstar.d.ts +12 -0
- package/dist/rules/compat/no-globstar.js +39 -0
- package/dist/rules/compat/no-lastpipe-semantics.d.ts +12 -0
- package/dist/rules/compat/no-lastpipe-semantics.js +53 -0
- package/dist/rules/compat/no-let.d.ts +12 -0
- package/dist/rules/compat/no-let.js +62 -0
- package/dist/rules/compat/no-mapfile.d.ts +13 -0
- package/dist/rules/compat/no-mapfile.js +122 -0
- package/dist/rules/compat/no-nameref.d.ts +12 -0
- package/dist/rules/compat/no-nameref.js +44 -0
- package/dist/rules/compat/no-negative-array-index.d.ts +13 -0
- package/dist/rules/compat/no-negative-array-index.js +123 -0
- package/dist/rules/compat/no-nounset-empty-array.d.ts +13 -0
- package/dist/rules/compat/no-nounset-empty-array.js +158 -0
- package/dist/rules/compat/no-parameter-transformation.d.ts +13 -0
- package/dist/rules/compat/no-parameter-transformation.js +123 -0
- package/dist/rules/compat/no-pipe-all.d.ts +13 -0
- package/dist/rules/compat/no-pipe-all.js +68 -0
- package/dist/rules/compat/no-printf-time-format.d.ts +12 -0
- package/dist/rules/compat/no-printf-time-format.js +99 -0
- package/dist/rules/compat/no-source.d.ts +13 -0
- package/dist/rules/compat/no-source.js +103 -0
- package/dist/rules/compat/no-test-v.d.ts +12 -0
- package/dist/rules/compat/no-test-v.js +280 -0
- package/dist/rules/compat/no-unicode-escape.d.ts +14 -0
- package/dist/rules/compat/no-unicode-escape.js +120 -0
- package/dist/rules/compat/no-wait-n.d.ts +12 -0
- package/dist/rules/compat/no-wait-n.js +73 -0
- package/dist/rules/compat/operator-range.d.ts +10 -0
- package/dist/rules/compat/operator-range.js +15 -0
- package/dist/rules/compat/posix-absent-command.d.ts +12 -0
- package/dist/rules/compat/posix-absent-command.js +134 -0
- package/dist/rules/compat/shopt-options.d.ts +9 -0
- package/dist/rules/compat/shopt-options.js +63 -0
- package/dist/rules/compat/version-gates.d.ts +76 -0
- package/dist/rules/compat/version-gates.js +197 -0
- package/dist/rules/create-rule.d.ts +91 -0
- package/dist/rules/create-rule.js +51 -0
- package/dist/rules/create-wrapper-rule.d.ts +166 -0
- package/dist/rules/create-wrapper-rule.js +407 -0
- package/dist/rules/define-shell-rule.d.ts +184 -0
- package/dist/rules/define-shell-rule.js +56 -0
- package/dist/rules/index.d.ts +26 -0
- package/dist/rules/index.js +88 -0
- package/dist/rules/max-script-complexity.d.ts +28 -0
- package/dist/rules/max-script-complexity.js +130 -0
- package/dist/rules/no-dynamic-invocation.d.ts +35 -0
- package/dist/rules/no-dynamic-invocation.js +117 -0
- package/dist/rules/no-eval.d.ts +13 -0
- package/dist/rules/no-eval.js +89 -0
- package/dist/rules/no-masked-exit-status.d.ts +21 -0
- package/dist/rules/no-masked-exit-status.js +667 -0
- package/dist/rules/no-secret-env-reads.d.ts +24 -0
- package/dist/rules/no-secret-env-reads.js +133 -0
- package/dist/rules/no-secret-flags.d.ts +32 -0
- package/dist/rules/no-secret-flags.js +162 -0
- package/dist/rules/no-unchecked-cd.d.ts +44 -0
- package/dist/rules/no-unchecked-cd.js +250 -0
- package/dist/rules/no-undeclared-commands.d.ts +20 -0
- package/dist/rules/no-undeclared-commands.js +115 -0
- package/dist/rules/no-unused-declarations.d.ts +23 -0
- package/dist/rules/no-unused-declarations.js +259 -0
- package/dist/rules/prefer-local.d.ts +16 -0
- package/dist/rules/prefer-local.js +249 -0
- package/dist/rules/quote-expansions.d.ts +13 -0
- package/dist/rules/quote-expansions.js +229 -0
- package/dist/rules/require-network-wrapper.d.ts +40 -0
- package/dist/rules/require-network-wrapper.js +244 -0
- package/dist/rules/require-shebang.d.ts +29 -0
- package/dist/rules/require-shebang.js +101 -0
- package/dist/rules/require-strict-mode.d.ts +52 -0
- package/dist/rules/require-strict-mode.js +177 -0
- package/dist/rules/require-tty-guard.d.ts +13 -0
- package/dist/rules/require-tty-guard.js +289 -0
- package/dist/rules/resolve-declaration-convention.d.ts +96 -0
- package/dist/rules/resolve-declaration-convention.js +109 -0
- package/dist/rules/resolve-network-wrapper.d.ts +19 -0
- package/dist/rules/resolve-network-wrapper.js +116 -0
- package/dist/rules/set-options.d.ts +109 -0
- package/dist/rules/set-options.js +201 -0
- package/dist/rules/shebang-interpreter.d.ts +18 -0
- package/dist/rules/shebang-interpreter.js +81 -0
- package/dist/rules/shebang-matches-dialect.d.ts +12 -0
- package/dist/rules/shebang-matches-dialect.js +69 -0
- package/dist/rules/transparent-wrappers.d.ts +52 -0
- package/dist/rules/transparent-wrappers.js +99 -0
- package/dist/rules/user-pattern.d.ts +63 -0
- package/dist/rules/user-pattern.js +184 -0
- package/dist/syntax/analyze.d.ts +19 -0
- package/dist/syntax/analyze.js +21 -0
- package/dist/syntax/index.d.ts +23 -0
- package/dist/syntax/index.js +25 -0
- package/dist/version.d.ts +0 -1
- package/dist/version.js +0 -1
- package/docs/compat.md +347 -0
- package/docs/custom-rule-tutorial.md +780 -34
- package/docs/declaration-conventions.md +478 -0
- package/docs/examples/audit-wrapper-pack.mjs +15 -0
- package/docs/examples/dep-line-convention.mjs +22 -0
- package/docs/examples/eslint.config.mjs +3 -2
- package/docs/examples/no-and-or-as-if-rule.mjs +61 -0
- package/docs/examples/no-backticks-rule-tester.mjs +106 -0
- package/docs/examples/no-backticks-rule.mjs +60 -0
- package/docs/examples/no-curl-in-functions-rule.mjs +53 -0
- package/docs/examples/no-function-keyword-rule.mjs +59 -0
- package/docs/examples/no-listed-commands-rule.ts +63 -0
- package/docs/examples/no-rm-rf-rule.mjs +26 -15
- package/docs/examples/no-unquoted-rm-variable-rule.ts +54 -0
- package/docs/examples/sidecar-convention.mjs +38 -0
- package/docs/examples/sidecar-snapshot-convention.mjs +26 -0
- package/docs/examples/sorted-declarations-rule.mjs +52 -0
- package/docs/reading-nodes.md +159 -0
- package/docs/rules/README.md +78 -0
- package/docs/rules/_template.md +83 -0
- package/docs/rules/compat/no-append-all-redirect.md +87 -0
- package/docs/rules/compat/no-associative-arrays.md +95 -0
- package/docs/rules/compat/no-case-attributes.md +82 -0
- package/docs/rules/compat/no-case-fallthrough.md +90 -0
- package/docs/rules/compat/no-case-modification.md +74 -0
- package/docs/rules/compat/no-coproc.md +76 -0
- package/docs/rules/compat/no-declare-g.md +80 -0
- package/docs/rules/compat/no-double-bracket.md +72 -0
- package/docs/rules/compat/no-epoch-variables.md +79 -0
- package/docs/rules/compat/no-fd-variable.md +78 -0
- package/docs/rules/compat/no-globstar.md +74 -0
- package/docs/rules/compat/no-lastpipe-semantics.md +73 -0
- package/docs/rules/compat/no-let.md +78 -0
- package/docs/rules/compat/no-mapfile.md +118 -0
- package/docs/rules/compat/no-nameref.md +81 -0
- package/docs/rules/compat/no-negative-array-index.md +87 -0
- package/docs/rules/compat/no-nounset-empty-array.md +103 -0
- package/docs/rules/compat/no-parameter-transformation.md +94 -0
- package/docs/rules/compat/no-pipe-all.md +85 -0
- package/docs/rules/compat/no-printf-time-format.md +75 -0
- package/docs/rules/compat/no-source.md +98 -0
- package/docs/rules/compat/no-test-v.md +82 -0
- package/docs/rules/compat/no-unicode-escape.md +91 -0
- package/docs/rules/compat/no-wait-n.md +85 -0
- package/docs/rules/max-script-complexity.md +109 -0
- package/docs/rules/no-dynamic-invocation.md +139 -0
- package/docs/rules/no-eval.md +106 -0
- package/docs/rules/no-masked-exit-status.md +239 -0
- package/docs/rules/no-secret-env-reads.md +173 -0
- package/docs/rules/no-secret-flags.md +169 -0
- package/docs/rules/no-unchecked-cd.md +146 -0
- package/docs/rules/no-undeclared-commands.md +141 -0
- package/docs/rules/no-unused-declarations.md +216 -0
- package/docs/rules/prefer-local.md +221 -0
- package/docs/rules/quote-expansions.md +157 -0
- package/docs/rules/require-network-wrapper.md +171 -0
- package/docs/rules/require-shebang.md +157 -0
- package/docs/rules/require-strict-mode.md +253 -0
- package/docs/rules/require-tty-guard.md +128 -0
- package/docs/rules/shebang-matches-dialect.md +120 -0
- package/docs/selectors.md +2 -2
- package/docs/shellcheck.md +86 -0
- package/docs/wrapper-rules.md +206 -0
- package/package.json +26 -20
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/language/selector-classes.d.ts.map +0 -1
- package/dist/language/selector-classes.js.map +0 -1
- package/dist/language/shell-language-options.d.ts.map +0 -1
- package/dist/language/shell-language-options.js.map +0 -1
- package/dist/language/shell-language.d.ts.map +0 -1
- package/dist/language/shell-language.js.map +0 -1
- package/dist/language/shell-source-code.d.ts.map +0 -1
- package/dist/language/shell-source-code.js.map +0 -1
- package/dist/language/source-coordinates.d.ts.map +0 -1
- package/dist/language/source-coordinates.js.map +0 -1
- package/dist/language/syntax-view.d.ts.map +0 -1
- package/dist/language/syntax-view.js.map +0 -1
- package/dist/language/word-resolution.d.ts.map +0 -1
- package/dist/language/word-resolution.js.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { createRule } from './create-rule.js';
|
|
2
|
+
import { shebangShell } from './shebang-interpreter.js';
|
|
3
|
+
/**
|
|
4
|
+
* Interpreter basenames that are POSIX shells for this rule's purpose: they
|
|
5
|
+
* implement the POSIX shell language and none of the bash extensions.
|
|
6
|
+
*/
|
|
7
|
+
const POSIX_SHELLS = new Set(['sh', 'dash', 'ash']);
|
|
8
|
+
/** The UTF-8 byte order mark as it appears in a decoded string. */
|
|
9
|
+
const BOM = '\uFEFF';
|
|
10
|
+
/**
|
|
11
|
+
* Which mismatch, if any, the shebang's shell and the dialect form. `undefined`
|
|
12
|
+
* means the pair is consistent or outside this rule's bash/POSIX contract.
|
|
13
|
+
*/
|
|
14
|
+
function mismatchFor(shell, dialect) {
|
|
15
|
+
if (shell === undefined) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
if (dialect === 'bash' && POSIX_SHELLS.has(shell)) {
|
|
19
|
+
return 'posixShebangBashDialect';
|
|
20
|
+
}
|
|
21
|
+
if (dialect === 'posix' && shell === 'bash') {
|
|
22
|
+
return 'bashShebangPosixDialect';
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The rule module registered as `shebang-matches-dialect`.
|
|
28
|
+
*
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
31
|
+
export const shebangMatchesDialect = createRule({
|
|
32
|
+
name: 'shebang-matches-dialect',
|
|
33
|
+
meta: {
|
|
34
|
+
type: 'problem',
|
|
35
|
+
docs: {
|
|
36
|
+
description: 'Require the shebang to name the same kind of shell as languageOptions.dialect',
|
|
37
|
+
},
|
|
38
|
+
schema: [],
|
|
39
|
+
messages: {
|
|
40
|
+
posixShebangBashDialect: "The shebang '{{shebang}}' names a POSIX shell, but this file is linted as bash, so bash-only advice (such as pipefail) can break it. Set the dialect for these files with `languageOptions: { dialect: 'posix' }` in your eslint.config.js, or change the shebang to a bash one such as '#!/usr/bin/env bash'.",
|
|
41
|
+
bashShebangPosixDialect: "The shebang '{{shebang}}' names bash, but this file is linted as posix, so bash syntax goes unchecked. Set the dialect for these files with `languageOptions: { dialect: 'bash' }` in your eslint.config.js, or change the shebang to a POSIX one such as '#!/bin/sh'.",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
create(context) {
|
|
45
|
+
const { sourceCode } = context;
|
|
46
|
+
return {
|
|
47
|
+
File() {
|
|
48
|
+
const firstLine = sourceCode.lines[0] ?? '';
|
|
49
|
+
const shebangLine = firstLine.startsWith(BOM) ? firstLine.slice(1) : firstLine;
|
|
50
|
+
if (!shebangLine.startsWith('#!')) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const messageId = mismatchFor(shebangShell(shebangLine.slice('#!'.length)), sourceCode.dialect);
|
|
54
|
+
if (messageId === undefined) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
// Line 1 in full, from the line's own length.
|
|
58
|
+
context.report({
|
|
59
|
+
loc: {
|
|
60
|
+
start: { line: 1, column: 1 },
|
|
61
|
+
end: { line: 1, column: firstLine.length + 1 },
|
|
62
|
+
},
|
|
63
|
+
messageId,
|
|
64
|
+
data: { shebang: shebangLine.trimEnd() },
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wrapper data that built-in rules add to `sh-ast/analyze`'s
|
|
3
|
+
* `DEFAULT_TRANSPARENT_WRAPPERS` when they ask
|
|
4
|
+
* `ShellSourceCode.resolveInvokedCommand` which command an invocation runs.
|
|
5
|
+
*
|
|
6
|
+
* Everything here is a `WrapperSpec`: data that tells `resolveArgv0` how to
|
|
7
|
+
* find the word a wrapper runs. No rule logic lives here, and no entry makes
|
|
8
|
+
* a safety judgment. Each rule decides which of these entries it follows.
|
|
9
|
+
*/
|
|
10
|
+
import type { WrapperSpec } from 'sh-ast/analyze';
|
|
11
|
+
/**
|
|
12
|
+
* Bash's `builtin [shell-builtin [args …]]`: runs the named builtin, bypassing
|
|
13
|
+
* any function of the same name. It takes no options of its own, so the word
|
|
14
|
+
* after it is the command it runs.
|
|
15
|
+
*
|
|
16
|
+
* `sh-ast/analyze`'s default wrapper table leaves `builtin` out, so
|
|
17
|
+
* `builtin "$x"` would otherwise resolve to the static command `builtin`.
|
|
18
|
+
*
|
|
19
|
+
* @see https://www.gnu.org/software/bash/manual/bash.html#Bash-Builtins — `builtin`
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
22
|
+
export declare const BUILTIN_WRAPPER: WrapperSpec;
|
|
23
|
+
/**
|
|
24
|
+
* A shell invoked as a command (`bash …`, `sh …`), modeled as a wrapper so the
|
|
25
|
+
* command it runs is found the same way as for `env` or `sudo`:
|
|
26
|
+
*
|
|
27
|
+
* - `bash script.sh` runs `script.sh`; the options before it are skipped.
|
|
28
|
+
* - `bash -c STRING` (and `-c` clustered after one other option letter, such
|
|
29
|
+
* as `bash -lc STRING` or `bash -ec STRING`) runs code embedded in a
|
|
30
|
+
* string. `-c` is an unresolvable flag, so `resolveArgv0` reports the
|
|
31
|
+
* effective command as `{ static: false, reason: 'embedded-command' }`
|
|
32
|
+
* instead of guessing at the string's contents.
|
|
33
|
+
* - `bash -s` (and `bash` with no operand) reads its commands from standard
|
|
34
|
+
* input. `-s` stops the chain: the shell itself is the effective command.
|
|
35
|
+
*
|
|
36
|
+
* `+`-prefixed option letters (`+x`) turn an option off and take no operand.
|
|
37
|
+
* `-o`/`+o` and Bash's `-O`/`+O` take an option name. An option this entry
|
|
38
|
+
* does not know makes `resolveArgv0` report `'unknown-flag'`, never a guess.
|
|
39
|
+
*
|
|
40
|
+
* `resolveArgv0` clusters `noArgFlags` with a following `argFlags` entry, but
|
|
41
|
+
* not with `unresolvableFlags` or `stopsChainFlags`, so the one-letter
|
|
42
|
+
* clusters ending in `c` or `s` are listed as exact tokens. Longer clusters
|
|
43
|
+
* (`-xec`) end in `'unknown-flag'`.
|
|
44
|
+
*
|
|
45
|
+
* Names match exactly, like every `sh-ast/analyze` wrapper: `/bin/bash` is not
|
|
46
|
+
* `bash`.
|
|
47
|
+
*
|
|
48
|
+
* @see https://www.gnu.org/software/bash/manual/bash.html#Invoking-Bash
|
|
49
|
+
* @see https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html — POSIX `sh`
|
|
50
|
+
* @internal
|
|
51
|
+
*/
|
|
52
|
+
export declare const SHELL_WRAPPER: WrapperSpec;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bash's `builtin [shell-builtin [args …]]`: runs the named builtin, bypassing
|
|
3
|
+
* any function of the same name. It takes no options of its own, so the word
|
|
4
|
+
* after it is the command it runs.
|
|
5
|
+
*
|
|
6
|
+
* `sh-ast/analyze`'s default wrapper table leaves `builtin` out, so
|
|
7
|
+
* `builtin "$x"` would otherwise resolve to the static command `builtin`.
|
|
8
|
+
*
|
|
9
|
+
* @see https://www.gnu.org/software/bash/manual/bash.html#Bash-Builtins — `builtin`
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export const BUILTIN_WRAPPER = { names: ['builtin'] };
|
|
13
|
+
/**
|
|
14
|
+
* The single-letter shell options that take no operand when a shell is
|
|
15
|
+
* invoked: every option letter the `set` builtin accepts, plus Bash's
|
|
16
|
+
* invocation-only `-i`, `-l`, `-r` and `-D`. `-c`, `-s` and `-o`/`-O` are
|
|
17
|
+
* absent on purpose: they are modeled separately in {@link SHELL_WRAPPER}.
|
|
18
|
+
*
|
|
19
|
+
* @see https://www.gnu.org/software/bash/manual/bash.html#Invoking-Bash
|
|
20
|
+
* @see https://www.gnu.org/software/bash/manual/bash.html#The-Set-Builtin
|
|
21
|
+
* @see https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html — POSIX `sh` options
|
|
22
|
+
*/
|
|
23
|
+
const SHELL_NO_ARG_LETTERS = [
|
|
24
|
+
'a',
|
|
25
|
+
'b',
|
|
26
|
+
'e',
|
|
27
|
+
'f',
|
|
28
|
+
'h',
|
|
29
|
+
'k',
|
|
30
|
+
'm',
|
|
31
|
+
'n',
|
|
32
|
+
'p',
|
|
33
|
+
't',
|
|
34
|
+
'u',
|
|
35
|
+
'v',
|
|
36
|
+
'x',
|
|
37
|
+
'B',
|
|
38
|
+
'C',
|
|
39
|
+
'E',
|
|
40
|
+
'H',
|
|
41
|
+
'P',
|
|
42
|
+
'T',
|
|
43
|
+
'i',
|
|
44
|
+
'l',
|
|
45
|
+
'r',
|
|
46
|
+
'D',
|
|
47
|
+
];
|
|
48
|
+
/**
|
|
49
|
+
* A shell invoked as a command (`bash …`, `sh …`), modeled as a wrapper so the
|
|
50
|
+
* command it runs is found the same way as for `env` or `sudo`:
|
|
51
|
+
*
|
|
52
|
+
* - `bash script.sh` runs `script.sh`; the options before it are skipped.
|
|
53
|
+
* - `bash -c STRING` (and `-c` clustered after one other option letter, such
|
|
54
|
+
* as `bash -lc STRING` or `bash -ec STRING`) runs code embedded in a
|
|
55
|
+
* string. `-c` is an unresolvable flag, so `resolveArgv0` reports the
|
|
56
|
+
* effective command as `{ static: false, reason: 'embedded-command' }`
|
|
57
|
+
* instead of guessing at the string's contents.
|
|
58
|
+
* - `bash -s` (and `bash` with no operand) reads its commands from standard
|
|
59
|
+
* input. `-s` stops the chain: the shell itself is the effective command.
|
|
60
|
+
*
|
|
61
|
+
* `+`-prefixed option letters (`+x`) turn an option off and take no operand.
|
|
62
|
+
* `-o`/`+o` and Bash's `-O`/`+O` take an option name. An option this entry
|
|
63
|
+
* does not know makes `resolveArgv0` report `'unknown-flag'`, never a guess.
|
|
64
|
+
*
|
|
65
|
+
* `resolveArgv0` clusters `noArgFlags` with a following `argFlags` entry, but
|
|
66
|
+
* not with `unresolvableFlags` or `stopsChainFlags`, so the one-letter
|
|
67
|
+
* clusters ending in `c` or `s` are listed as exact tokens. Longer clusters
|
|
68
|
+
* (`-xec`) end in `'unknown-flag'`.
|
|
69
|
+
*
|
|
70
|
+
* Names match exactly, like every `sh-ast/analyze` wrapper: `/bin/bash` is not
|
|
71
|
+
* `bash`.
|
|
72
|
+
*
|
|
73
|
+
* @see https://www.gnu.org/software/bash/manual/bash.html#Invoking-Bash
|
|
74
|
+
* @see https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html — POSIX `sh`
|
|
75
|
+
* @internal
|
|
76
|
+
*/
|
|
77
|
+
export const SHELL_WRAPPER = {
|
|
78
|
+
names: ['bash', 'sh', 'dash', 'ksh', 'mksh', 'zsh'],
|
|
79
|
+
noArgFlags: [
|
|
80
|
+
...SHELL_NO_ARG_LETTERS.map((letter) => `-${letter}`),
|
|
81
|
+
'--debugger',
|
|
82
|
+
'--dump-po-strings',
|
|
83
|
+
'--dump-strings',
|
|
84
|
+
'--help',
|
|
85
|
+
'--login',
|
|
86
|
+
'--noediting',
|
|
87
|
+
'--noprofile',
|
|
88
|
+
'--norc',
|
|
89
|
+
'--posix',
|
|
90
|
+
'--pretty-print',
|
|
91
|
+
'--restricted',
|
|
92
|
+
'--verbose',
|
|
93
|
+
'--version',
|
|
94
|
+
],
|
|
95
|
+
noArgFlagPattern: /^\+[abefhkmnptuvxBCEHPT]+$/,
|
|
96
|
+
argFlags: ['-o', '+o', '-O', '+O', '--init-file', '--rcfile'],
|
|
97
|
+
unresolvableFlags: ['-c', ...SHELL_NO_ARG_LETTERS.map((letter) => `-${letter}c`)],
|
|
98
|
+
stopsChainFlags: ['-s', ...SHELL_NO_ARG_LETTERS.map((letter) => `-${letter}s`)],
|
|
99
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compiling regular-expression sources that a rule takes from its options and
|
|
3
|
+
* tests against text from the linted script.
|
|
4
|
+
*
|
|
5
|
+
* The configuration is trusted, but the subject is not: when the plugin lints
|
|
6
|
+
* a repository it does not control, the script's author picks the names a
|
|
7
|
+
* pattern runs against. V8's backtracking engine takes time exponential in the
|
|
8
|
+
* subject's length for a pattern that repeats a group which itself repeats,
|
|
9
|
+
* such as `^([A-Z]+_?)*_KEY$` against `AAAA…Ax`. {@link compileUserPattern}
|
|
10
|
+
* rejects the common form of such a pattern as a configuration error, so the
|
|
11
|
+
* accident of writing one is caught. It is not a proof that an accepted
|
|
12
|
+
* pattern is safe.
|
|
13
|
+
*
|
|
14
|
+
* The check is a static star-height test: a quantifier that repeats (`*`, `+`,
|
|
15
|
+
* `{n,}` or `{n,m}` with `m > 1`) applied to a group that already contains a
|
|
16
|
+
* repeating quantifier is rejected. It is conservative. A nested repetition
|
|
17
|
+
* that happens to be linear, such as `(a*b)*`, is rejected too; a rewrite
|
|
18
|
+
* without the nesting exists only for some such patterns. It does not detect every
|
|
19
|
+
* super-linear pattern: an alternation whose branches overlap under a
|
|
20
|
+
* repetition (`^([A-Z]|[A-Z0-9])+_KEY$`, `(a|a)+$`) passes, and so do
|
|
21
|
+
* adjacent quantifiers that can match the same text (`.*.*.*x`), whose cost is
|
|
22
|
+
* polynomial in the name's length. The rule docs list these limits.
|
|
23
|
+
*
|
|
24
|
+
* @see https://github.com/mike-north/eslint-sh/issues/221
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Where in a source a rejected nested repetition was found, for the error message.
|
|
28
|
+
*
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
31
|
+
export interface NestedRepetition {
|
|
32
|
+
/** The outer repeating quantifier as written, such as `*` or `{2,}`. */
|
|
33
|
+
readonly outer: string;
|
|
34
|
+
/** The text of the group the outer quantifier repeats, such as `([A-Z]+_?)`. */
|
|
35
|
+
readonly group: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Finds a repeating quantifier applied to a group that already contains one,
|
|
39
|
+
* the shape that makes a backtracking engine exponential. Returns the first
|
|
40
|
+
* such nesting, or `undefined` when the source has star height of at most one.
|
|
41
|
+
*
|
|
42
|
+
* The source is assumed to be a valid regular expression; the caller compiles
|
|
43
|
+
* it first.
|
|
44
|
+
*
|
|
45
|
+
* @internal
|
|
46
|
+
*/
|
|
47
|
+
export declare function findNestedRepetition(source: string): NestedRepetition | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Compiles a regular-expression source from a rule option that will be tested
|
|
50
|
+
* against script-controlled text. Throws a `TypeError` naming the rule and the
|
|
51
|
+
* option when the source is not a valid expression, or when it nests one
|
|
52
|
+
* repetition inside another (see {@link findNestedRepetition}). ESLint reports
|
|
53
|
+
* the error from the rule's `create()`, so it surfaces on the first file linted
|
|
54
|
+
* with the rule ("Occurred while linting <file>"), not when the config loads.
|
|
55
|
+
*
|
|
56
|
+
* @param rule - The rule's full name, such as `sh/prefer-local`.
|
|
57
|
+
* @param option - The option as the user wrote it, such as `patterns[0]`.
|
|
58
|
+
* @param source - The regular-expression source.
|
|
59
|
+
* @param flags - The flags to compile with.
|
|
60
|
+
*
|
|
61
|
+
* @internal
|
|
62
|
+
*/
|
|
63
|
+
export declare function compileUserPattern(rule: string, option: string, source: string, flags: string): RegExp;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compiling regular-expression sources that a rule takes from its options and
|
|
3
|
+
* tests against text from the linted script.
|
|
4
|
+
*
|
|
5
|
+
* The configuration is trusted, but the subject is not: when the plugin lints
|
|
6
|
+
* a repository it does not control, the script's author picks the names a
|
|
7
|
+
* pattern runs against. V8's backtracking engine takes time exponential in the
|
|
8
|
+
* subject's length for a pattern that repeats a group which itself repeats,
|
|
9
|
+
* such as `^([A-Z]+_?)*_KEY$` against `AAAA…Ax`. {@link compileUserPattern}
|
|
10
|
+
* rejects the common form of such a pattern as a configuration error, so the
|
|
11
|
+
* accident of writing one is caught. It is not a proof that an accepted
|
|
12
|
+
* pattern is safe.
|
|
13
|
+
*
|
|
14
|
+
* The check is a static star-height test: a quantifier that repeats (`*`, `+`,
|
|
15
|
+
* `{n,}` or `{n,m}` with `m > 1`) applied to a group that already contains a
|
|
16
|
+
* repeating quantifier is rejected. It is conservative. A nested repetition
|
|
17
|
+
* that happens to be linear, such as `(a*b)*`, is rejected too; a rewrite
|
|
18
|
+
* without the nesting exists only for some such patterns. It does not detect every
|
|
19
|
+
* super-linear pattern: an alternation whose branches overlap under a
|
|
20
|
+
* repetition (`^([A-Z]|[A-Z0-9])+_KEY$`, `(a|a)+$`) passes, and so do
|
|
21
|
+
* adjacent quantifiers that can match the same text (`.*.*.*x`), whose cost is
|
|
22
|
+
* polynomial in the name's length. The rule docs list these limits.
|
|
23
|
+
*
|
|
24
|
+
* @see https://github.com/mike-north/eslint-sh/issues/221
|
|
25
|
+
*/
|
|
26
|
+
/** Matches a `{n}`, `{n,}` or `{n,m}` quantifier at the start of the string. */
|
|
27
|
+
const BRACE_QUANTIFIER = /^\{\d+(?:,\d*)?\}/u;
|
|
28
|
+
/**
|
|
29
|
+
* Reads the quantifier starting at `source[i]`, if there is one. Returns its
|
|
30
|
+
* length in characters, including a trailing lazy `?`, and whether it can
|
|
31
|
+
* match its atom more than once.
|
|
32
|
+
*/
|
|
33
|
+
function readQuantifier(source, i) {
|
|
34
|
+
const char = source[i];
|
|
35
|
+
let length;
|
|
36
|
+
let repeats;
|
|
37
|
+
if (char === '*' || char === '+') {
|
|
38
|
+
length = 1;
|
|
39
|
+
repeats = true;
|
|
40
|
+
}
|
|
41
|
+
else if (char === '?') {
|
|
42
|
+
length = 1;
|
|
43
|
+
repeats = false;
|
|
44
|
+
}
|
|
45
|
+
else if (char === '{') {
|
|
46
|
+
const match = BRACE_QUANTIFIER.exec(source.slice(i));
|
|
47
|
+
// Without the `u` flag a `{` that does not form a quantifier is a literal.
|
|
48
|
+
if (match === null)
|
|
49
|
+
return undefined;
|
|
50
|
+
length = match[0].length;
|
|
51
|
+
// `{n}` repeats at most n times, `{n,}` without bound, `{n,m}` at most m times.
|
|
52
|
+
const bounds = match[0].slice(1, -1);
|
|
53
|
+
const comma = bounds.indexOf(',');
|
|
54
|
+
const upper = comma === -1
|
|
55
|
+
? Number(bounds)
|
|
56
|
+
: comma === bounds.length - 1
|
|
57
|
+
? Infinity
|
|
58
|
+
: Number(bounds.slice(comma + 1));
|
|
59
|
+
repeats = upper > 1;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
// A lazy quantifier (`*?`, `{2,}?`) backtracks the same way.
|
|
65
|
+
if (source[i + length] === '?')
|
|
66
|
+
length++;
|
|
67
|
+
return { length, repeats };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Returns the index just past the escape sequence starting at `source[i]`
|
|
71
|
+
* (which is `\`). Braced and angled forms (`\u{…}`, `\p{…}`, `\k<…>`) are
|
|
72
|
+
* consumed whole, so their braces are not read as a quantifier.
|
|
73
|
+
*/
|
|
74
|
+
function skipEscape(source, i) {
|
|
75
|
+
const next = source[i + 1];
|
|
76
|
+
const close = next === 'k' ? '>' : '}';
|
|
77
|
+
const open = next === 'k' ? '<' : '{';
|
|
78
|
+
if ((next === 'u' || next === 'p' || next === 'P' || next === 'k') && source[i + 2] === open) {
|
|
79
|
+
const end = source.indexOf(close, i + 3);
|
|
80
|
+
if (end !== -1)
|
|
81
|
+
return end + 1;
|
|
82
|
+
}
|
|
83
|
+
return i + 2;
|
|
84
|
+
}
|
|
85
|
+
/** Returns the index just past the character class starting at `source[i]` (which is `[`). */
|
|
86
|
+
function skipClass(source, i) {
|
|
87
|
+
let j = i + 1;
|
|
88
|
+
if (source[j] === '^')
|
|
89
|
+
j++;
|
|
90
|
+
while (j < source.length && source[j] !== ']') {
|
|
91
|
+
j = source[j] === '\\' ? j + 2 : j + 1;
|
|
92
|
+
}
|
|
93
|
+
return j + 1;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Finds a repeating quantifier applied to a group that already contains one,
|
|
97
|
+
* the shape that makes a backtracking engine exponential. Returns the first
|
|
98
|
+
* such nesting, or `undefined` when the source has star height of at most one.
|
|
99
|
+
*
|
|
100
|
+
* The source is assumed to be a valid regular expression; the caller compiles
|
|
101
|
+
* it first.
|
|
102
|
+
*
|
|
103
|
+
* @internal
|
|
104
|
+
*/
|
|
105
|
+
export function findNestedRepetition(source) {
|
|
106
|
+
const stack = [{ start: 0, containsRepetition: false }];
|
|
107
|
+
let i = 0;
|
|
108
|
+
while (i < source.length) {
|
|
109
|
+
const char = source[i];
|
|
110
|
+
// The atom just read: where it starts, where it ends, and whether it
|
|
111
|
+
// already contains a repetition (only a group can).
|
|
112
|
+
let atomStart = i;
|
|
113
|
+
let atomEnd;
|
|
114
|
+
let atomRepeats = false;
|
|
115
|
+
if (char === '\\') {
|
|
116
|
+
atomEnd = skipEscape(source, i);
|
|
117
|
+
}
|
|
118
|
+
else if (char === '[') {
|
|
119
|
+
atomEnd = skipClass(source, i);
|
|
120
|
+
}
|
|
121
|
+
else if (char === '(') {
|
|
122
|
+
stack.push({ start: i, containsRepetition: false });
|
|
123
|
+
i++;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
else if (char === ')') {
|
|
127
|
+
const frame = stack.pop();
|
|
128
|
+
// An unbalanced `)` is a syntax error the caller already rejected.
|
|
129
|
+
if (frame === undefined || stack.length === 0)
|
|
130
|
+
return undefined;
|
|
131
|
+
atomStart = frame.start;
|
|
132
|
+
atomEnd = i + 1;
|
|
133
|
+
atomRepeats = frame.containsRepetition;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
atomEnd = i + 1;
|
|
137
|
+
}
|
|
138
|
+
const quantifier = readQuantifier(source, atomEnd);
|
|
139
|
+
if (quantifier?.repeats === true && atomRepeats) {
|
|
140
|
+
return {
|
|
141
|
+
outer: source.slice(atomEnd, atomEnd + quantifier.length),
|
|
142
|
+
group: source.slice(atomStart, atomEnd),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const top = stack.at(-1);
|
|
146
|
+
if (top !== undefined && (atomRepeats || quantifier?.repeats === true)) {
|
|
147
|
+
top.containsRepetition = true;
|
|
148
|
+
}
|
|
149
|
+
i = atomEnd + (quantifier?.length ?? 0);
|
|
150
|
+
}
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Compiles a regular-expression source from a rule option that will be tested
|
|
155
|
+
* against script-controlled text. Throws a `TypeError` naming the rule and the
|
|
156
|
+
* option when the source is not a valid expression, or when it nests one
|
|
157
|
+
* repetition inside another (see {@link findNestedRepetition}). ESLint reports
|
|
158
|
+
* the error from the rule's `create()`, so it surfaces on the first file linted
|
|
159
|
+
* with the rule ("Occurred while linting <file>"), not when the config loads.
|
|
160
|
+
*
|
|
161
|
+
* @param rule - The rule's full name, such as `sh/prefer-local`.
|
|
162
|
+
* @param option - The option as the user wrote it, such as `patterns[0]`.
|
|
163
|
+
* @param source - The regular-expression source.
|
|
164
|
+
* @param flags - The flags to compile with.
|
|
165
|
+
*
|
|
166
|
+
* @internal
|
|
167
|
+
*/
|
|
168
|
+
export function compileUserPattern(rule, option, source, flags) {
|
|
169
|
+
let pattern;
|
|
170
|
+
try {
|
|
171
|
+
pattern = new RegExp(source, flags);
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
throw new TypeError(`${rule}: \`${option}\` must be a valid regular expression; got ${JSON.stringify(source)}.`);
|
|
175
|
+
}
|
|
176
|
+
const nested = findNestedRepetition(source);
|
|
177
|
+
if (nested !== undefined) {
|
|
178
|
+
throw new TypeError(`${rule}: \`${option}\` (${JSON.stringify(source)}) repeats ${nested.group} with \`${nested.outer}\`, ` +
|
|
179
|
+
'and that group already repeats. The pattern runs against names the linted script chooses, ' +
|
|
180
|
+
'and a nested repetition can take exponential time on a long name. Rewrite it with one level ' +
|
|
181
|
+
'of repetition, for example `[A-Z0-9_]*` instead of `([A-Z]+_?)*`.');
|
|
182
|
+
}
|
|
183
|
+
return pattern;
|
|
184
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The faithful shell analysis surface (`@eslint-sh/eslint-plugin/syntax/analyze`): the complete
|
|
3
|
+
* public surface of `sh-ast/analyze`, re-exported unchanged from the very `sh-ast` instance this
|
|
4
|
+
* plugin's language parses with.
|
|
5
|
+
*
|
|
6
|
+
* It provides `enumerateCommands`, `resolveArgv0` (whose `chainIndices` locate a resolved command's
|
|
7
|
+
* arguments in its site's `argv`), `DEFAULT_TRANSPARENT_WRAPPERS`, `WrapperSpec`, the raw
|
|
8
|
+
* `resolveWord(Node)`, and the named operator constants with `rawOperator` for comparing and
|
|
9
|
+
* constructing the operator values those records carry. All of it works on the faithful records
|
|
10
|
+
* from `@eslint-sh/eslint-plugin/syntax`. The plugin root's `resolveWord` takes a lint-view
|
|
11
|
+
* `ShellNode` instead; the two share a name but not an input contract, so this subpath is kept
|
|
12
|
+
* separate from the root.
|
|
13
|
+
*
|
|
14
|
+
* Like `@eslint-sh/eslint-plugin/syntax`, it tracks the plugin's pinned `sh-ast` minor and never
|
|
15
|
+
* wraps or renames anything.
|
|
16
|
+
*
|
|
17
|
+
* @packageDocumentation
|
|
18
|
+
*/
|
|
19
|
+
export * from 'sh-ast/analyze';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/* eslint-disable @api-extractor-tools/package-documentation -- this file is the barrel of the
|
|
2
|
+
`./syntax/analyze` entry point (package.json `exports`); the rule only recognizes the root barrel. */
|
|
3
|
+
/**
|
|
4
|
+
* The faithful shell analysis surface (`@eslint-sh/eslint-plugin/syntax/analyze`): the complete
|
|
5
|
+
* public surface of `sh-ast/analyze`, re-exported unchanged from the very `sh-ast` instance this
|
|
6
|
+
* plugin's language parses with.
|
|
7
|
+
*
|
|
8
|
+
* It provides `enumerateCommands`, `resolveArgv0` (whose `chainIndices` locate a resolved command's
|
|
9
|
+
* arguments in its site's `argv`), `DEFAULT_TRANSPARENT_WRAPPERS`, `WrapperSpec`, the raw
|
|
10
|
+
* `resolveWord(Node)`, and the named operator constants with `rawOperator` for comparing and
|
|
11
|
+
* constructing the operator values those records carry. All of it works on the faithful records
|
|
12
|
+
* from `@eslint-sh/eslint-plugin/syntax`. The plugin root's `resolveWord` takes a lint-view
|
|
13
|
+
* `ShellNode` instead; the two share a name but not an input contract, so this subpath is kept
|
|
14
|
+
* separate from the root.
|
|
15
|
+
*
|
|
16
|
+
* Like `@eslint-sh/eslint-plugin/syntax`, it tracks the plugin's pinned `sh-ast` minor and never
|
|
17
|
+
* wraps or renames anything.
|
|
18
|
+
*
|
|
19
|
+
* @packageDocumentation
|
|
20
|
+
*/
|
|
21
|
+
export * from 'sh-ast/analyze';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The faithful shell syntax surface (`@eslint-sh/eslint-plugin/syntax`): the complete public
|
|
3
|
+
* surface of `sh-ast`'s root entry point, re-exported unchanged from the very `sh-ast` instance
|
|
4
|
+
* this plugin's language parses with.
|
|
5
|
+
*
|
|
6
|
+
* Use it when a custom rule edits or analyzes the parser's own records rather than the plugin's
|
|
7
|
+
* lint view: `walk`, `parseSync`, `printSync` and `PrintOptions`, the record types (`Node`,
|
|
8
|
+
* `Word`, `CallExpr`, `SyntaxPos`, `GoString`, …), and the operator and language constants.
|
|
9
|
+
* Importing them from here, instead of installing `sh-ast` separately, means a rule always gets
|
|
10
|
+
* the same `sh-ast` version and module instance as the language that produced its syntax. Under
|
|
11
|
+
* 0.x a different `sh-ast` minor is a breaking change, and records from one copy are not
|
|
12
|
+
* guaranteed to be accepted by another.
|
|
13
|
+
*
|
|
14
|
+
* This entry point tracks the plugin's pinned `sh-ast` minor, so an `sh-ast` 0.x minor bump is a
|
|
15
|
+
* minor release of this plugin. It never wraps or renames anything: every name has `sh-ast`'s
|
|
16
|
+
* own contract. The plugin root (`@eslint-sh/eslint-plugin`) keeps the view-aware helpers such
|
|
17
|
+
* as `resolveWord(ShellNode)`, which differ from the raw helpers of the same name in
|
|
18
|
+
* `@eslint-sh/eslint-plugin/syntax/analyze`. That is why these names live on their own subpath
|
|
19
|
+
* and are not merged into the root.
|
|
20
|
+
*
|
|
21
|
+
* @packageDocumentation
|
|
22
|
+
*/
|
|
23
|
+
export * from 'sh-ast';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* eslint-disable @api-extractor-tools/package-documentation -- this file is the barrel of the
|
|
2
|
+
`./syntax` entry point (package.json `exports`); the rule only recognizes the root barrel. */
|
|
3
|
+
/**
|
|
4
|
+
* The faithful shell syntax surface (`@eslint-sh/eslint-plugin/syntax`): the complete public
|
|
5
|
+
* surface of `sh-ast`'s root entry point, re-exported unchanged from the very `sh-ast` instance
|
|
6
|
+
* this plugin's language parses with.
|
|
7
|
+
*
|
|
8
|
+
* Use it when a custom rule edits or analyzes the parser's own records rather than the plugin's
|
|
9
|
+
* lint view: `walk`, `parseSync`, `printSync` and `PrintOptions`, the record types (`Node`,
|
|
10
|
+
* `Word`, `CallExpr`, `SyntaxPos`, `GoString`, …), and the operator and language constants.
|
|
11
|
+
* Importing them from here, instead of installing `sh-ast` separately, means a rule always gets
|
|
12
|
+
* the same `sh-ast` version and module instance as the language that produced its syntax. Under
|
|
13
|
+
* 0.x a different `sh-ast` minor is a breaking change, and records from one copy are not
|
|
14
|
+
* guaranteed to be accepted by another.
|
|
15
|
+
*
|
|
16
|
+
* This entry point tracks the plugin's pinned `sh-ast` minor, so an `sh-ast` 0.x minor bump is a
|
|
17
|
+
* minor release of this plugin. It never wraps or renames anything: every name has `sh-ast`'s
|
|
18
|
+
* own contract. The plugin root (`@eslint-sh/eslint-plugin`) keeps the view-aware helpers such
|
|
19
|
+
* as `resolveWord(ShellNode)`, which differ from the raw helpers of the same name in
|
|
20
|
+
* `@eslint-sh/eslint-plugin/syntax/analyze`. That is why these names live on their own subpath
|
|
21
|
+
* and are not merged into the root.
|
|
22
|
+
*
|
|
23
|
+
* @packageDocumentation
|
|
24
|
+
*/
|
|
25
|
+
export * from 'sh-ast';
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED