@eslint-sh/eslint-plugin 0.1.1 → 0.3.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 CHANGED
@@ -42,6 +42,10 @@ ShellLanguage }, rules: {} }`. `meta.version` always matches this package's `pac
42
42
  prefix directive/inline-config comments must start with — e.g. the default `"eslint"` recognizes
43
43
  `# eslint-disable-next-line ...`; consumers who already overload `# eslint-*` comments for
44
44
  something else can configure a different prefix (e.g. `directivePrefix: 'shellcheck-sh'`).
45
+ - `resolveWord` — re-export of `sh-ast`'s word-resolution helper, so a project that installs only
46
+ this package can write custom rules that resolve static shell words (e.g. the `no-rm-rf` example
47
+ in the [custom-rule tutorial](./docs/custom-rule-tutorial.md)) without a separate `sh-ast`
48
+ install.
45
49
 
46
50
  Parse failures surface as fatal lint messages at mvdan/sh's real reported position (never a 1:1
47
51
  placeholder) — see `ShParseError` in `sh-ast`.
@@ -4,6 +4,7 @@ import type { FileProblem } from '@eslint/core';
4
4
  import type { Language } from '@eslint/core';
5
5
  import type { OkParseResult } from '@eslint/core';
6
6
  import type { ParseResult } from '@eslint/core';
7
+ import { resolveWord as resolveWord_2 } from 'sh-ast/analyze';
7
8
  import type { RulesConfig } from '@eslint/core';
8
9
  import type { ShellDialect } from 'sh-ast';
9
10
  import type { ShFile } from 'sh-ast';
@@ -62,6 +63,20 @@ export declare interface NormalizedShellLanguageOptions extends Record<string, u
62
63
  toJSON(): ShellLanguageOptionsJSON;
63
64
  }
64
65
 
66
+ /**
67
+ * Re-exported from `sh-ast/analyze` so a project that installs only
68
+ * `@eslint-sh/eslint-plugin` can write custom rules that resolve static
69
+ * shell words (e.g. `docs/custom-rule-tutorial.md`'s `no-rm-rf` example)
70
+ * without a separate `sh-ast` install or relying on npm hoisting to make
71
+ * `sh-ast` resolvable from the consumer's own `node_modules`. Declared
72
+ * locally (instead of a bare re-export statement) so API Extractor
73
+ * attaches this doc comment and its public release tag to the committed
74
+ * api-report instead of emitting it as an untagged export.
75
+ *
76
+ * @public
77
+ */
78
+ export declare const resolveWord: typeof resolveWord_2;
79
+
65
80
  /**
66
81
  * `ShellLanguage` — the `@eslint/core` `Language` implementation for shell
67
82
  * scripts (design/ARCHITECTURE.md, "SourceCode and Language objects";
package/dist/index.d.ts CHANGED
@@ -1,7 +1,21 @@
1
+ import { resolveWord as shAstResolveWord } from 'sh-ast/analyze';
1
2
  import { ShellLanguage } from './language/shell-language.js';
2
3
  export { ShellLanguage } from './language/shell-language.js';
3
4
  export { ShellSourceCode } from './language/shell-source-code.js';
4
5
  export type { NormalizedShellLanguageOptions, ShellLanguageOptions, ShellLanguageOptionsJSON, } from './language/shell-language-options.js';
6
+ /**
7
+ * Re-exported from `sh-ast/analyze` so a project that installs only
8
+ * `@eslint-sh/eslint-plugin` can write custom rules that resolve static
9
+ * shell words (e.g. `docs/custom-rule-tutorial.md`'s `no-rm-rf` example)
10
+ * without a separate `sh-ast` install or relying on npm hoisting to make
11
+ * `sh-ast` resolvable from the consumer's own `node_modules`. Declared
12
+ * locally (instead of a bare re-export statement) so API Extractor
13
+ * attaches this doc comment and its public release tag to the committed
14
+ * api-report instead of emitting it as an untagged export.
15
+ *
16
+ * @public
17
+ */
18
+ export declare const resolveWord: typeof shAstResolveWord;
5
19
  /**
6
20
  * The `@eslint-sh/eslint-plugin` plugin object (design/PACKAGES.md,
7
21
  * `@eslint-sh/eslint-plugin`): `languages.sh` provides `ShellLanguage` /
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAG7D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,YAAY,EACV,8BAA8B,EAC9B,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,sCAAsC,CAAC;AAE9C;;;;;;;;;;;;;;;;GAgBG;;;;;;;;;;;;AACH,wBAUmB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,IAAI,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAG7D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,YAAY,EACV,8BAA8B,EAC9B,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,sCAAsC,CAAC;AAE9C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW,yBAAmB,CAAC;AAE5C;;;;;;;;;;;;;;;;GAgBG;;;;;;;;;;;;AACH,wBAUmB"}
package/dist/index.js CHANGED
@@ -1,7 +1,21 @@
1
+ import { resolveWord as shAstResolveWord } from 'sh-ast/analyze';
1
2
  import { ShellLanguage } from './language/shell-language.js';
2
3
  import { readPackageVersion } from './version.js';
3
4
  export { ShellLanguage } from './language/shell-language.js';
4
5
  export { ShellSourceCode } from './language/shell-source-code.js';
6
+ /**
7
+ * Re-exported from `sh-ast/analyze` so a project that installs only
8
+ * `@eslint-sh/eslint-plugin` can write custom rules that resolve static
9
+ * shell words (e.g. `docs/custom-rule-tutorial.md`'s `no-rm-rf` example)
10
+ * without a separate `sh-ast` install or relying on npm hoisting to make
11
+ * `sh-ast` resolvable from the consumer's own `node_modules`. Declared
12
+ * locally (instead of a bare re-export statement) so API Extractor
13
+ * attaches this doc comment and its public release tag to the committed
14
+ * api-report instead of emitting it as an untagged export.
15
+ *
16
+ * @public
17
+ */
18
+ export const resolveWord = shAstResolveWord;
5
19
  /**
6
20
  * The `@eslint-sh/eslint-plugin` plugin object (design/PACKAGES.md,
7
21
  * `@eslint-sh/eslint-plugin`): `languages.sh` provides `ShellLanguage` /
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAOlE;;;;;;;;;;;;;;;;GAgBG;AACH,eAAe;IACb,IAAI,EAAE;QACJ,IAAI,EAAE,0BAA0B;QAChC,OAAO,EAAE,kBAAkB,EAAE;QAC7B,SAAS,EAAE,IAAI;KAChB;IACD,SAAS,EAAE;QACT,EAAE,EAAE,IAAI,aAAa,EAAE;KACxB;IACD,KAAK,EAAE,EAAE;CACO,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,IAAI,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAOlE;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,gBAAgB,CAAC;AAE5C;;;;;;;;;;;;;;;;GAgBG;AACH,eAAe;IACb,IAAI,EAAE;QACJ,IAAI,EAAE,0BAA0B;QAChC,OAAO,EAAE,kBAAkB,EAAE;QAC7B,SAAS,EAAE,IAAI;KAChB;IACD,SAAS,EAAE;QACT,EAAE,EAAE,IAAI,aAAa,EAAE;KACxB;IACD,KAAK,EAAE,EAAE;CACO,CAAC"}
@@ -1,4 +1,4 @@
1
- import { ShBridgeError, ShParseError, parseSync, visitorKeys as shellVisitorKeys } from 'sh-ast';
1
+ import { ShAstError, ShParseError, parseSync, visitorKeys as shellVisitorKeys } from 'sh-ast';
2
2
  import { matchesSelectorClass as matchesShellSelectorClass } from './selector-classes.js';
3
3
  import { ShellSourceCode } from './shell-source-code.js';
4
4
  import { normalizeShellLanguageOptions, validateShellLanguageOptions, } from './shell-language-options.js';
@@ -63,7 +63,7 @@ function stripPositionPrefix(message, filename) {
63
63
  *
64
64
  * Any other error reaching here (`ShInvalidDialectError` — unreachable in
65
65
  * practice, since `validateLanguageOptions` rejects a bad dialect before
66
- * ESLint ever calls `parse()`; or `ShBridgeInternalError` — a shim contract
66
+ * ESLint ever calls `parse()`; or `ShInternalError` — a shim contract
67
67
  * violation) has no real source position to report, so `1:1` is used as an
68
68
  * explicit, honest fallback for the "no position available" case — not a
69
69
  * stand-in for an actual parse error's position.
@@ -76,7 +76,7 @@ function toFileError(error) {
76
76
  column: error.column,
77
77
  };
78
78
  }
79
- const message = error instanceof ShBridgeError || error instanceof Error ? error.message : String(error);
79
+ const message = error instanceof ShAstError || error instanceof Error ? error.message : String(error);
80
80
  return { message, line: 1, column: 1 };
81
81
  }
82
82
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"shell-language.js","sourceRoot":"","sources":["../../src/language/shell-language.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shell-language.js","sourceRoot":"","sources":["../../src/language/shell-language.ts"],"names":[],"mappings":"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"}
@@ -7,11 +7,16 @@ wiring — `plugins`, `rules`, `language` — needed to run it.
7
7
 
8
8
  Every code block on this page is the literal, shipped file it names — not a paraphrase — and both
9
9
  files are executed verbatim in CI (`test/custom-rule-tutorial.test.ts` imports
10
- [`examples/no-rm-rf-rule.js`](./examples/no-rm-rf-rule.js) and
10
+ [`examples/no-rm-rf-rule.mjs`](./examples/no-rm-rf-rule.mjs) and
11
11
  [`examples/eslint.config.mjs`](./examples/eslint.config.mjs) directly and runs them through a real
12
12
  `eslint` `Linter.verify()`): the rule fires on a fixture script and does not fire on a clean one,
13
13
  so this tutorial cannot silently drift from working code. Both example files ship with this
14
- package (`docs/examples/`), so you can copy them into your own project as-is.
14
+ package (`docs/examples/`), so you can copy them into your own project as-is. Both use the `.mjs`
15
+ extension deliberately: they are ES modules (`import`/`export`), and a default `npm init -y`
16
+ project is CommonJS, so a plain `.js` extension would make Node — and ESLint's own config
17
+ loader — try to parse them as CommonJS and fail with `SyntaxError: Cannot use import statement
18
+ outside a module`. `.mjs` marks each file as an ES module regardless of the surrounding project's
19
+ `"type"` field.
15
20
 
16
21
  ## 1. Pick a selector from the cookbook
17
22
 
@@ -22,9 +27,9 @@ flag?) the same way a hand-written rule narrows any other selector-first ESLint
22
27
 
23
28
  ## 2. Write the rule module
24
29
 
25
- `docs/examples/no-rm-rf-rule.js`:
30
+ `docs/examples/no-rm-rf-rule.mjs`:
26
31
 
27
- ```js
32
+ ```mjs
28
33
  /**
29
34
  * The complete "flag `rm -rf`" custom rule from the custom-rule tutorial
30
35
  * (`../custom-rule-tutorial.md`). Plain, directly-runnable JavaScript (no
@@ -41,18 +46,23 @@ flag?) the same way a hand-written rule narrows any other selector-first ESLint
41
46
  * here the selector does the node-type narrowing, `create()` only does the
42
47
  * domain-specific check).
43
48
  *
44
- * Flag words are resolved with `sh-ast/analyze`'s `resolveWord` rather than
45
- * a hand-rolled scan: a word is only ever treated as the literal text
49
+ * Flag words are resolved with `@eslint-sh/eslint-plugin`'s re-exported
50
+ * `resolveWord` (originally from `sh-ast/analyze`) rather than a
51
+ * hand-rolled scan: a word is only ever treated as the literal text
46
52
  * `-rf`/`-fr` when it is *statically* that string (bare or quoted literal
47
53
  * concatenations); a word containing a `ParamExp`/`CmdSubst`/`ArithmExp`/
48
54
  * `ProcSubst` resolves to `null` ("unknown") rather than being silently
49
55
  * erased into a static match or a static non-match — the same static-word
50
- * contract `design/PACKAGES.md` documents for `resolveWordText`.
56
+ * contract `design/PACKAGES.md` documents for `resolveWordText`. Importing
57
+ * it from the plugin package (rather than `sh-ast/analyze` directly) means
58
+ * a project that installs only `@eslint-sh/eslint-plugin` has everything
59
+ * this rule needs, with no separate `sh-ast` install or npm-hoisting
60
+ * reliance.
51
61
  *
52
62
  * @see ../custom-rule-tutorial.md — the tutorial this rule backs
53
63
  * @see ../selectors.md — the `:command` launch class
54
64
  */
55
- import { resolveWord } from 'sh-ast/analyze';
65
+ import { resolveWord } from '@eslint-sh/eslint-plugin';
56
66
 
57
67
  const noRmRf = {
58
68
  meta: {
@@ -107,8 +117,9 @@ A few things worth calling out:
107
117
  classes](./selectors.md#the-six-launch-classes) — not a bare node-type name. ESLint's
108
118
  `esquery`-based rule-listener matching (via `ShellLanguage.matchesSelectorClass`) treats it
109
119
  exactly like any other selector key such as `CallExpr` or `CallExpr[args.0.parts.0.value='jq']`.
110
- - `resolveWord(arg, { context: 'command-argument' })` — from `sh-ast/analyze` — is the correct way
111
- to ask "is this word statically the string X": it returns `{ static: true, text }` for a bare or
120
+ - `resolveWord(arg, { context: 'command-argument' })` — re-exported from `@eslint-sh/eslint-plugin`
121
+ (originally `sh-ast/analyze`) — is the correct way to ask "is this word statically the string X":
122
+ it returns `{ static: true, text }` for a bare or
112
123
  quoted literal (`rm`, `'r'm`, `"-rf"` all resolve), and `{ static: false, reason }` the moment any
113
124
  part of the word depends on a `ParamExp`/`CmdSubst`/`ArithmExp`/`ProcSubst` — never inventing a
114
125
  value for a dynamic word.
@@ -121,7 +132,7 @@ A few things worth calling out:
121
132
 
122
133
  `docs/examples/eslint.config.mjs`:
123
134
 
124
- ```js
135
+ ```mjs
125
136
  // eslint.config.mjs
126
137
  //
127
138
  // The custom-rule tutorial's exact flat-config wiring
@@ -130,7 +141,7 @@ A few things worth calling out:
130
141
  // `eslint` `Linter.verify()` — not a reconstruction of this shape — so the
131
142
  // documented wiring is genuinely tested.
132
143
  import sh from '@eslint-sh/eslint-plugin'; // or the umbrella: import sh from 'eslint-sh'
133
- import noRmRf from './no-rm-rf-rule.js';
144
+ import noRmRf from './no-rm-rf-rule.mjs';
134
145
 
135
146
  export default [
136
147
  {
@@ -6,7 +6,7 @@
6
6
  // `eslint` `Linter.verify()` — not a reconstruction of this shape — so the
7
7
  // documented wiring is genuinely tested.
8
8
  import sh from '@eslint-sh/eslint-plugin'; // or the umbrella: import sh from 'eslint-sh'
9
- import noRmRf from './no-rm-rf-rule.js';
9
+ import noRmRf from './no-rm-rf-rule.mjs';
10
10
 
11
11
  export default [
12
12
  {
@@ -14,18 +14,23 @@
14
14
  * here the selector does the node-type narrowing, `create()` only does the
15
15
  * domain-specific check).
16
16
  *
17
- * Flag words are resolved with `sh-ast/analyze`'s `resolveWord` rather than
18
- * a hand-rolled scan: a word is only ever treated as the literal text
17
+ * Flag words are resolved with `@eslint-sh/eslint-plugin`'s re-exported
18
+ * `resolveWord` (originally from `sh-ast/analyze`) rather than a
19
+ * hand-rolled scan: a word is only ever treated as the literal text
19
20
  * `-rf`/`-fr` when it is *statically* that string (bare or quoted literal
20
21
  * concatenations); a word containing a `ParamExp`/`CmdSubst`/`ArithmExp`/
21
22
  * `ProcSubst` resolves to `null` ("unknown") rather than being silently
22
23
  * erased into a static match or a static non-match — the same static-word
23
- * contract `design/PACKAGES.md` documents for `resolveWordText`.
24
+ * contract `design/PACKAGES.md` documents for `resolveWordText`. Importing
25
+ * it from the plugin package (rather than `sh-ast/analyze` directly) means
26
+ * a project that installs only `@eslint-sh/eslint-plugin` has everything
27
+ * this rule needs, with no separate `sh-ast` install or npm-hoisting
28
+ * reliance.
24
29
  *
25
30
  * @see ../custom-rule-tutorial.md — the tutorial this rule backs
26
31
  * @see ../selectors.md — the `:command` launch class
27
32
  */
28
- import { resolveWord } from 'sh-ast/analyze';
33
+ import { resolveWord } from '@eslint-sh/eslint-plugin';
29
34
 
30
35
  const noRmRf = {
31
36
  meta: {
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@eslint-sh/eslint-plugin",
3
3
  "description": "Shell script parsing and syntax-error reporting for ESLint, with custom-rule authoring over a typed shell AST, backed by sh-ast.",
4
- "version": "0.1.1",
4
+ "version": "0.3.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.1.0"
9
+ "sh-ast": "^0.3.0"
10
10
  },
11
11
  "devDependencies": {
12
12
  "eslint": "^10.7.0"