@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 +22 -0
- package/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +114 -0
- package/package.json +42 -0
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
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|