@unotest/eslint-plugin 0.18.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/CHANGELOG.md ADDED
@@ -0,0 +1,22 @@
1
+ # @unotest/eslint-plugin
2
+
3
+ ## [0.18.0] - 2026-08-23
4
+
5
+ ### Minor Changes
6
+
7
+ - a088bac: New package `@unotest/eslint-plugin` — unotest DSL scenarios as
8
+ first-class ESLint citizens. One rule (`@unotest/dsl`) delegates every
9
+ check to the validator inside the installed `@unotest/web` (parser,
10
+ vocabulary, linter rules, `linter.rules` severities from
11
+ unotest.config), so `npx eslint unotest/e2e` and `unotest-web lint`
12
+ report identical diagnostics; a new validator rule needs zero plugin
13
+ changes. `configs.recommended()` (async factory, flat config) wires
14
+ `unotest/e2e/**/*.js`, turns `no-undef` off (implicit globals are the
15
+ DSL's design; unknown names are the engine's job) and declares globals
16
+ for the vocabulary, your helpers and your `.env`/`.secrets` variable
17
+ names.
18
+
19
+ `@unotest/web`: the `@unotest/web/dsl` subpath now exports
20
+ `loadLintProjectContext()` (config severities + helper discovery +
21
+ variable names) and `linterRulesToSeverity()` — the same loader the CLI
22
+ `lint` command now uses, so the two frontends cannot drift.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ivan Volkov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # @unotest/eslint-plugin
2
+
3
+ ESLint plugin for [unotest web](https://docs.unotest.com) DSL scenarios.
4
+
5
+ One rule — `@unotest/dsl` — runs your scenarios through the exact same
6
+ validator as `unotest-web lint` and the viewer's live editor: parser,
7
+ vocabulary checks, linter rules, and the severity overrides from your
8
+ `unotest.config.mjs`. ESLint and the CLI can never disagree, and a new
9
+ validator rule needs zero plugin updates.
10
+
11
+ ## Setup
12
+
13
+ ```sh
14
+ npm install --save-dev eslint @unotest/eslint-plugin
15
+ ```
16
+
17
+ `@unotest/web` must be installed in the project (it usually already is —
18
+ the plugin delegates all checking to it).
19
+
20
+ ```js
21
+ // eslint.config.mjs
22
+ import unotest from "@unotest/eslint-plugin";
23
+
24
+ export default [...(await unotest.configs.recommended())];
25
+ ```
26
+
27
+ The factory is async because it loads your `unotest.config.{js,mjs,ts}`
28
+ (severity overrides), discovers helper names from `unotest/e2e/_helpers/`
29
+ and your variable names from `unotest/.env` / `.secrets` (names only —
30
+ values are never read into the config).
31
+
32
+ Then:
33
+
34
+ ```sh
35
+ npx eslint unotest/e2e
36
+ ```
37
+
38
+ ## What `recommended` wires
39
+
40
+ - `files: ["unotest/e2e/**/*.js"]` — scenarios and helpers only.
41
+ - `@unotest/dsl: "error"` — every engine diagnostic is reported at its
42
+ line/column; the engine's own rule id and severity ride in the message
43
+ (`[warning lint:pause-explicit] …`), since an ESLint rule has a single
44
+ configured severity.
45
+ - `no-undef: "off"` — in-scenario bindings are implicit globals by
46
+ design; unknown names are the engine's `unknown-function` job.
47
+ - Globals for the DSL vocabulary, your helpers and your `UPPER_SNAKE`
48
+ variables, so other plugins' rules don't trip on them.
49
+
50
+ ## Severity
51
+
52
+ Rule severities come from `linter.rules` in `unotest.config.mjs` — the
53
+ same source the CLI uses. `"off"` there removes the diagnostic entirely;
54
+ there is nothing to configure on the ESLint side.
55
+
56
+ ## License
57
+
58
+ MIT © Ivan Volkov
@@ -0,0 +1,25 @@
1
+ import * as eslint from 'eslint';
2
+ import { Rule, Linter } from 'eslint';
3
+
4
+ declare const dslRule: Rule.RuleModule;
5
+
6
+ interface RecommendedOptions {
7
+ /** Project root holding unotest.config.* and unotest/ (default cwd). */
8
+ cwd?: string;
9
+ }
10
+ declare const basePlugin: {
11
+ meta: {
12
+ name: string;
13
+ };
14
+ rules: {
15
+ dsl: eslint.Rule.RuleModule;
16
+ };
17
+ };
18
+ declare const plugin: typeof basePlugin & {
19
+ configs: {
20
+ recommended: typeof recommended;
21
+ };
22
+ };
23
+ declare function recommended(opts?: RecommendedOptions): Promise<Linter.Config[]>;
24
+
25
+ export { type RecommendedOptions, plugin as default, dslRule };
package/dist/index.js ADDED
@@ -0,0 +1,114 @@
1
+ // src/index.ts
2
+ import { createWebDslLanguageService as createWebDslLanguageService2, loadLintProjectContext } from "@unotest/web/dsl";
3
+
4
+ // src/rules/dsl.ts
5
+ import {
6
+ createWebDslLanguageService,
7
+ linterRulesToSeverity
8
+ } from "@unotest/web/dsl";
9
+ var dslRule = {
10
+ meta: {
11
+ type: "problem",
12
+ docs: {
13
+ description: "Validate a unotest DSL scenario with the same engine as `unotest-web lint`"
14
+ },
15
+ schema: [
16
+ {
17
+ type: "object",
18
+ properties: {
19
+ linterRules: {
20
+ type: "object",
21
+ additionalProperties: { type: "string" }
22
+ },
23
+ helperNames: {
24
+ type: "array",
25
+ items: { type: "string" }
26
+ }
27
+ },
28
+ additionalProperties: false
29
+ }
30
+ ]
31
+ },
32
+ create(context) {
33
+ const opts = context.options[0] ?? {};
34
+ const service = createWebDslLanguageService(
35
+ opts.linterRules ? linterRulesToSeverity(opts.linterRules) : void 0
36
+ );
37
+ const diagnostics = service.validate(context.sourceCode.text, {
38
+ helperNames: opts.helperNames
39
+ });
40
+ return {
41
+ Program() {
42
+ for (const d of diagnostics) {
43
+ context.report({
44
+ // The DSL severity (error/warning/info) cannot vary per report
45
+ // in ESLint — it rides in the message; the rule id of the
46
+ // ENGINE rule too, since ESLint's own ruleId is always ours.
47
+ message: `[${d.severity} ${d.rule}] ${d.message}`,
48
+ loc: {
49
+ // DslDiagnostic is 1-based for both; ESLint loc columns are
50
+ // 0-based.
51
+ start: { line: d.line, column: d.column - 1 },
52
+ end: {
53
+ line: d.endLine ?? d.line,
54
+ column: (d.endColumn ?? d.column + 1) - 1
55
+ }
56
+ }
57
+ });
58
+ }
59
+ }
60
+ };
61
+ }
62
+ };
63
+
64
+ // src/index.ts
65
+ var basePlugin = {
66
+ meta: {
67
+ name: "@unotest/eslint-plugin"
68
+ },
69
+ rules: {
70
+ dsl: dslRule
71
+ }
72
+ };
73
+ var plugin = {
74
+ ...basePlugin,
75
+ configs: {
76
+ recommended
77
+ }
78
+ };
79
+ async function recommended(opts = {}) {
80
+ const cwd = opts.cwd ?? process.cwd();
81
+ const ctx = await loadLintProjectContext(cwd);
82
+ const vocabNames = createWebDslLanguageService2().getVocab().filter((e) => e.category !== "keyword" || e.name === "step").map((e) => e.name);
83
+ const globals = Object.fromEntries(
84
+ [...vocabNames, ...ctx.envVarNames, ...ctx.helpers.names].map((n) => [n, "readonly"])
85
+ );
86
+ return [
87
+ {
88
+ name: "@unotest/recommended",
89
+ files: ["unotest/e2e/**/*.js"],
90
+ plugins: { "@unotest": basePlugin },
91
+ languageOptions: {
92
+ ecmaVersion: 2022,
93
+ sourceType: "script",
94
+ globals
95
+ },
96
+ rules: {
97
+ "no-undef": "off",
98
+ "@unotest/dsl": [
99
+ "error",
100
+ {
101
+ linterRules: { ...ctx.linterRules },
102
+ helperNames: [...ctx.helpers.names]
103
+ }
104
+ ]
105
+ }
106
+ }
107
+ ];
108
+ }
109
+ var src_default = plugin;
110
+ export {
111
+ src_default as default,
112
+ dslRule
113
+ };
114
+ //# sourceMappingURL=index.js.map
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@unotest/eslint-plugin",
3
+ "version": "0.18.0",
4
+ "description": "ESLint plugin for unotest web DSL scenarios: one `dsl` rule that runs the exact same validator as `unotest-web lint` (parser, vocabulary, linter rules, config severities) and reports its diagnostics through ESLint, plus a flat `recommended` config wired for unotest/e2e/**.",
5
+ "license": "MIT",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "author": "Ivan Volkov <ivan@volkov.io>",
10
+ "homepage": "https://www.npmjs.com/package/@unotest/eslint-plugin",
11
+ "type": "module",
12
+ "engines": {
13
+ "node": ">=20"
14
+ },
15
+ "main": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js"
21
+ }
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "!dist/**/*.map",
26
+ "CHANGELOG.md",
27
+ "LICENSE",
28
+ "README.md"
29
+ ],
30
+ "peerDependencies": {
31
+ "@unotest/web": ">=0.18.0",
32
+ "eslint": ">=9"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^22.10.0",
36
+ "eslint": "^9.15.0",
37
+ "tsup": "^8.5.1",
38
+ "tsx": "^4.19.2",
39
+ "typescript": "^5.7.2",
40
+ "@unotest/web": "^0.18.0"
41
+ }
42
+ }