@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.
- package/README.md +14 -8
- package/dist/command-harvest/builtins/bash.d.ts +1 -1
- package/dist/command-harvest/builtins/bash.js +1 -1
- package/dist/command-harvest/builtins/bats.d.ts +2 -0
- package/dist/command-harvest/builtins/bats.js +3 -0
- package/dist/command-harvest/wrapper-table.d.ts +3 -19
- package/dist/command-harvest/wrapper-table.js +8 -38
- package/dist/configs/bash-3-2.d.ts +7 -6
- package/dist/configs/bash-3-2.js +4 -3
- package/dist/configs/posix-portable.d.ts +1 -1
- package/dist/declaration-conventions/read-declared-commands.d.ts +2 -2
- package/dist/declaration-conventions/read-declared-commands.js +2 -2
- package/dist/eslint-plugin-syntax.d.ts +4 -0
- package/dist/eslint-plugin.d.ts +86 -40
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -2
- package/dist/language/shell-language-options.d.ts +18 -15
- package/dist/language/shell-language-options.js +33 -26
- package/dist/language/shell-language.d.ts +10 -9
- package/dist/language/shell-language.js +18 -35
- package/dist/language/shell-source-code.d.ts +17 -9
- package/dist/language/shell-source-code.js +53 -12
- package/dist/rules/bash-variable-semantics.d.ts +22 -0
- package/dist/rules/bash-variable-semantics.js +41 -0
- package/dist/rules/compat/create-compat-rule.js +1 -1
- package/dist/rules/compat/declaration-options.d.ts +2 -2
- package/dist/rules/compat/declaration-options.js +5 -2
- package/dist/rules/compat/no-append-all-redirect.js +1 -1
- package/dist/rules/compat/no-associative-arrays.js +2 -2
- package/dist/rules/compat/no-brace-sequence-padding-step.js +4 -4
- package/dist/rules/compat/no-case-attributes.js +3 -3
- package/dist/rules/compat/no-case-fallthrough.js +2 -2
- package/dist/rules/compat/no-case-modification.js +2 -2
- package/dist/rules/compat/no-coproc.js +1 -1
- package/dist/rules/compat/no-declare-g.js +3 -3
- package/dist/rules/compat/no-double-bracket.js +2 -2
- package/dist/rules/compat/no-epoch-variables.js +2 -2
- package/dist/rules/compat/no-fd-variable.js +1 -1
- package/dist/rules/compat/no-globstar.js +1 -1
- package/dist/rules/compat/no-lastpipe-semantics.js +2 -2
- package/dist/rules/compat/no-let.js +2 -1
- package/dist/rules/compat/no-mapfile.js +4 -2
- package/dist/rules/compat/no-nameref.js +3 -3
- package/dist/rules/compat/no-negative-array-index.js +39 -9
- package/dist/rules/compat/no-nounset-empty-array.js +15 -4
- package/dist/rules/compat/no-parameter-transformation.js +2 -2
- package/dist/rules/compat/no-pipe-all.js +1 -1
- package/dist/rules/compat/no-printf-time-format.js +4 -2
- package/dist/rules/compat/no-source.js +2 -1
- package/dist/rules/compat/no-test-v.js +4 -2
- package/dist/rules/compat/no-unicode-escape.js +2 -2
- package/dist/rules/compat/no-wait-n.js +4 -2
- package/dist/rules/compat/nounset-array-proof.d.ts +8 -0
- package/dist/rules/compat/nounset-array-proof.js +563 -0
- package/dist/rules/compat/posix-absent-command.d.ts +7 -8
- package/dist/rules/compat/posix-absent-command.js +17 -15
- package/dist/rules/compat/shopt-options.js +3 -1
- package/dist/rules/compat/version-gates.d.ts +9 -9
- package/dist/rules/compat/version-gates.js +13 -13
- package/dist/rules/create-wrapper-rule.js +12 -7
- package/dist/rules/no-dynamic-invocation.d.ts +0 -8
- package/dist/rules/no-dynamic-invocation.js +7 -15
- package/dist/rules/no-eval.js +3 -1
- package/dist/rules/no-masked-exit-status.js +15 -8
- package/dist/rules/no-secret-env-reads.d.ts +4 -3
- package/dist/rules/no-secret-env-reads.js +37 -14
- package/dist/rules/no-secret-flags.d.ts +2 -1
- package/dist/rules/no-secret-flags.js +7 -8
- package/dist/rules/no-unchecked-cd.js +3 -1
- package/dist/rules/no-undeclared-commands.d.ts +1 -15
- package/dist/rules/no-undeclared-commands.js +3 -15
- package/dist/rules/no-unused-declarations.d.ts +1 -18
- package/dist/rules/no-unused-declarations.js +9 -34
- package/dist/rules/prefer-local.js +4 -3
- package/dist/rules/quote-expansions.js +3 -28
- package/dist/rules/require-network-wrapper.d.ts +2 -17
- package/dist/rules/require-network-wrapper.js +33 -4
- package/dist/rules/resolve-declaration-convention.d.ts +5 -6
- package/dist/rules/resolve-declaration-convention.js +5 -6
- package/dist/rules/resolve-network-wrapper.d.ts +19 -0
- package/dist/rules/resolve-network-wrapper.js +57 -26
- package/dist/rules/resolve-transparent-wrappers.d.ts +43 -0
- package/dist/rules/resolve-transparent-wrappers.js +98 -0
- package/dist/rules/set-options.d.ts +2 -1
- package/dist/rules/set-options.js +17 -5
- package/dist/rules/shebang-interpreter.js +4 -2
- package/dist/rules/transparent-wrappers.d.ts +4 -3
- package/dist/rules/transparent-wrappers.js +5 -4
- package/dist/rules/user-pattern.d.ts +5 -3
- package/dist/rules/user-pattern.js +6 -4
- package/dist/syntax/index.d.ts +4 -0
- package/dist/syntax/index.js +4 -0
- package/docs/compat.md +76 -27
- package/docs/custom-rule-tutorial.md +9 -3
- package/docs/examples/no-listed-commands-rule.ts +2 -2
- package/docs/rules/README.md +36 -4
- package/docs/rules/_template.md +2 -2
- package/docs/rules/compat/no-append-all-redirect.md +12 -12
- package/docs/rules/compat/no-associative-arrays.md +14 -14
- package/docs/rules/compat/no-brace-sequence-padding-step.md +12 -12
- package/docs/rules/compat/no-case-attributes.md +13 -13
- package/docs/rules/compat/no-case-fallthrough.md +12 -12
- package/docs/rules/compat/no-case-modification.md +11 -11
- package/docs/rules/compat/no-coproc.md +13 -13
- package/docs/rules/compat/no-declare-g.md +13 -13
- package/docs/rules/compat/no-double-bracket.md +2 -2
- package/docs/rules/compat/no-epoch-variables.md +13 -13
- package/docs/rules/compat/no-fd-variable.md +12 -12
- package/docs/rules/compat/no-globstar.md +13 -13
- package/docs/rules/compat/no-lastpipe-semantics.md +13 -13
- package/docs/rules/compat/no-let.md +2 -2
- package/docs/rules/compat/no-mapfile.md +16 -16
- package/docs/rules/compat/no-nameref.md +13 -13
- package/docs/rules/compat/no-negative-array-index.md +14 -14
- package/docs/rules/compat/no-nounset-empty-array.md +72 -17
- package/docs/rules/compat/no-parameter-transformation.md +19 -19
- package/docs/rules/compat/no-pipe-all.md +13 -13
- package/docs/rules/compat/no-printf-time-format.md +13 -13
- package/docs/rules/compat/no-source.md +2 -2
- package/docs/rules/compat/no-test-v.md +12 -12
- package/docs/rules/compat/no-unicode-escape.md +13 -13
- package/docs/rules/compat/no-wait-n.md +13 -13
- package/docs/rules/no-dynamic-invocation.md +17 -12
- package/docs/rules/no-eval.md +6 -1
- package/docs/rules/no-masked-exit-status.md +4 -0
- package/docs/rules/no-secret-env-reads.md +34 -3
- package/docs/rules/no-secret-flags.md +19 -2
- package/docs/rules/no-unchecked-cd.md +9 -2
- package/docs/rules/no-undeclared-commands.md +16 -15
- package/docs/rules/no-unused-declarations.md +17 -20
- package/docs/rules/prefer-local.md +20 -2
- package/docs/rules/quote-expansions.md +8 -5
- package/docs/rules/require-network-wrapper.md +22 -17
- package/docs/rules/require-shebang.md +10 -4
- package/docs/rules/require-strict-mode.md +6 -0
- package/docs/shellcheck.md +9 -5
- package/docs/transparent-wrappers.md +97 -0
- package/docs/wrapper-rules.md +9 -0
- package/package.json +2 -2
- package/dist/language/end-of-input-marker.d.ts +0 -29
- package/dist/language/end-of-input-marker.js +0 -88
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eslint-sh/eslint-plugin",
|
|
3
3
|
"description": "Shell script linting for ESLint, with built-in policy rules, shared presets, and custom rules over a typed shell AST",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.8.0",
|
|
5
5
|
"author": "Mike North <michael.l.north@gmail.com>",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@eslint/core": "^1.2.1",
|
|
8
8
|
"@eslint/plugin-kit": "^0.7.2",
|
|
9
|
-
"sh-ast": "^0.
|
|
9
|
+
"sh-ast": "^0.9.0"
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|
|
12
12
|
"eslint": "^10.7.0"
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Detects a source whose U+0080 made the parser silently drop text.
|
|
3
|
-
*
|
|
4
|
-
* mvdan/sh's reader uses U+0080 (`utf8.RuneSelf`) as its internal end-of-input marker. In some
|
|
5
|
-
* positions the parser reads it as an ordinary character and returns the whole tree; in others
|
|
6
|
-
* it stops there and returns a tree for only the text before it, with no error. A linter that
|
|
7
|
-
* accepted the second kind would skip every rule on the lost text, so those sources must be
|
|
8
|
-
* refused while the first kind keeps linting.
|
|
9
|
-
*
|
|
10
|
-
* The two are told apart by asking the parser what it does with the same source when U+0080 is
|
|
11
|
-
* an ordinary character: the parse is complete exactly when the printed tree is the same as the
|
|
12
|
-
* printed tree of the source with every U+0080 swapped for `X`. Text the parser dropped is
|
|
13
|
-
* missing from the first print and present in the second. Nothing is inferred from tree
|
|
14
|
-
* extents, so a trailing comment or heredoc body cannot make a truncated file look complete.
|
|
15
|
-
*/
|
|
16
|
-
import { type ParseOptions } from 'sh-ast';
|
|
17
|
-
/**
|
|
18
|
-
* The UTF-16 index of the first U+0080 whose presence makes the parser drop text, or
|
|
19
|
-
* `undefined` when `text` holds none or the parser handles every one of them completely.
|
|
20
|
-
*
|
|
21
|
-
* A source the parser rejects with an error is also `undefined`: that is a fatal parse error the
|
|
22
|
-
* caller reports through its normal path, not a silent loss. Every other doubt is a refusal:
|
|
23
|
-
* a source that parses but prints differently from its swapped counterpart is refused, and so is
|
|
24
|
-
* one whose swapped counterpart no longer parses, so an undecidable position is never accepted.
|
|
25
|
-
*
|
|
26
|
-
* @param text - The source about to be parsed.
|
|
27
|
-
* @param options - The parse options the caller will use, so the dialect matches.
|
|
28
|
-
*/
|
|
29
|
-
export declare function firstDroppedTextIndex(text: string, options: ParseOptions): number | undefined;
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Detects a source whose U+0080 made the parser silently drop text.
|
|
3
|
-
*
|
|
4
|
-
* mvdan/sh's reader uses U+0080 (`utf8.RuneSelf`) as its internal end-of-input marker. In some
|
|
5
|
-
* positions the parser reads it as an ordinary character and returns the whole tree; in others
|
|
6
|
-
* it stops there and returns a tree for only the text before it, with no error. A linter that
|
|
7
|
-
* accepted the second kind would skip every rule on the lost text, so those sources must be
|
|
8
|
-
* refused while the first kind keeps linting.
|
|
9
|
-
*
|
|
10
|
-
* The two are told apart by asking the parser what it does with the same source when U+0080 is
|
|
11
|
-
* an ordinary character: the parse is complete exactly when the printed tree is the same as the
|
|
12
|
-
* printed tree of the source with every U+0080 swapped for `X`. Text the parser dropped is
|
|
13
|
-
* missing from the first print and present in the second. Nothing is inferred from tree
|
|
14
|
-
* extents, so a trailing comment or heredoc body cannot make a truncated file look complete.
|
|
15
|
-
*/
|
|
16
|
-
import { parseSync, printSync } from 'sh-ast';
|
|
17
|
-
const END_OF_INPUT_MARKER = '\u0080';
|
|
18
|
-
/** An ordinary one-UTF-16-unit word character, so offsets are the same in both parses. */
|
|
19
|
-
const ORDINARY_CHARACTER = 'X';
|
|
20
|
-
/** What the parser makes of a source: the printed tree, or `ERROR` when it does not parse. */
|
|
21
|
-
const ERROR = Symbol('parse error');
|
|
22
|
-
/**
|
|
23
|
-
* Prints `text` the way the parser sees it. Every U+0080 in the result is swapped for the
|
|
24
|
-
* ordinary character so the print of the original can be compared with the print of the
|
|
25
|
-
* swapped source.
|
|
26
|
-
*/
|
|
27
|
-
function printed(text, options) {
|
|
28
|
-
try {
|
|
29
|
-
const output = printSync(parseSync(text, options));
|
|
30
|
-
const decoded = typeof output === 'string' ? output : new TextDecoder().decode(output);
|
|
31
|
-
return decoded.replaceAll(END_OF_INPUT_MARKER, ORDINARY_CHARACTER);
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
return ERROR;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* The UTF-16 index of the first U+0080 whose presence makes the parser drop text, or
|
|
39
|
-
* `undefined` when `text` holds none or the parser handles every one of them completely.
|
|
40
|
-
*
|
|
41
|
-
* A source the parser rejects with an error is also `undefined`: that is a fatal parse error the
|
|
42
|
-
* caller reports through its normal path, not a silent loss. Every other doubt is a refusal:
|
|
43
|
-
* a source that parses but prints differently from its swapped counterpart is refused, and so is
|
|
44
|
-
* one whose swapped counterpart no longer parses, so an undecidable position is never accepted.
|
|
45
|
-
*
|
|
46
|
-
* @param text - The source about to be parsed.
|
|
47
|
-
* @param options - The parse options the caller will use, so the dialect matches.
|
|
48
|
-
*/
|
|
49
|
-
export function firstDroppedTextIndex(text, options) {
|
|
50
|
-
const indices = [];
|
|
51
|
-
for (let at = text.indexOf(END_OF_INPUT_MARKER); at !== -1; at = text.indexOf(END_OF_INPUT_MARKER, at + 1)) {
|
|
52
|
-
indices.push(at);
|
|
53
|
-
}
|
|
54
|
-
if (indices.length === 0)
|
|
55
|
-
return undefined;
|
|
56
|
-
const ordinary = text.replaceAll(END_OF_INPUT_MARKER, ORDINARY_CHARACTER);
|
|
57
|
-
const expected = printed(ordinary, options);
|
|
58
|
-
const actual = printed(text, options);
|
|
59
|
-
if (actual === ERROR)
|
|
60
|
-
return undefined;
|
|
61
|
-
if (expected !== ERROR && actual === expected)
|
|
62
|
-
return undefined;
|
|
63
|
-
/** Whether keeping only the first `count` U+0080 characters (the rest swapped) still differs. */
|
|
64
|
-
const dropsText = (count) => {
|
|
65
|
-
let candidate = '';
|
|
66
|
-
let from = 0;
|
|
67
|
-
for (const index of indices.slice(0, count)) {
|
|
68
|
-
candidate += ordinary.slice(from, index) + END_OF_INPUT_MARKER;
|
|
69
|
-
from = index + 1;
|
|
70
|
-
}
|
|
71
|
-
candidate += ordinary.slice(from);
|
|
72
|
-
const result = printed(candidate, options);
|
|
73
|
-
return result === ERROR || expected === ERROR || result !== expected;
|
|
74
|
-
};
|
|
75
|
-
// The full source differs, so the smallest differing prefix of U+0080 characters exists; the
|
|
76
|
-
// search assumes that keeping more of them never repairs the parse, and only ever lands on a
|
|
77
|
-
// count that was itself observed to differ.
|
|
78
|
-
let low = 1;
|
|
79
|
-
let high = indices.length;
|
|
80
|
-
while (low < high) {
|
|
81
|
-
const middle = (low + high) >> 1;
|
|
82
|
-
if (dropsText(middle))
|
|
83
|
-
high = middle;
|
|
84
|
-
else
|
|
85
|
-
low = middle + 1;
|
|
86
|
-
}
|
|
87
|
-
return indices[high - 1];
|
|
88
|
-
}
|