@eslint-sh/eslint-plugin 0.1.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.
@@ -0,0 +1,104 @@
1
+ import type { ShellDialect } from 'sh-ast';
2
+ /**
3
+ * `languageOptions` accepted by {@link ShellLanguage} (design/PACKAGES.md's
4
+ * `languageOptions` schema): `dialect` selects mvdan/sh's `LangVariant` by
5
+ * string name (default `"bash"`); `shellVersion` is consumed by the
6
+ * rule layer (e.g. the bash-3.2 compatibility pack), never by the parser
7
+ * itself (see design/ARCHITECTURE.md, "A verified nuance worth stating
8
+ * loudly").
9
+ *
10
+ * Extends `Record<string, unknown>` (rather than a bare interface) so this
11
+ * type satisfies the `@eslint/core` package's `LanguageOptions` bound
12
+ * (itself `Record<string, unknown>`) wherever it is used as a
13
+ * `Language`/`SourceCode` type parameter — ESLint's own contract for
14
+ * language options is that they are arbitrary user-supplied records, not a
15
+ * closed shape.
16
+ *
17
+ * @public
18
+ */
19
+ export interface ShellLanguageOptions extends Record<string, unknown> {
20
+ readonly dialect?: ShellDialect;
21
+ readonly shellVersion?: string;
22
+ /**
23
+ * The prefix directive/inline-config comments must start with — e.g. the
24
+ * default `"eslint"` recognizes `# eslint-disable-next-line ...` and
25
+ * `# eslint <rule-config>`. Consumers who embed shell-linting inside a
26
+ * larger toolchain that already overloads `#eslint-*` comments for
27
+ * something else can configure a different prefix (design/ARCHITECTURE.md,
28
+ * "SourceCode and Language objects").
29
+ * @defaultValue `"eslint"`
30
+ */
31
+ readonly directivePrefix?: string;
32
+ }
33
+ /**
34
+ * The plain-data shape {@link NormalizedShellLanguageOptions.toJSON} returns:
35
+ * `dialect` and `directivePrefix` always present (both defaulted), `shellVersion`
36
+ * present only when the caller supplied one. Keys are always emitted in this
37
+ * fixed order so that two logically-identical inputs serialize identically
38
+ * regardless of the key order the caller happened to use — the property this
39
+ * type exists to guarantee for cache-key stability (see
40
+ * {@link NormalizedShellLanguageOptions}). `directivePrefix` is included
41
+ * unconditionally (like `dialect`, unlike `shellVersion`) because — like
42
+ * `dialect` and unlike `shellVersion` — it always has a concrete effect on
43
+ * linting results (which comments the `SourceCode` recognizes as directives)
44
+ * whether or not the caller supplied one, so two configs that differ only in
45
+ * an *implicit* default vs. an *explicit* `directivePrefix: "eslint"` must
46
+ * still cache-key identically.
47
+ *
48
+ * @public
49
+ */
50
+ export interface ShellLanguageOptionsJSON {
51
+ readonly dialect: ShellDialect;
52
+ readonly directivePrefix: string;
53
+ readonly shellVersion?: string;
54
+ }
55
+ /**
56
+ * The result of {@link ShellLanguage.normalizeLanguageOptions}: `dialect` and
57
+ * `directivePrefix` are always resolved to concrete values (defaulted when
58
+ * the caller didn't specify them), and `toJSON()` provides the "custom
59
+ * serialization" ESLint's languages guide recommends
60
+ * (https://eslint.org/docs/latest/extend/languages, "normalizeLanguageOptions")
61
+ * so that config caching keyed off `JSON.stringify(languageOptions)` is
62
+ * stable irrespective of the input object's own key order — `JSON.stringify`
63
+ * calls `toJSON()` automatically when present.
64
+ *
65
+ * @public
66
+ */
67
+ export interface NormalizedShellLanguageOptions extends Record<string, unknown> {
68
+ readonly dialect: ShellDialect;
69
+ readonly directivePrefix: string;
70
+ readonly shellVersion?: string;
71
+ toJSON(): ShellLanguageOptionsJSON;
72
+ }
73
+ /**
74
+ * Validates `languageOptions` for {@link ShellLanguage}. Throws a
75
+ * `TypeError` when `dialect` is present but not one of the five supported
76
+ * dialects, when `shellVersion` is present but not a string or does not
77
+ * match the `MAJOR[.MINOR]` numeric shape (see {@link SHELL_VERSION_PATTERN};
78
+ * e.g. `''` and `'not-a-version'` are rejected, but a shape-valid,
79
+ * nonexistent version like `'99.99'` is accepted — shape is validated, not
80
+ * membership in a known-versions list), or when `directivePrefix` is present
81
+ * but not a valid directive-comment label (see {@link DIRECTIVE_PREFIX_PATTERN}).
82
+ * Unknown keys are silently ignored, per the ESLint `Language` contract
83
+ * (design/ARCHITECTURE.md's `languageOptions` schema comment).
84
+ *
85
+ * The parameter is treated as `Record<string, unknown>` internally (rather
86
+ * than trusting {@link ShellLanguageOptions}'s declared field types) because
87
+ * this function's entire job is validating an untrusted boundary: the
88
+ * caller-supplied object may not actually match the declared shape.
89
+ *
90
+ * @public
91
+ */
92
+ export declare function validateShellLanguageOptions(languageOptions: Record<string, unknown>): void;
93
+ /**
94
+ * Normalizes `languageOptions` for {@link ShellLanguage}: resolves `dialect`
95
+ * and `directivePrefix` to their defaults (`"bash"` / `"eslint"`) when
96
+ * unset, and attaches a `toJSON()` method whose key order never depends on
97
+ * the input object's own key order (see {@link NormalizedShellLanguageOptions}).
98
+ * Callers are expected to have already run {@link validateShellLanguageOptions}
99
+ * — this function does not re-validate.
100
+ *
101
+ * @public
102
+ */
103
+ export declare function normalizeShellLanguageOptions(languageOptions: Record<string, unknown>): NormalizedShellLanguageOptions;
104
+ //# sourceMappingURL=shell-language-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-language-options.d.ts","sourceRoot":"","sources":["../../src/language/shell-language-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AA4D3C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,oBAAqB,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACnE,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAE/B;;;;;;;;OAQG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,8BAA+B,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC7E,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,MAAM,IAAI,wBAAwB,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,4BAA4B,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CA8B3F;AAED;;;;;;;;;GASG;AACH,wBAAgB,6BAA6B,CAC3C,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACvC,8BAA8B,CAkBhC"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * The full set of dialects {@link ShellLanguage.validateLanguageOptions}
3
+ * accepts, in the order surfaced by its error message. Mirrors
4
+ * `sh-ast`'s `SUPPORTED_SHELL_DIALECTS` (see
5
+ * design/ARCHITECTURE.md, "Parse errors and dialects", and
6
+ * design/PACKAGES.md's `languageOptions` schema) — kept as an independent
7
+ * constant here (rather than imported) because the bridge does not export
8
+ * its internal list; both lists are exercised by tests that pin the same
9
+ * five dialect names.
10
+ */
11
+ const SUPPORTED_SHELL_DIALECTS = ['bash', 'posix', 'mksh', 'bats', 'zsh'];
12
+ function isShellDialect(value) {
13
+ return (typeof value === 'string' && SUPPORTED_SHELL_DIALECTS.includes(value));
14
+ }
15
+ /**
16
+ * Default for {@link ShellLanguageOptions.directivePrefix} — the
17
+ * ESLint-conventional spelling (design/ARCHITECTURE.md, "SourceCode and
18
+ * Language objects": "The directive prefix is configurable but defaults to
19
+ * the ESLint-conventional spelling").
20
+ */
21
+ const DEFAULT_DIRECTIVE_PREFIX = 'eslint';
22
+ /**
23
+ * Matches `@eslint/plugin-kit`'s `ConfigCommentParser` internal
24
+ * `directivesPattern` (`/^([a-z]+(?:-[a-z]+)*)(?:\s|$)/u`) for the label
25
+ * portion alone: a `directivePrefix` that doesn't fit this shape could never
26
+ * combine with a `-disable`/`-enable`/`-disable-line`/`-disable-next-line`
27
+ * suffix into a label that parser can ever recognize, so rejecting it here
28
+ * (rather than silently accepting a prefix that can never match anything)
29
+ * is a strict improvement.
30
+ */
31
+ const DIRECTIVE_PREFIX_PATTERN = /^[a-z]+(?:-[a-z]+)*$/u;
32
+ function isValidDirectivePrefix(value) {
33
+ return typeof value === 'string' && DIRECTIVE_PREFIX_PATTERN.test(value);
34
+ }
35
+ /**
36
+ * Matches the `MAJOR[.MINOR]` numeric shape {@link validateShellLanguageOptions}
37
+ * requires `shellVersion` to have (issue #34: the sibling `dialect` option throws
38
+ * an exemplary enum-listing TypeError, while `shellVersion` accepted any
39
+ * string — including `''` and `'not-a-version'` — with zero feedback). This
40
+ * validates *shape only*, not membership in a known-versions list: a
41
+ * shape-valid but nonexistent version like `'99.99'` is intentionally
42
+ * accepted, per this issue's acceptance criteria and non-goals (deciding
43
+ * `shellVersion`'s consumption semantics, and per-dialect known-version
44
+ * allowlists, are separate concerns left to the rule layer).
45
+ */
46
+ const SHELL_VERSION_PATTERN = /^\d+(?:\.\d+)?$/u;
47
+ function isValidShellVersionShape(value) {
48
+ return SHELL_VERSION_PATTERN.test(value);
49
+ }
50
+ /**
51
+ * Validates `languageOptions` for {@link ShellLanguage}. Throws a
52
+ * `TypeError` when `dialect` is present but not one of the five supported
53
+ * dialects, when `shellVersion` is present but not a string or does not
54
+ * match the `MAJOR[.MINOR]` numeric shape (see {@link SHELL_VERSION_PATTERN};
55
+ * e.g. `''` and `'not-a-version'` are rejected, but a shape-valid,
56
+ * nonexistent version like `'99.99'` is accepted — shape is validated, not
57
+ * membership in a known-versions list), or when `directivePrefix` is present
58
+ * but not a valid directive-comment label (see {@link DIRECTIVE_PREFIX_PATTERN}).
59
+ * Unknown keys are silently ignored, per the ESLint `Language` contract
60
+ * (design/ARCHITECTURE.md's `languageOptions` schema comment).
61
+ *
62
+ * The parameter is treated as `Record<string, unknown>` internally (rather
63
+ * than trusting {@link ShellLanguageOptions}'s declared field types) because
64
+ * this function's entire job is validating an untrusted boundary: the
65
+ * caller-supplied object may not actually match the declared shape.
66
+ *
67
+ * @public
68
+ */
69
+ export function validateShellLanguageOptions(languageOptions) {
70
+ const { dialect, shellVersion, directivePrefix } = languageOptions;
71
+ if (dialect !== undefined && !isShellDialect(dialect)) {
72
+ throw new TypeError(`@eslint-sh/eslint-plugin: languageOptions.dialect must be one of ${SUPPORTED_SHELL_DIALECTS.join(', ')}; received ${JSON.stringify(dialect)}.`);
73
+ }
74
+ if (shellVersion !== undefined && typeof shellVersion !== 'string') {
75
+ throw new TypeError(`@eslint-sh/eslint-plugin: languageOptions.shellVersion must be a string (e.g. "3.2"); received ${JSON.stringify(shellVersion)}.`);
76
+ }
77
+ if (typeof shellVersion === 'string' && !isValidShellVersionShape(shellVersion)) {
78
+ throw new TypeError(`@eslint-sh/eslint-plugin: languageOptions.shellVersion must match the shape MAJOR[.MINOR] (e.g. "3.2", "5"); received ${JSON.stringify(shellVersion)}.`);
79
+ }
80
+ if (directivePrefix !== undefined && !isValidDirectivePrefix(directivePrefix)) {
81
+ throw new TypeError(`@eslint-sh/eslint-plugin: languageOptions.directivePrefix must be one or more lowercase, hyphen-separated words (e.g. "eslint", "shellcheck-sh"); received ${JSON.stringify(directivePrefix)}.`);
82
+ }
83
+ }
84
+ /**
85
+ * Normalizes `languageOptions` for {@link ShellLanguage}: resolves `dialect`
86
+ * and `directivePrefix` to their defaults (`"bash"` / `"eslint"`) when
87
+ * unset, and attaches a `toJSON()` method whose key order never depends on
88
+ * the input object's own key order (see {@link NormalizedShellLanguageOptions}).
89
+ * Callers are expected to have already run {@link validateShellLanguageOptions}
90
+ * — this function does not re-validate.
91
+ *
92
+ * @public
93
+ */
94
+ export function normalizeShellLanguageOptions(languageOptions) {
95
+ const dialect = languageOptions.dialect ?? 'bash';
96
+ const directivePrefix = languageOptions.directivePrefix ?? DEFAULT_DIRECTIVE_PREFIX;
97
+ const shellVersion = languageOptions.shellVersion;
98
+ return {
99
+ dialect,
100
+ directivePrefix,
101
+ // `exactOptionalPropertyTypes` forbids assigning `undefined` to an
102
+ // optional property directly — omit the key entirely rather than set it
103
+ // to `undefined` when the caller didn't supply a shellVersion.
104
+ ...(shellVersion === undefined ? {} : { shellVersion }),
105
+ toJSON() {
106
+ return shellVersion === undefined
107
+ ? { dialect, directivePrefix }
108
+ : { dialect, directivePrefix, shellVersion };
109
+ },
110
+ };
111
+ }
112
+ //# sourceMappingURL=shell-language-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-language-options.js","sourceRoot":"","sources":["../../src/language/shell-language-options.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,wBAAwB,GAA4B,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;AAEnG,SAAS,cAAc,CAAC,KAAc;IACpC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ,IAAK,wBAA8C,CAAC,QAAQ,CAAC,KAAK,CAAC,CAC7F,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,wBAAwB,GAAG,QAAQ,CAAC;AAE1C;;;;;;;;GAQG;AACH,MAAM,wBAAwB,GAAG,uBAAuB,CAAC;AAEzD,SAAS,sBAAsB,CAAC,KAAc;IAC5C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC3E,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,qBAAqB,GAAG,kBAAkB,CAAC;AAEjD,SAAS,wBAAwB,CAAC,KAAa;IAC7C,OAAO,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC3C,CAAC;AA6ED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,4BAA4B,CAAC,eAAwC;IACnF,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,GAAG,eAAe,CAAC;IACnE,IAAI,OAAO,KAAK,SAAS,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,SAAS,CACjB,oEAAoE,wBAAwB,CAAC,IAAI,CAC/F,IAAI,CACL,cAAc,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAC1C,CAAC;IACJ,CAAC;IACD,IAAI,YAAY,KAAK,SAAS,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;QACnE,MAAM,IAAI,SAAS,CACjB,kGAAkG,IAAI,CAAC,SAAS,CAC9G,YAAY,CACb,GAAG,CACL,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,YAAY,KAAK,QAAQ,IAAI,CAAC,wBAAwB,CAAC,YAAY,CAAC,EAAE,CAAC;QAChF,MAAM,IAAI,SAAS,CACjB,yHAAyH,IAAI,CAAC,SAAS,CACrI,YAAY,CACb,GAAG,CACL,CAAC;IACJ,CAAC;IACD,IAAI,eAAe,KAAK,SAAS,IAAI,CAAC,sBAAsB,CAAC,eAAe,CAAC,EAAE,CAAC;QAC9E,MAAM,IAAI,SAAS,CACjB,8JAA8J,IAAI,CAAC,SAAS,CAC1K,eAAe,CAChB,GAAG,CACL,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,6BAA6B,CAC3C,eAAwC;IAExC,MAAM,OAAO,GAAI,eAAe,CAAC,OAAoC,IAAI,MAAM,CAAC;IAChF,MAAM,eAAe,GAClB,eAAe,CAAC,eAAsC,IAAI,wBAAwB,CAAC;IACtF,MAAM,YAAY,GAAG,eAAe,CAAC,YAAkC,CAAC;IACxE,OAAO;QACL,OAAO;QACP,eAAe;QACf,mEAAmE;QACnE,wEAAwE;QACxE,+DAA+D;QAC/D,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC;QACvD,MAAM;YACJ,OAAO,YAAY,KAAK,SAAS;gBAC/B,CAAC,CAAC,EAAE,OAAO,EAAE,eAAe,EAAE;gBAC9B,CAAC,CAAC,EAAE,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC;QACjD,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,84 @@
1
+ import type { ShFile, ShNode } from 'sh-ast';
2
+ import type { File, Language, OkParseResult, ParseResult } from '@eslint/core';
3
+ import { ShellSourceCode } from './shell-source-code.js';
4
+ import { type NormalizedShellLanguageOptions, type ShellLanguageOptions } from './shell-language-options.js';
5
+ /**
6
+ * `ShellLanguage` — the `@eslint/core` `Language` implementation for shell
7
+ * scripts (design/ARCHITECTURE.md, "SourceCode and Language objects";
8
+ * design/PACKAGES.md, `@eslint-sh/eslint-plugin`). Registered by the plugin
9
+ * as `languages.sh`, so consumers select it via `language: "sh/sh"`.
10
+ *
11
+ * Implements `matchesSelectorClass` (the six launch esquery selector
12
+ * classes — see `./selector-classes.js`), so rules and end users can write
13
+ * class-based selectors (`:command`, `:loop`, `:conditional`, `:expansion`,
14
+ * `:quoted`, `:function`) against shell trees. The `SourceCode` directive
15
+ * members (`getDisableDirectives`, `getInlineConfigNodes`,
16
+ * `applyInlineConfig`) are implemented on `ShellSourceCode` itself; see
17
+ * shell-source-code.ts.
18
+ *
19
+ * @public
20
+ */
21
+ export declare class ShellLanguage implements Language {
22
+ readonly fileType: "text";
23
+ readonly lineStart: 1;
24
+ readonly columnStart: 1;
25
+ readonly nodeTypeKey: string;
26
+ readonly visitorKeys: Record<string, string[]>;
27
+ readonly defaultLanguageOptions: ShellLanguageOptions;
28
+ /**
29
+ * Validates `languageOptions.dialect` (must be one of the five supported
30
+ * dialects, if present), `languageOptions.shellVersion` (must be a
31
+ * string matching the `MAJOR[.MINOR]` numeric shape, if present), and
32
+ * `languageOptions.directivePrefix` (must be a valid directive-comment
33
+ * label, if present). Unknown keys are silently ignored, per the
34
+ * `Language` contract. See `shell-language-options.ts`'s
35
+ * `validateShellLanguageOptions` for the full rationale.
36
+ */
37
+ validateLanguageOptions(languageOptions: ShellLanguageOptions): void;
38
+ /**
39
+ * Resolves `dialect` to its default (`"bash"`) when unset and attaches a
40
+ * `toJSON()` method with a fixed key order, so that ESLint's config
41
+ * caching (keyed off serializing `languageOptions`) is stable regardless
42
+ * of the key order a user's config happened to use. See
43
+ * `shell-language-options.ts`'s `normalizeShellLanguageOptions`.
44
+ */
45
+ normalizeLanguageOptions(languageOptions: ShellLanguageOptions): NormalizedShellLanguageOptions;
46
+ /**
47
+ * `@eslint/core`'s esquery hook: lets a selector's leading `:` "class"
48
+ * (e.g. `:command` in `CallExpr[args.0.parts.0.value='jq']` isn't even
49
+ * needed there, but `:loop :command` is) match a whole family of node
50
+ * types instead of one literal type name. Delegates to
51
+ * `./selector-classes.js`'s `SELECTOR_CLASS_NODE_TYPES` table for the six
52
+ * launch classes (design/ARCHITECTURE.md, "SourceCode and Language
53
+ * objects"); `ancestry` is unused today because none of the six launch
54
+ * classes need ancestor context, but the parameter is accepted to match
55
+ * `Language.matchesSelectorClass`'s signature exactly.
56
+ */
57
+ matchesSelectorClass(className: string, node: ShNode, _ancestry: ShNode[]): boolean;
58
+ /**
59
+ * Parses `file.body` (guaranteed to be a `string`, since `fileType` is
60
+ * `"text"`) via `sh-ast`'s `parseSync`, mapping the result to
61
+ * the `{ok: true, ast}` / `{ok: false, errors}` shape `Language.parse` is
62
+ * required to return. Never throws — parse failures are reported as
63
+ * fatal lint messages, per the `Language` contract
64
+ * (design/ARCHITECTURE.md, "Parse errors and dialects").
65
+ */
66
+ parse(file: File, context: {
67
+ languageOptions: ShellLanguageOptions;
68
+ }): ParseResult<ShFile>;
69
+ /**
70
+ * Creates the `ShellSourceCode` ESLint uses to run rules against `ast`.
71
+ * `context.languageOptions` is ESLint's already-normalized copy (see
72
+ * `packages/eslint-plugin/test/language-options.test.ts` and
73
+ * `eslint`'s `Config#finalize`, which replaces `languageOptions` with
74
+ * `normalizeLanguageOptions`'s return value before either `parse` or
75
+ * `createSourceCode` is called) — `directivePrefix` is therefore always
76
+ * present, but `?? 'eslint'` stays as an explicit, honest fallback for
77
+ * direct construction (e.g. tests that skip the `Language` normalization
78
+ * step), mirroring `parse()`'s `dialect` default above.
79
+ */
80
+ createSourceCode(file: File, parseResult: OkParseResult<ShFile>, context: {
81
+ languageOptions: ShellLanguageOptions;
82
+ }): ShellSourceCode;
83
+ }
84
+ //# sourceMappingURL=shell-language.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-language.d.ts","sourceRoot":"","sources":["../../src/language/shell-language.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAgB,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC3D,OAAO,KAAK,EAAE,IAAI,EAAa,QAAQ,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE1F,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAGL,KAAK,8BAA8B,EACnC,KAAK,oBAAoB,EAC1B,MAAM,6BAA6B,CAAC;AAsFrC;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,aAAc,YAAW,QAAQ;IAC5C,QAAQ,CAAC,QAAQ,EAAG,MAAM,CAAU;IACpC,QAAQ,CAAC,SAAS,EAAG,CAAC,CAAU;IAChC,QAAQ,CAAC,WAAW,EAAG,CAAC,CAAU;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAU;IACtC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAA2B;IACzE,QAAQ,CAAC,sBAAsB,EAAE,oBAAoB,CAAuB;IAE5E;;;;;;;;OAQG;IACH,uBAAuB,CAAC,eAAe,EAAE,oBAAoB,GAAG,IAAI;IAIpE;;;;;;OAMG;IACH,wBAAwB,CAAC,eAAe,EAAE,oBAAoB,GAAG,8BAA8B;IAI/F;;;;;;;;;;OAUG;IACH,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO;IAInF;;;;;;;OAOG;IACH,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE;QAAE,eAAe,EAAE,oBAAoB,CAAA;KAAE,GAAG,WAAW,CAAC,MAAM,CAAC;IAY1F;;;;;;;;;;OAUG;IACH,gBAAgB,CACd,IAAI,EAAE,IAAI,EACV,WAAW,EAAE,aAAa,CAAC,MAAM,CAAC,EAClC,OAAO,EAAE;QAAE,eAAe,EAAE,oBAAoB,CAAA;KAAE,GACjD,eAAe;CAOnB"}
@@ -0,0 +1,179 @@
1
+ import { ShBridgeError, ShParseError, parseSync, visitorKeys as shellVisitorKeys } from 'sh-ast';
2
+ import { matchesSelectorClass as matchesShellSelectorClass } from './selector-classes.js';
3
+ import { ShellSourceCode } from './shell-source-code.js';
4
+ import { normalizeShellLanguageOptions, validateShellLanguageOptions, } from './shell-language-options.js';
5
+ /**
6
+ * `sh-ast`'s `visitorKeys` is deep-frozen and typed with
7
+ * `readonly string[]` arrays (a shared, immutable table safe to hand to any
8
+ * number of consumers). `@eslint/core`'s `Language.visitorKeys` types this
9
+ * property as mutable `Record<string, string[]>` for historical reasons,
10
+ * even though ESLint itself only ever reads it. Rather than cast away the
11
+ * mismatch, build one genuinely mutable (if still logically frozen-in-spirit)
12
+ * copy at module load — a one-time, small-object cost.
13
+ */
14
+ const mutableShellVisitorKeys = Object.fromEntries(Object.entries(shellVisitorKeys).map(([type, keys]) => [type, [...keys]]));
15
+ /**
16
+ * Escapes every regex-special character in `value` so it can be embedded
17
+ * literally inside a `RegExp` source string. Used by
18
+ * {@link stripPositionPrefix} to build a filename-specific prefix pattern
19
+ * without the filename's own characters (e.g. `.` in `b.sh`, or any
20
+ * sandbox-path characters) being interpreted as regex metacharacters.
21
+ */
22
+ function escapeRegExp(value) {
23
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
24
+ }
25
+ /**
26
+ * Strips mvdan/sh's own `<filename>:<line>:<col>: ` prefix from a
27
+ * `ShParseError.message`, if present. mvdan/sh's `.message` is its own
28
+ * formatted error string (`sh-ast`'s `parse.ts` passes
29
+ * `result.parseError.message` to `ShParseError` verbatim — see that file's
30
+ * doc comment), which already embeds the same `filename:line:col` ESLint's
31
+ * own fatal-message header prints from `FileError.line`/`.column` — see
32
+ * issue #36. Left un-stripped, ESLint's console formatter (and any other
33
+ * consumer that reads `message.message`) shows the position twice and
34
+ * exposes the sandbox-absolute `file.path` a second time inside the message
35
+ * body.
36
+ *
37
+ * Only strips a prefix that names *this exact* `filename` (escaped, so the
38
+ * filename's own characters can't be misread as regex syntax), matched
39
+ * against `\d+:\d+` for line/column — mvdan/sh's message uses its own raw
40
+ * byte column, which is not always numerically identical to
41
+ * `ShParseError.column` (UTF-16-converted; see `ShParseErrorInfo.column`'s
42
+ * doc comment). A message that doesn't start with that exact prefix (e.g.
43
+ * mvdan/sh changes its format, or `filename` doesn't appear verbatim) is
44
+ * returned unchanged rather than mangled.
45
+ */
46
+ function stripPositionPrefix(message, filename) {
47
+ const prefixPattern = new RegExp(`^${escapeRegExp(filename)}:\\d+:\\d+:\\s*`);
48
+ return message.replace(prefixPattern, '');
49
+ }
50
+ /**
51
+ * Converts a caught `parseSync` failure into the `FileError` ESLint expects
52
+ * from a `NotOkParseResult`. A `ShParseError` carries mvdan/sh's *real*
53
+ * reported position (`.line`/`.column`, already UTF-16 — see
54
+ * `sh-ast`'s `ShParseError` doc comment) and is used verbatim;
55
+ * this is the fix for the retired spike's known flaw (`spike/language.mjs`
56
+ * hardcoded `{ line: 1, column: 1 }` for every parse failure, discarding the
57
+ * position mvdan/sh actually reported). The message text has its duplicated
58
+ * `filename:line:col:` prefix stripped (see {@link stripPositionPrefix}) —
59
+ * ESLint already renders that position from `.line`/`.column` in its own
60
+ * fatal-message header, so embedding it again in the message body is both
61
+ * noisy and, for absolute `file.path` values, an unwanted disclosure of the
62
+ * sandbox-absolute path (issue #36).
63
+ *
64
+ * Any other error reaching here (`ShInvalidDialectError` — unreachable in
65
+ * practice, since `validateLanguageOptions` rejects a bad dialect before
66
+ * ESLint ever calls `parse()`; or `ShBridgeInternalError` — a shim contract
67
+ * violation) has no real source position to report, so `1:1` is used as an
68
+ * explicit, honest fallback for the "no position available" case — not a
69
+ * stand-in for an actual parse error's position.
70
+ */
71
+ function toFileError(error) {
72
+ if (error instanceof ShParseError) {
73
+ return {
74
+ message: stripPositionPrefix(error.message, error.filename),
75
+ line: error.line,
76
+ column: error.column,
77
+ };
78
+ }
79
+ const message = error instanceof ShBridgeError || error instanceof Error ? error.message : String(error);
80
+ return { message, line: 1, column: 1 };
81
+ }
82
+ /**
83
+ * `ShellLanguage` — the `@eslint/core` `Language` implementation for shell
84
+ * scripts (design/ARCHITECTURE.md, "SourceCode and Language objects";
85
+ * design/PACKAGES.md, `@eslint-sh/eslint-plugin`). Registered by the plugin
86
+ * as `languages.sh`, so consumers select it via `language: "sh/sh"`.
87
+ *
88
+ * Implements `matchesSelectorClass` (the six launch esquery selector
89
+ * classes — see `./selector-classes.js`), so rules and end users can write
90
+ * class-based selectors (`:command`, `:loop`, `:conditional`, `:expansion`,
91
+ * `:quoted`, `:function`) against shell trees. The `SourceCode` directive
92
+ * members (`getDisableDirectives`, `getInlineConfigNodes`,
93
+ * `applyInlineConfig`) are implemented on `ShellSourceCode` itself; see
94
+ * shell-source-code.ts.
95
+ *
96
+ * @public
97
+ */
98
+ export class ShellLanguage {
99
+ fileType = 'text';
100
+ lineStart = 1;
101
+ columnStart = 1;
102
+ nodeTypeKey = 'type';
103
+ visitorKeys = mutableShellVisitorKeys;
104
+ defaultLanguageOptions = { dialect: 'bash' };
105
+ /**
106
+ * Validates `languageOptions.dialect` (must be one of the five supported
107
+ * dialects, if present), `languageOptions.shellVersion` (must be a
108
+ * string matching the `MAJOR[.MINOR]` numeric shape, if present), and
109
+ * `languageOptions.directivePrefix` (must be a valid directive-comment
110
+ * label, if present). Unknown keys are silently ignored, per the
111
+ * `Language` contract. See `shell-language-options.ts`'s
112
+ * `validateShellLanguageOptions` for the full rationale.
113
+ */
114
+ validateLanguageOptions(languageOptions) {
115
+ validateShellLanguageOptions(languageOptions);
116
+ }
117
+ /**
118
+ * Resolves `dialect` to its default (`"bash"`) when unset and attaches a
119
+ * `toJSON()` method with a fixed key order, so that ESLint's config
120
+ * caching (keyed off serializing `languageOptions`) is stable regardless
121
+ * of the key order a user's config happened to use. See
122
+ * `shell-language-options.ts`'s `normalizeShellLanguageOptions`.
123
+ */
124
+ normalizeLanguageOptions(languageOptions) {
125
+ return normalizeShellLanguageOptions(languageOptions);
126
+ }
127
+ /**
128
+ * `@eslint/core`'s esquery hook: lets a selector's leading `:` "class"
129
+ * (e.g. `:command` in `CallExpr[args.0.parts.0.value='jq']` isn't even
130
+ * needed there, but `:loop :command` is) match a whole family of node
131
+ * types instead of one literal type name. Delegates to
132
+ * `./selector-classes.js`'s `SELECTOR_CLASS_NODE_TYPES` table for the six
133
+ * launch classes (design/ARCHITECTURE.md, "SourceCode and Language
134
+ * objects"); `ancestry` is unused today because none of the six launch
135
+ * classes need ancestor context, but the parameter is accepted to match
136
+ * `Language.matchesSelectorClass`'s signature exactly.
137
+ */
138
+ matchesSelectorClass(className, node, _ancestry) {
139
+ return matchesShellSelectorClass(className, node);
140
+ }
141
+ /**
142
+ * Parses `file.body` (guaranteed to be a `string`, since `fileType` is
143
+ * `"text"`) via `sh-ast`'s `parseSync`, mapping the result to
144
+ * the `{ok: true, ast}` / `{ok: false, errors}` shape `Language.parse` is
145
+ * required to return. Never throws — parse failures are reported as
146
+ * fatal lint messages, per the `Language` contract
147
+ * (design/ARCHITECTURE.md, "Parse errors and dialects").
148
+ */
149
+ parse(file, context) {
150
+ const text = file.body;
151
+ const dialect = context.languageOptions.dialect ?? 'bash';
152
+ try {
153
+ const ast = parseSync(text, { dialect, filename: file.path });
154
+ return { ok: true, ast };
155
+ }
156
+ catch (error) {
157
+ return { ok: false, errors: [toFileError(error)] };
158
+ }
159
+ }
160
+ /**
161
+ * Creates the `ShellSourceCode` ESLint uses to run rules against `ast`.
162
+ * `context.languageOptions` is ESLint's already-normalized copy (see
163
+ * `packages/eslint-plugin/test/language-options.test.ts` and
164
+ * `eslint`'s `Config#finalize`, which replaces `languageOptions` with
165
+ * `normalizeLanguageOptions`'s return value before either `parse` or
166
+ * `createSourceCode` is called) — `directivePrefix` is therefore always
167
+ * present, but `?? 'eslint'` stays as an explicit, honest fallback for
168
+ * direct construction (e.g. tests that skip the `Language` normalization
169
+ * step), mirroring `parse()`'s `dialect` default above.
170
+ */
171
+ createSourceCode(file, parseResult, context) {
172
+ return new ShellSourceCode({
173
+ text: file.body,
174
+ ast: parseResult.ast,
175
+ directivePrefix: context.languageOptions.directivePrefix ?? 'eslint',
176
+ });
177
+ }
178
+ }
179
+ //# sourceMappingURL=shell-language.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-language.js","sourceRoot":"","sources":["../../src/language/shell-language.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,121 @@
1
+ import { TextSourceCodeBase } from '@eslint/plugin-kit';
2
+ import type { ShFile, ShNode, ShNodes } from 'sh-ast';
3
+ import type { Directive as IDirective, FileProblem, RulesConfig, SourceLocation, TraversalStep } from '@eslint/core';
4
+ import type { ShellLanguageOptions } from './shell-language-options.js';
5
+ /**
6
+ * `ShellSourceCode` — the `SourceCode` object ESLint uses to work with a
7
+ * parsed shell file (design/ARCHITECTURE.md, "SourceCode and Language
8
+ * objects"). Extends `@eslint/plugin-kit`'s `TextSourceCodeBase`, which
9
+ * already implements `getLoc`/`getRange`/`getText`/`lines` (both by reading
10
+ * the normalized node's own `loc`/`range` fields — see
11
+ * `sh-ast`'s "Normalized node shape"), `getLocFromIndex`, and
12
+ * `getIndexFromLoc`; this class adds the two members the base class
13
+ * deliberately leaves unimplemented: `traverse()` (base throws "Not
14
+ * implemented") and `getParent()` (base throws "Not implemented", and
15
+ * `getAncestors()` — already implemented generically in terms of
16
+ * `getParent()` — falls out for free).
17
+ *
18
+ * Directive support (`getDisableDirectives`, `getInlineConfigNodes`,
19
+ * `applyInlineConfig`) recognizes all four disable-comment forms
20
+ * (`<prefix>-disable`, `<prefix>-enable`, `<prefix>-disable-line`,
21
+ * `<prefix>-disable-next-line`) plus whole-file inline rule config
22
+ * (bare `<prefix> <rule-config>`), sourced from `Comment` nodes gathered via
23
+ * `sh-ast`'s structural `walk()` and parsed with
24
+ * `@eslint/plugin-kit`'s `ConfigCommentParser` — the same building blocks
25
+ * `@eslint/json`'s `JSONSourceCode` uses, generalized so the `<prefix>` is
26
+ * configurable (default `"eslint"`; see `ShellLanguageOptions.directivePrefix`)
27
+ * rather than hardcoded to `"eslint"`.
28
+ *
29
+ * Selector-class support (`matchesSelectorClass`, the six launch esquery
30
+ * classes) is implemented on `ShellLanguage` — see `shell-language.ts` and
31
+ * `selector-classes.ts`.
32
+ *
33
+ * @public
34
+ */
35
+ export declare class ShellSourceCode extends TextSourceCodeBase<{
36
+ LangOptions: ShellLanguageOptions;
37
+ RootNode: ShFile;
38
+ SyntaxElementWithLoc: ShNode;
39
+ ConfigNode: ShNodes.ShCommentNode;
40
+ }> {
41
+ #private;
42
+ constructor(options: {
43
+ text: string;
44
+ ast: ShFile;
45
+ directivePrefix?: string;
46
+ });
47
+ /**
48
+ * Returns the parent of `node`, or `undefined` for the root `File` node
49
+ * (or any node visited before `traverse()` has run once). ESLint always
50
+ * calls `traverse()` before any rule can call `getParent`/`getAncestors`,
51
+ * so in practice the map is always populated by the time a rule observes
52
+ * it.
53
+ */
54
+ getParent(node: ShNode): ShNode | undefined;
55
+ /**
56
+ * Traverses the AST pre-order, yielding a `VisitNodeStep` on node enter
57
+ * (`phase: 1`) and another on node exit (`phase: 2`) for every node —
58
+ * exactly the two-phase shape `@eslint/json`/`@eslint/css` produce, which
59
+ * is what lets rule listeners use `NodeType` and `NodeType:exit` esquery
60
+ * selectors. Children are discovered via `sh-ast`'s generated,
61
+ * frozen `visitorKeys` table (per node `type`) — **not** structurally
62
+ * (contrast `sh-ast`'s own `walk()`, which deliberately walks
63
+ * structurally so its correctness never depends on that table) — because
64
+ * ESLint's traversal contract is defined in terms of a language's
65
+ * declared `visitorKeys`, and this class's whole job is to honor that
66
+ * contract for shell ASTs.
67
+ */
68
+ traverse(): Iterable<TraversalStep>;
69
+ /**
70
+ * Returns every `Comment` node whose text is a directive or inline-config
71
+ * candidate for this source code's `directivePrefix` — the pool both
72
+ * {@link ShellSourceCode.getDisableDirectives} and {@link ShellSourceCode.applyInlineConfig} draw from,
73
+ * and the array ESLint core itself asks for directly (to warn "has no
74
+ * effect" when `linterOptions.noInlineConfig` is set — see
75
+ * `eslint`'s `lib/linter/linter.js`, the `warnInlineConfig` branch).
76
+ */
77
+ getInlineConfigNodes(): ShNodes.ShCommentNode[];
78
+ /**
79
+ * Returns the disable/enable directives found among
80
+ * {@link ShellSourceCode.getInlineConfigNodes}'s comments, recognizing all four forms —
81
+ * `<prefix>-disable`, `<prefix>-enable`, `<prefix>-disable-line`,
82
+ * `<prefix>-disable-next-line` — with `-- justification` capture, per the
83
+ * `@eslint/core` `Directive` contract. Mirrors `@eslint/json`'s
84
+ * `JSONSourceCode.getDisableDirectives` (verified against its published
85
+ * source as the named ecosystem precedent), generalized to a configurable
86
+ * prefix instead of the hardcoded `"eslint"` label.
87
+ *
88
+ * Unlike `JSONSourceCode` (whose block comments can span multiple
89
+ * physical lines, making `<prefix>-disable-line` spanning lines a real,
90
+ * reportable problem), mvdan/sh's `#` line comments cannot contain an
91
+ * embedded newline — the parser always terminates a comment at end of
92
+ * line — so that particular problem can never arise here and is not
93
+ * checked for.
94
+ */
95
+ getDisableDirectives(): {
96
+ directives: IDirective[];
97
+ problems: FileProblem[];
98
+ };
99
+ /**
100
+ * Returns the whole-file inline rule configuration found among
101
+ * {@link ShellSourceCode.getInlineConfigNodes}'s comments — a bare `<prefix> <rule-config>`
102
+ * comment (e.g. `# eslint sh/rule-a: "error"`), parsed with
103
+ * `ConfigCommentParser.parseJSONLikeConfig`. Mirrors `@eslint/json`'s
104
+ * `JSONSourceCode.applyInlineConfig`. This is the surface
105
+ * `linterOptions.noInlineConfig: true` suppresses (ESLint core skips
106
+ * calling this method — and `getDisableDirectives`, together, since both
107
+ * are gated by the same flag in `eslint`'s own `linter.js` — and instead
108
+ * calls `getInlineConfigNodes` to emit a "has no effect" warning per
109
+ * comment; see design/ARCHITECTURE.md, "SourceCode and Language objects").
110
+ */
111
+ applyInlineConfig(): {
112
+ configs: {
113
+ config: {
114
+ rules: RulesConfig;
115
+ };
116
+ loc: SourceLocation;
117
+ }[];
118
+ problems: FileProblem[];
119
+ };
120
+ }
121
+ //# sourceMappingURL=shell-source-code.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-source-code.d.ts","sourceRoot":"","sources":["../../src/language/shell-source-code.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,kBAAkB,EAEnB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtD,OAAO,KAAK,EACV,SAAS,IAAI,UAAU,EAEvB,WAAW,EACX,WAAW,EACX,cAAc,EACd,aAAa,EACd,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAuExE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,eAAgB,SAAQ,kBAAkB,CAAC;IACtD,WAAW,EAAE,oBAAoB,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,UAAU,EAAE,OAAO,CAAC,aAAa,CAAC;CACnC,CAAC;;gBAwDY,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE;IAU5E;;;;;;OAMG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI3C;;;;;;;;;;;;OAYG;IACH,QAAQ,IAAI,QAAQ,CAAC,aAAa,CAAC;IAoEnC;;;;;;;OAOG;IACH,oBAAoB,IAAI,OAAO,CAAC,aAAa,EAAE;IAO/C;;;;;;;;;;;;;;;;OAgBG;IACH,oBAAoB,IAAI;QAAE,UAAU,EAAE,UAAU,EAAE,CAAC;QAAC,QAAQ,EAAE,WAAW,EAAE,CAAA;KAAE;IA0B7E;;;;;;;;;;;OAWG;IACH,iBAAiB,IAAI;QACnB,OAAO,EAAE;YAAE,MAAM,EAAE;gBAAE,KAAK,EAAE,WAAW,CAAA;aAAE,CAAC;YAAC,GAAG,EAAE,cAAc,CAAA;SAAE,EAAE,CAAC;QACnE,QAAQ,EAAE,WAAW,EAAE,CAAC;KACzB;CAqBF"}