@eslint-sh/eslint-plugin 0.4.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,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sh/compat/no-pipe-all`: reports the `|&` pipe, shorthand for `2>&1 |`, in
|
|
3
|
+
* files whose target shell lacks it, that is, Bash older than 4.0 (macOS's
|
|
4
|
+
* stock `/bin/bash` is 3.2) and POSIX sh.
|
|
5
|
+
*
|
|
6
|
+
* Bash 3.2 rejects `|&` as a syntax error, so the whole script fails to run.
|
|
7
|
+
* The portable spelling is `cmd 2>&1 | other`. The Bash manual defines `|&`
|
|
8
|
+
* as that implicit `2>&1`, applied after any redirections the left-hand
|
|
9
|
+
* command writes, so writing `2>&1` just before the `|` is exactly
|
|
10
|
+
* equivalent. The rule offers that rewrite as a suggestion.
|
|
11
|
+
*
|
|
12
|
+
* What is read: a pipeline (`BinaryCmd`) whose operator is `|&`. A plain `|`,
|
|
13
|
+
* `||`, `&&`, a background `&`, and any of these inside quotes are other
|
|
14
|
+
* syntax and are not reported. The report is on the `|&` operator.
|
|
15
|
+
*/
|
|
16
|
+
import { PipeAll } from 'sh-ast';
|
|
17
|
+
import { createCompatRule } from './create-compat-rule.js';
|
|
18
|
+
import { operatorRange } from './operator-range.js';
|
|
19
|
+
/**
|
|
20
|
+
* The `sh/compat/no-pipe-all` rule module.
|
|
21
|
+
*
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
export const noPipeAll = createCompatRule({
|
|
25
|
+
name: 'compat/no-pipe-all',
|
|
26
|
+
feature: 'pipeAll',
|
|
27
|
+
meta: {
|
|
28
|
+
type: 'problem',
|
|
29
|
+
hasSuggestions: true,
|
|
30
|
+
docs: {
|
|
31
|
+
description: 'Disallow the `|&` pipe when the target shell lacks it',
|
|
32
|
+
},
|
|
33
|
+
messages: {
|
|
34
|
+
pipeAll: '`|&` needs {{required}}, but this file targets {{target}}. Write `cmd 2>&1 | other` instead.',
|
|
35
|
+
useStderrRedirect: 'Replace `|&` with `2>&1 |`.',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
create(context, gate) {
|
|
39
|
+
const { sourceCode } = context;
|
|
40
|
+
return {
|
|
41
|
+
BinaryCmd(node) {
|
|
42
|
+
if (node.op !== PipeAll)
|
|
43
|
+
return;
|
|
44
|
+
const range = operatorRange(sourceCode, node, '|&');
|
|
45
|
+
if (range === undefined) {
|
|
46
|
+
context.report({ node, messageId: 'pipeAll', data: { ...gate } });
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const [start, end] = range;
|
|
50
|
+
// `cmd|&other` has no space before the operator, and `2>&1` must not
|
|
51
|
+
// join the command's last word.
|
|
52
|
+
const before = sourceCode.text[start - 1];
|
|
53
|
+
const separator = before === ' ' || before === '\t' ? '' : ' ';
|
|
54
|
+
context.report({
|
|
55
|
+
loc: { start: sourceCode.getLocFromIndex(start), end: sourceCode.getLocFromIndex(end) },
|
|
56
|
+
messageId: 'pipeAll',
|
|
57
|
+
data: { ...gate },
|
|
58
|
+
suggest: [
|
|
59
|
+
{
|
|
60
|
+
messageId: 'useStderrRedirect',
|
|
61
|
+
fix: (fixer) => fixer.replaceTextRange(range, `${separator}2>&1 |`),
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message ids this rule reports with.
|
|
3
|
+
*
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
export type NoPrintfTimeFormatMessageId = 'printfTime';
|
|
7
|
+
/**
|
|
8
|
+
* The `sh/compat/no-printf-time-format` rule module.
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export declare const noPrintfTimeFormat: import("../define-shell-rule.js").ShellRule<[], "printfTime">;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sh/compat/no-printf-time-format`: reports `printf`'s `%(fmt)T` date
|
|
3
|
+
* format in files whose target shell lacks it, that is, Bash older than 4.2
|
|
4
|
+
* (macOS's stock `/bin/bash` is 3.2) and POSIX sh.
|
|
5
|
+
*
|
|
6
|
+
* Bash 4.1 rejects the directive with "invalid format character", so
|
|
7
|
+
* nothing is printed. The message suggests the portable alternative for the
|
|
8
|
+
* current time: `date '+fmt'`, which takes the same `strftime` format.
|
|
9
|
+
*
|
|
10
|
+
* What is read: a `printf` command (after transparent wrappers such as
|
|
11
|
+
* `builtin`) whose format argument, after an optional `-v name` and `--`, is
|
|
12
|
+
* a static word containing a `%(…)T` directive. The format is scanned as
|
|
13
|
+
* `printf` reads it: `%%` is a literal percent sign, and a directive may
|
|
14
|
+
* carry flags, a width and a precision before the `(`. A format that is not
|
|
15
|
+
* static cannot be read, and is not reported. The report is on the format
|
|
16
|
+
* word.
|
|
17
|
+
*/
|
|
18
|
+
import { DEFAULT_TRANSPARENT_WRAPPERS } from 'sh-ast/analyze';
|
|
19
|
+
import { BUILTIN_WRAPPER } from '../transparent-wrappers.js';
|
|
20
|
+
import { createCompatRule } from './create-compat-rule.js';
|
|
21
|
+
/**
|
|
22
|
+
* The wrappers followed to find the `printf` command: `sh-ast/analyze`'s
|
|
23
|
+
* defaults (which include `command`) plus Bash's `builtin`.
|
|
24
|
+
*/
|
|
25
|
+
const TRANSPARENT_WRAPPERS = [
|
|
26
|
+
...DEFAULT_TRANSPARENT_WRAPPERS,
|
|
27
|
+
BUILTIN_WRAPPER,
|
|
28
|
+
];
|
|
29
|
+
/** A conversion's flags, width and precision, as `printf` accepts them. */
|
|
30
|
+
const DIRECTIVE_PREFIX = /^[-+ #0']*(?:\d+|\*)?(?:\.(?:\d+|\*)?)?/u;
|
|
31
|
+
/**
|
|
32
|
+
* The `strftime` format of the first `%(fmt)T` directive in a `printf`
|
|
33
|
+
* format string, or `undefined` when it has none.
|
|
34
|
+
*/
|
|
35
|
+
function timeFormatIn(format) {
|
|
36
|
+
for (let index = 0; index < format.length; index++) {
|
|
37
|
+
if (format[index] !== '%')
|
|
38
|
+
continue;
|
|
39
|
+
if (format[index + 1] === '%') {
|
|
40
|
+
index++;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const rest = format.slice(index + 1);
|
|
44
|
+
const prefix = DIRECTIVE_PREFIX.exec(rest)?.[0] ?? '';
|
|
45
|
+
const afterPrefix = rest.slice(prefix.length);
|
|
46
|
+
if (!afterPrefix.startsWith('('))
|
|
47
|
+
continue;
|
|
48
|
+
const close = afterPrefix.indexOf(')');
|
|
49
|
+
if (close !== -1 && afterPrefix[close + 1] === 'T')
|
|
50
|
+
return afterPrefix.slice(1, close);
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The `sh/compat/no-printf-time-format` rule module.
|
|
56
|
+
*
|
|
57
|
+
* @internal
|
|
58
|
+
*/
|
|
59
|
+
export const noPrintfTimeFormat = createCompatRule({
|
|
60
|
+
name: 'compat/no-printf-time-format',
|
|
61
|
+
feature: 'printfTimeFormat',
|
|
62
|
+
meta: {
|
|
63
|
+
type: 'problem',
|
|
64
|
+
docs: {
|
|
65
|
+
description: "Disallow `printf`'s `%(fmt)T` date format when the target shell lacks it",
|
|
66
|
+
},
|
|
67
|
+
messages: {
|
|
68
|
+
printfTime: "`printf '%({{format}})T'` needs {{required}}, but this file targets {{target}}. For the current time, run `date '+{{format}}'` instead.",
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
create(context, gate) {
|
|
72
|
+
const { sourceCode } = context;
|
|
73
|
+
return {
|
|
74
|
+
CallExpr(node) {
|
|
75
|
+
const invoked = sourceCode.resolveInvokedCommand(node, {
|
|
76
|
+
transparentWrappers: TRANSPARENT_WRAPPERS,
|
|
77
|
+
});
|
|
78
|
+
if (invoked?.effective.static !== true || invoked.effective.text !== 'printf')
|
|
79
|
+
return;
|
|
80
|
+
// `chainIndices` index `node.args` directly, so slice before filtering.
|
|
81
|
+
const words = node.args.slice((invoked.chainIndices.at(-1) ?? 0) + 1);
|
|
82
|
+
let index = 0;
|
|
83
|
+
const first = words.at(0);
|
|
84
|
+
if (first !== undefined && sourceCode.resolveWordText(first) === '-v')
|
|
85
|
+
index = 2;
|
|
86
|
+
if (index < words.length && sourceCode.resolveWordText(words[index]) === '--')
|
|
87
|
+
index++;
|
|
88
|
+
const formatWord = words.at(index);
|
|
89
|
+
if (formatWord === undefined)
|
|
90
|
+
return;
|
|
91
|
+
const text = sourceCode.resolveWordText(formatWord);
|
|
92
|
+
const format = text === null ? undefined : timeFormatIn(text);
|
|
93
|
+
if (format === undefined)
|
|
94
|
+
return;
|
|
95
|
+
context.report({ node: formatWord, messageId: 'printfTime', data: { ...gate, format } });
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message ids this rule reports with: `source` for the report and `useDot` for
|
|
3
|
+
* its suggestion.
|
|
4
|
+
*
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
export type NoSourceMessageId = 'source' | 'useDot';
|
|
8
|
+
/**
|
|
9
|
+
* The `sh/compat/no-source` rule module.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
export declare const noSource: import("../define-shell-rule.js").ShellRule<[], NoSourceMessageId>;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sh/compat/no-source`: reports the `source` builtin in files that target
|
|
3
|
+
* POSIX sh, where the portable name is the `.` special built-in. Every Bash has
|
|
4
|
+
* `source`, so this is a POSIX-only gate.
|
|
5
|
+
*
|
|
6
|
+
* POSIX section 2.9.1.4 leaves the results of a command named `source`
|
|
7
|
+
* unspecified. dash has no such command: `source ./lib.sh` fails with `source:
|
|
8
|
+
* not found` and status 127, and the file is never read. `bash --posix` and
|
|
9
|
+
* BusyBox ash accept it, so only some POSIX shells show the problem.
|
|
10
|
+
*
|
|
11
|
+
* What is read: a command whose effective name is `source`, outside a branch
|
|
12
|
+
* for one shell (see `./posix-absent-command.ts`).
|
|
13
|
+
*
|
|
14
|
+
* A function named `source` anywhere in the file exempts a call that names it
|
|
15
|
+
* directly, since dash can then run that function; `bash --posix` refuses that
|
|
16
|
+
* definition, which this rule does not judge. This is a name check across the
|
|
17
|
+
* file and does not prove the function exists at the call: execution order, a
|
|
18
|
+
* definition in a branch or subshell, and `unset -f` are not read, so some exempt
|
|
19
|
+
* calls still fail on dash. A call through a wrapper is never exempt, because
|
|
20
|
+
* wrappers such as `command`, `env` and `exec` do not run functions.
|
|
21
|
+
*
|
|
22
|
+
* The suggestion replaces the word with `.` only where the two read the same
|
|
23
|
+
* file with the same effect, apart from a file that cannot be read: `.` then
|
|
24
|
+
* ends the script on a POSIX shell, so a `|| fallback` never runs, and the
|
|
25
|
+
* suggestion's text says so. The conditions are:
|
|
26
|
+
*
|
|
27
|
+
* - The command is written directly, with no wrapper.
|
|
28
|
+
* - It has no leading `VAR=value`. Such an assignment persists after `.` on a
|
|
29
|
+
* POSIX shell, while Bash outside POSIX mode scopes it to the `source`
|
|
30
|
+
* command.
|
|
31
|
+
* - It has exactly one operand. `.` takes one in POSIX, and dash ignores any
|
|
32
|
+
* others where `source` would set the positional parameters.
|
|
33
|
+
* - The operand has a literal `/`. A name without one makes `.` search `PATH`.
|
|
34
|
+
*/
|
|
35
|
+
import { definedFunctionNames } from '../../command-harvest/harvest-commands.js';
|
|
36
|
+
import { childNodes } from '../../language/syntax-view.js';
|
|
37
|
+
import { createCompatRule } from './create-compat-rule.js';
|
|
38
|
+
import { reachableCommandWord } from './posix-absent-command.js';
|
|
39
|
+
/** The builtin this rule gates. */
|
|
40
|
+
const SOURCE = 'source';
|
|
41
|
+
/**
|
|
42
|
+
* Whether `word` holds a `/` that no expansion can remove: in unquoted or
|
|
43
|
+
* quoted literal text, whatever its other parts expand to. A `/` inside an
|
|
44
|
+
* expansion (`${x%/*}`, `$(pwd)`) does not count, so the walk stops at anything
|
|
45
|
+
* that is not literal text or a double-quoted group.
|
|
46
|
+
*/
|
|
47
|
+
function hasLiteralSlash(word) {
|
|
48
|
+
return childNodes(word).some((part) => {
|
|
49
|
+
if (part.type === 'Lit' || part.type === 'SglQuoted')
|
|
50
|
+
return part.value.includes('/');
|
|
51
|
+
return part.type === 'DblQuoted' && hasLiteralSlash(part);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/** Whether `.` is an exact replacement for this `source` command (see the module comment). */
|
|
55
|
+
function dotIsEquivalent(sourceCode, node) {
|
|
56
|
+
if (sourceCode.getCommandName(node) !== SOURCE)
|
|
57
|
+
return false;
|
|
58
|
+
if (node.assigns.length > 0 || node.args.length !== 2)
|
|
59
|
+
return false;
|
|
60
|
+
return hasLiteralSlash(node.args[1]);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The `sh/compat/no-source` rule module.
|
|
64
|
+
*
|
|
65
|
+
* @internal
|
|
66
|
+
*/
|
|
67
|
+
export const noSource = createCompatRule({
|
|
68
|
+
name: 'compat/no-source',
|
|
69
|
+
feature: 'sourceCommand',
|
|
70
|
+
meta: {
|
|
71
|
+
type: 'problem',
|
|
72
|
+
hasSuggestions: true,
|
|
73
|
+
docs: {
|
|
74
|
+
description: 'Disallow the `source` builtin when the target shell is POSIX sh',
|
|
75
|
+
},
|
|
76
|
+
messages: {
|
|
77
|
+
source: '`source` is not part of {{target}}. dash fails with `source: not found` and the file is never read. Use the `.` special built-in with a path that contains a slash, for example `. ./lib.sh`.',
|
|
78
|
+
useDot: 'Replace `source` with `.`, which ends the script if the file is missing.',
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
create(context, gate) {
|
|
82
|
+
const { sourceCode } = context;
|
|
83
|
+
const fileDefinesSource = definedFunctionNames(sourceCode).has(SOURCE);
|
|
84
|
+
return {
|
|
85
|
+
CallExpr(node) {
|
|
86
|
+
const word = reachableCommandWord(sourceCode, node, SOURCE);
|
|
87
|
+
if (word === undefined)
|
|
88
|
+
return;
|
|
89
|
+
// Only a name written directly can run a function: a wrapper skips it.
|
|
90
|
+
if (fileDefinesSource && sourceCode.getCommandName(node) === SOURCE)
|
|
91
|
+
return;
|
|
92
|
+
context.report({
|
|
93
|
+
node: word,
|
|
94
|
+
messageId: 'source',
|
|
95
|
+
data: { target: gate.target },
|
|
96
|
+
suggest: dotIsEquivalent(sourceCode, node)
|
|
97
|
+
? [{ messageId: 'useDot', fix: (fixer) => fixer.replaceText(word, '.') }]
|
|
98
|
+
: [],
|
|
99
|
+
});
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
},
|
|
103
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message ids this rule reports with.
|
|
3
|
+
*
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
export type NoTestVMessageId = 'testV';
|
|
7
|
+
/**
|
|
8
|
+
* The `sh/compat/no-test-v` rule module.
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export declare const noTestV: import("../define-shell-rule.js").ShellRule<[], "testV">;
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sh/compat/no-test-v`: reports the `-v name` test (`[[ -v x ]]`,
|
|
3
|
+
* `test -v x`, `[ -v x ]`) in files whose target shell lacks it, that is,
|
|
4
|
+
* Bash older than 4.2 (macOS's stock `/bin/bash` is 3.2) and POSIX sh.
|
|
5
|
+
*
|
|
6
|
+
* In Bash 4.1 and older, `[[ -v x ]]` is a syntax error, so the whole script
|
|
7
|
+
* fails to parse, and `test -v x` fails with "unary operator expected". The
|
|
8
|
+
* message suggests the portable alternative, `[ -n "${x+set}" ]`, which is
|
|
9
|
+
* true exactly when `x` is set (even to the empty string) and is safe under
|
|
10
|
+
* `set -u`.
|
|
11
|
+
*
|
|
12
|
+
* What is read:
|
|
13
|
+
*
|
|
14
|
+
* - inside `[[ … ]]`, the parser's `-v` unary test node;
|
|
15
|
+
* - for `test` and `[` (after transparent wrappers such as `builtin`), the
|
|
16
|
+
* arguments, read the way Bash's `test` reads them: with two arguments a
|
|
17
|
+
* leading `-v` is the operator; with three, the middle one is a binary
|
|
18
|
+
* operator if it can be one, and `! -v name` negates the test; with four,
|
|
19
|
+
* `! …` and `( … )` peel off; anything longer goes through `test`'s own
|
|
20
|
+
* precedence parser, where `-v` is the operator only where a primary is
|
|
21
|
+
* expected and another argument follows it. So `[ "$x" = -v ]` and
|
|
22
|
+
* `[ -n -v ]` do not report. A double-quoted expansion (`"$name"`) is one
|
|
23
|
+
* operand whatever its value. Any other argument that is not a static word
|
|
24
|
+
* ends the reading, because it may expand to several arguments or none.
|
|
25
|
+
*
|
|
26
|
+
* The report is on the `[[` test, or on the `-v` word.
|
|
27
|
+
*/
|
|
28
|
+
import { TsVarSet } from 'sh-ast';
|
|
29
|
+
import { DEFAULT_TRANSPARENT_WRAPPERS } from 'sh-ast/analyze';
|
|
30
|
+
import { BUILTIN_WRAPPER } from '../transparent-wrappers.js';
|
|
31
|
+
import { createCompatRule } from './create-compat-rule.js';
|
|
32
|
+
/**
|
|
33
|
+
* The wrappers followed to find the `test` command: `sh-ast/analyze`'s
|
|
34
|
+
* defaults (which include `command`) plus Bash's `builtin`.
|
|
35
|
+
*/
|
|
36
|
+
const TRANSPARENT_WRAPPERS = [
|
|
37
|
+
...DEFAULT_TRANSPARENT_WRAPPERS,
|
|
38
|
+
BUILTIN_WRAPPER,
|
|
39
|
+
];
|
|
40
|
+
/** The commands that are Bash's `test` builtin. */
|
|
41
|
+
const TEST_COMMANDS = new Set(['test', '[']);
|
|
42
|
+
/** Bash's `test` binary operators (`test_binop`), without `-a` and `-o`. */
|
|
43
|
+
const BINARY_OPERATORS = new Set([
|
|
44
|
+
'=',
|
|
45
|
+
'==',
|
|
46
|
+
'!=',
|
|
47
|
+
'<',
|
|
48
|
+
'>',
|
|
49
|
+
'-eq',
|
|
50
|
+
'-ne',
|
|
51
|
+
'-lt',
|
|
52
|
+
'-le',
|
|
53
|
+
'-gt',
|
|
54
|
+
'-ge',
|
|
55
|
+
'-nt',
|
|
56
|
+
'-ot',
|
|
57
|
+
'-ef',
|
|
58
|
+
]);
|
|
59
|
+
/** Bash's `test` unary operators (`test_unop`). */
|
|
60
|
+
const UNARY_OPERATORS = new Set([
|
|
61
|
+
'-a',
|
|
62
|
+
'-b',
|
|
63
|
+
'-c',
|
|
64
|
+
'-d',
|
|
65
|
+
'-e',
|
|
66
|
+
'-f',
|
|
67
|
+
'-g',
|
|
68
|
+
'-h',
|
|
69
|
+
'-k',
|
|
70
|
+
'-n',
|
|
71
|
+
'-o',
|
|
72
|
+
'-p',
|
|
73
|
+
'-r',
|
|
74
|
+
'-s',
|
|
75
|
+
'-t',
|
|
76
|
+
'-u',
|
|
77
|
+
'-v',
|
|
78
|
+
'-w',
|
|
79
|
+
'-x',
|
|
80
|
+
'-z',
|
|
81
|
+
'-G',
|
|
82
|
+
'-L',
|
|
83
|
+
'-O',
|
|
84
|
+
'-S',
|
|
85
|
+
'-N',
|
|
86
|
+
'-R',
|
|
87
|
+
]);
|
|
88
|
+
/**
|
|
89
|
+
* What a double-quoted, non-static argument stands for while reading `test`'s
|
|
90
|
+
* arguments: an operand that is never an operator. A NUL cannot occur in a
|
|
91
|
+
* shell word, so no real argument equals it.
|
|
92
|
+
*/
|
|
93
|
+
const QUOTED_OPERAND = '\0operand';
|
|
94
|
+
/** A shell variable name, optionally with an array subscript. */
|
|
95
|
+
const VARIABLE_NAME = /^[A-Za-z_][A-Za-z0-9_]*(?:\[[^\]]*\])?$/u;
|
|
96
|
+
/**
|
|
97
|
+
* The indices of the arguments in `args` that Bash's `test` reads as the
|
|
98
|
+
* `-v` operator. `args` are the static texts after the command name (and
|
|
99
|
+
* without a closing `]`); reading stops at the first `null`.
|
|
100
|
+
*/
|
|
101
|
+
function variableTestIndices(args) {
|
|
102
|
+
const end = args.indexOf(null);
|
|
103
|
+
const argv = (end === -1 ? args : args.slice(0, end));
|
|
104
|
+
const found = [];
|
|
105
|
+
/** `test`'s two-argument rule, at offset `at`. */
|
|
106
|
+
function twoArguments(at) {
|
|
107
|
+
if (argv[at] === '-v')
|
|
108
|
+
found.push(at);
|
|
109
|
+
}
|
|
110
|
+
/** `test`'s three-argument rule, at offset `at`. */
|
|
111
|
+
function threeArguments(at) {
|
|
112
|
+
const middle = argv[at + 1];
|
|
113
|
+
if (BINARY_OPERATORS.has(middle) || middle === '-a' || middle === '-o')
|
|
114
|
+
return true;
|
|
115
|
+
if (argv[at] === '!') {
|
|
116
|
+
twoArguments(at + 1);
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
return argv[at] === '(' && argv[at + 2] === ')';
|
|
120
|
+
}
|
|
121
|
+
/** `test`'s precedence parser over every argument (`expr` in Bash's test.c). */
|
|
122
|
+
function expression() {
|
|
123
|
+
let pos = 0;
|
|
124
|
+
const term = () => {
|
|
125
|
+
if (pos >= argv.length)
|
|
126
|
+
return;
|
|
127
|
+
const word = argv[pos];
|
|
128
|
+
if (word === '!') {
|
|
129
|
+
pos++;
|
|
130
|
+
term();
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (word === '(') {
|
|
134
|
+
pos++;
|
|
135
|
+
or();
|
|
136
|
+
if (argv[pos] === ')')
|
|
137
|
+
pos++;
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (pos + 3 <= argv.length && BINARY_OPERATORS.has(argv[pos + 1])) {
|
|
141
|
+
pos += 3;
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (UNARY_OPERATORS.has(word) && pos + 1 < argv.length) {
|
|
145
|
+
if (word === '-v')
|
|
146
|
+
found.push(pos);
|
|
147
|
+
pos += 2;
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
pos++;
|
|
151
|
+
};
|
|
152
|
+
const and = () => {
|
|
153
|
+
term();
|
|
154
|
+
while (argv[pos] === '-a') {
|
|
155
|
+
pos++;
|
|
156
|
+
term();
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
const or = () => {
|
|
160
|
+
and();
|
|
161
|
+
while (argv[pos] === '-o') {
|
|
162
|
+
pos++;
|
|
163
|
+
and();
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
while (pos < argv.length) {
|
|
167
|
+
const before = pos;
|
|
168
|
+
or();
|
|
169
|
+
if (pos === before)
|
|
170
|
+
pos++;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
switch (argv.length) {
|
|
174
|
+
case 0:
|
|
175
|
+
case 1:
|
|
176
|
+
break;
|
|
177
|
+
case 2:
|
|
178
|
+
if (argv[0] !== '!')
|
|
179
|
+
twoArguments(0);
|
|
180
|
+
break;
|
|
181
|
+
case 3:
|
|
182
|
+
if (!threeArguments(0))
|
|
183
|
+
expression();
|
|
184
|
+
break;
|
|
185
|
+
case 4:
|
|
186
|
+
if (argv[0] === '!') {
|
|
187
|
+
if (!threeArguments(1))
|
|
188
|
+
expression();
|
|
189
|
+
}
|
|
190
|
+
else if (argv[0] === '(' && argv[3] === ')') {
|
|
191
|
+
if (argv[1] !== '!')
|
|
192
|
+
twoArguments(1);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
expression();
|
|
196
|
+
}
|
|
197
|
+
break;
|
|
198
|
+
default:
|
|
199
|
+
expression();
|
|
200
|
+
}
|
|
201
|
+
return found;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* The `sh/compat/no-test-v` rule module.
|
|
205
|
+
*
|
|
206
|
+
* @internal
|
|
207
|
+
*/
|
|
208
|
+
export const noTestV = createCompatRule({
|
|
209
|
+
name: 'compat/no-test-v',
|
|
210
|
+
feature: 'testVariableSet',
|
|
211
|
+
meta: {
|
|
212
|
+
type: 'problem',
|
|
213
|
+
docs: {
|
|
214
|
+
description: 'Disallow the `-v name` test (`[[ -v x ]]`, `test -v x`) when the target shell lacks it',
|
|
215
|
+
},
|
|
216
|
+
messages: {
|
|
217
|
+
testV: '`-v {{name}}` needs {{required}}, but this file targets {{target}}. Test whether the variable is set with `[ -n "${{{name}}+set}" ]` instead.',
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
create(context, gate) {
|
|
221
|
+
const { sourceCode } = context;
|
|
222
|
+
/** The variable name shown in the message for `operand`. */
|
|
223
|
+
function nameOf(operand) {
|
|
224
|
+
const text = operand?.type === 'Word' ? sourceCode.resolveWordText(operand) : null;
|
|
225
|
+
return text !== null && VARIABLE_NAME.test(text) ? text : 'name';
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* The argument `word` gives `test`: its static text; a stand-in operand
|
|
229
|
+
* for a word that is one double-quoted string, which always makes exactly
|
|
230
|
+
* one argument whatever it expands to; or `null` for any other word, which
|
|
231
|
+
* may split into several arguments or none.
|
|
232
|
+
*/
|
|
233
|
+
function argumentText(word) {
|
|
234
|
+
const text = sourceCode.resolveWordText(word);
|
|
235
|
+
if (text !== null)
|
|
236
|
+
return text;
|
|
237
|
+
if (word.parts.length === 1 && word.parts[0].type === 'DblQuoted') {
|
|
238
|
+
return QUOTED_OPERAND;
|
|
239
|
+
}
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
UnaryTest(node) {
|
|
244
|
+
if (node.op !== TsVarSet)
|
|
245
|
+
return;
|
|
246
|
+
context.report({
|
|
247
|
+
node,
|
|
248
|
+
messageId: 'testV',
|
|
249
|
+
data: { ...gate, name: nameOf(node.x) },
|
|
250
|
+
});
|
|
251
|
+
},
|
|
252
|
+
CallExpr(node) {
|
|
253
|
+
const invoked = sourceCode.resolveInvokedCommand(node, {
|
|
254
|
+
transparentWrappers: TRANSPARENT_WRAPPERS,
|
|
255
|
+
});
|
|
256
|
+
if (invoked?.effective.static !== true)
|
|
257
|
+
return;
|
|
258
|
+
const command = invoked.effective.text;
|
|
259
|
+
if (!TEST_COMMANDS.has(command))
|
|
260
|
+
return;
|
|
261
|
+
// `chainIndices` index `node.args` directly, so slice before filtering.
|
|
262
|
+
let words = node.args.slice((invoked.chainIndices.at(-1) ?? 0) + 1);
|
|
263
|
+
if (command === '[') {
|
|
264
|
+
const last = words.at(-1);
|
|
265
|
+
if (last === undefined || sourceCode.resolveWordText(last) !== ']')
|
|
266
|
+
return;
|
|
267
|
+
words = words.slice(0, -1);
|
|
268
|
+
}
|
|
269
|
+
const texts = words.map((word) => argumentText(word));
|
|
270
|
+
for (const index of variableTestIndices(texts)) {
|
|
271
|
+
context.report({
|
|
272
|
+
node: words[index],
|
|
273
|
+
messageId: 'testV',
|
|
274
|
+
data: { ...gate, name: nameOf(words[index + 1]) },
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
},
|
|
280
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message ids this rule reports with: `unicodeEscape` when the target leaves
|
|
3
|
+
* the escape undecoded, `unicodeEscapeLatin1` when it decodes it to a
|
|
4
|
+
* Latin-1 byte.
|
|
5
|
+
*
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
export type NoUnicodeEscapeMessageId = 'unicodeEscape' | 'unicodeEscapeLatin1';
|
|
9
|
+
/**
|
|
10
|
+
* The `sh/compat/no-unicode-escape` rule module.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
export declare const noUnicodeEscape: import("../define-shell-rule.js").ShellRule<[], NoUnicodeEscapeMessageId>;
|