@zukhruf/eslint 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.
- package/LICENSE +21 -0
- package/README.md +138 -0
- package/dist/authoring/ast.d.ts +10 -0
- package/dist/authoring/ast.d.ts.map +1 -0
- package/dist/authoring/ast.js +22 -0
- package/dist/authoring/ast.js.map +1 -0
- package/dist/authoring/import-ban.d.ts +14 -0
- package/dist/authoring/import-ban.d.ts.map +1 -0
- package/dist/authoring/import-ban.js +32 -0
- package/dist/authoring/import-ban.js.map +1 -0
- package/dist/authoring/path-scope.d.ts +12 -0
- package/dist/authoring/path-scope.d.ts.map +1 -0
- package/dist/authoring/path-scope.js +28 -0
- package/dist/authoring/path-scope.js.map +1 -0
- package/dist/authoring/rule-module.d.ts +14 -0
- package/dist/authoring/rule-module.d.ts.map +1 -0
- package/dist/authoring/rule-module.js +23 -0
- package/dist/authoring/rule-module.js.map +1 -0
- package/dist/authoring/scoped-rule.d.ts +8 -0
- package/dist/authoring/scoped-rule.d.ts.map +1 -0
- package/dist/authoring/scoped-rule.js +12 -0
- package/dist/authoring/scoped-rule.js.map +1 -0
- package/dist/authoring/selector-rule.d.ts +17 -0
- package/dist/authoring/selector-rule.d.ts.map +1 -0
- package/dist/authoring/selector-rule.js +23 -0
- package/dist/authoring/selector-rule.js.map +1 -0
- package/dist/base/config.d.ts +8 -0
- package/dist/base/config.d.ts.map +1 -0
- package/dist/base/config.js +122 -0
- package/dist/base/config.js.map +1 -0
- package/dist/base/options.d.ts +23 -0
- package/dist/base/options.d.ts.map +1 -0
- package/dist/base/options.js +22 -0
- package/dist/base/options.js.map +1 -0
- package/dist/concept.d.ts +16 -0
- package/dist/concept.d.ts.map +1 -0
- package/dist/concept.js +5 -0
- package/dist/concept.js.map +1 -0
- package/dist/diagnostics/config.d.ts +4 -0
- package/dist/diagnostics/config.d.ts.map +1 -0
- package/dist/diagnostics/config.js +25 -0
- package/dist/diagnostics/config.js.map +1 -0
- package/dist/diagnostics/consistent-event-name.d.ts +4 -0
- package/dist/diagnostics/consistent-event-name.d.ts.map +1 -0
- package/dist/diagnostics/consistent-event-name.js +72 -0
- package/dist/diagnostics/consistent-event-name.js.map +1 -0
- package/dist/diagnostics/no-fabricated-fallback.d.ts +4 -0
- package/dist/diagnostics/no-fabricated-fallback.d.ts.map +1 -0
- package/dist/diagnostics/no-fabricated-fallback.js +116 -0
- package/dist/diagnostics/no-fabricated-fallback.js.map +1 -0
- package/dist/diagnostics/no-hardcoded-id-shape.d.ts +4 -0
- package/dist/diagnostics/no-hardcoded-id-shape.d.ts.map +1 -0
- package/dist/diagnostics/no-hardcoded-id-shape.js +73 -0
- package/dist/diagnostics/no-hardcoded-id-shape.js.map +1 -0
- package/dist/environment/config.d.ts +7 -0
- package/dist/environment/config.d.ts.map +1 -0
- package/dist/environment/config.js +28 -0
- package/dist/environment/config.js.map +1 -0
- package/dist/environment/no-default-for-path-env.d.ts +10 -0
- package/dist/environment/no-default-for-path-env.d.ts.map +1 -0
- package/dist/environment/no-default-for-path-env.js +124 -0
- package/dist/environment/no-default-for-path-env.js.map +1 -0
- package/dist/environment/no-single-env-read-wrapper.d.ts +10 -0
- package/dist/environment/no-single-env-read-wrapper.d.ts.map +1 -0
- package/dist/environment/no-single-env-read-wrapper.js +76 -0
- package/dist/environment/no-single-env-read-wrapper.js.map +1 -0
- package/dist/environment/no-undeclared-process-env.d.ts +11 -0
- package/dist/environment/no-undeclared-process-env.d.ts.map +1 -0
- package/dist/environment/no-undeclared-process-env.js +195 -0
- package/dist/environment/no-undeclared-process-env.js.map +1 -0
- package/dist/files.d.ts +16 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +27 -0
- package/dist/files.js.map +1 -0
- package/dist/hono/config.d.ts +3 -0
- package/dist/hono/config.d.ts.map +1 -0
- package/dist/hono/config.js +18 -0
- package/dist/hono/config.js.map +1 -0
- package/dist/hono/no-untyped-empty-json.d.ts +8 -0
- package/dist/hono/no-untyped-empty-json.d.ts.map +1 -0
- package/dist/hono/no-untyped-empty-json.js +14 -0
- package/dist/hono/no-untyped-empty-json.js.map +1 -0
- package/dist/imports/no-deepagents-agent.d.ts +3 -0
- package/dist/imports/no-deepagents-agent.d.ts.map +1 -0
- package/dist/imports/no-deepagents-agent.js +7 -0
- package/dist/imports/no-deepagents-agent.js.map +1 -0
- package/dist/imports/no-playwright-test.d.ts +3 -0
- package/dist/imports/no-playwright-test.d.ts.map +1 -0
- package/dist/imports/no-playwright-test.js +7 -0
- package/dist/imports/no-playwright-test.js.map +1 -0
- package/dist/imports/no-state-managers.d.ts +3 -0
- package/dist/imports/no-state-managers.d.ts.map +1 -0
- package/dist/imports/no-state-managers.js +15 -0
- package/dist/imports/no-state-managers.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/island/island-scope.d.ts +10 -0
- package/dist/island/island-scope.d.ts.map +1 -0
- package/dist/island/island-scope.js +21 -0
- package/dist/island/island-scope.js.map +1 -0
- package/dist/island/no-console.d.ts +9 -0
- package/dist/island/no-console.d.ts.map +1 -0
- package/dist/island/no-console.js +39 -0
- package/dist/island/no-console.js.map +1 -0
- package/dist/island/no-generic-port.d.ts +8 -0
- package/dist/island/no-generic-port.d.ts.map +1 -0
- package/dist/island/no-generic-port.js +14 -0
- package/dist/island/no-generic-port.js.map +1 -0
- package/dist/island/no-process-env.d.ts +3 -0
- package/dist/island/no-process-env.d.ts.map +1 -0
- package/dist/island/no-process-env.js +10 -0
- package/dist/island/no-process-env.js.map +1 -0
- package/dist/island/plugin.d.ts +25 -0
- package/dist/island/plugin.d.ts.map +1 -0
- package/dist/island/plugin.js +67 -0
- package/dist/island/plugin.js.map +1 -0
- package/dist/language/no-enum.d.ts +3 -0
- package/dist/language/no-enum.d.ts.map +1 -0
- package/dist/language/no-enum.js +7 -0
- package/dist/language/no-enum.js.map +1 -0
- package/dist/nx-policy/dependency-policy.d.ts +30 -0
- package/dist/nx-policy/dependency-policy.d.ts.map +1 -0
- package/dist/nx-policy/dependency-policy.js +31 -0
- package/dist/nx-policy/dependency-policy.js.map +1 -0
- package/dist/nx-policy/island-constraint.d.ts +10 -0
- package/dist/nx-policy/island-constraint.d.ts.map +1 -0
- package/dist/nx-policy/island-constraint.js +15 -0
- package/dist/nx-policy/island-constraint.js.map +1 -0
- package/dist/nx-policy/module-boundaries.d.ts +29 -0
- package/dist/nx-policy/module-boundaries.d.ts.map +1 -0
- package/dist/nx-policy/module-boundaries.js +20 -0
- package/dist/nx-policy/module-boundaries.js.map +1 -0
- package/dist/nx-project/config.d.ts +4 -0
- package/dist/nx-project/config.d.ts.map +1 -0
- package/dist/nx-project/config.js +20 -0
- package/dist/nx-project/config.js.map +1 -0
- package/dist/nx-project/no-missing-asset-input.d.ts +9 -0
- package/dist/nx-project/no-missing-asset-input.d.ts.map +1 -0
- package/dist/nx-project/no-missing-asset-input.js +209 -0
- package/dist/nx-project/no-missing-asset-input.js.map +1 -0
- package/dist/nx.d.ts +5 -0
- package/dist/nx.d.ts.map +1 -0
- package/dist/nx.js +5 -0
- package/dist/nx.js.map +1 -0
- package/dist/packaged-app/config.d.ts +4 -0
- package/dist/packaged-app/config.d.ts.map +1 -0
- package/dist/packaged-app/config.js +19 -0
- package/dist/packaged-app/config.js.map +1 -0
- package/dist/packaged-app/no-bare-spawn.d.ts +4 -0
- package/dist/packaged-app/no-bare-spawn.d.ts.map +1 -0
- package/dist/packaged-app/no-bare-spawn.js +175 -0
- package/dist/packaged-app/no-bare-spawn.js.map +1 -0
- package/dist/plugin.d.ts +14 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +48 -0
- package/dist/plugin.js.map +1 -0
- package/dist/pulumi/config.d.ts +3 -0
- package/dist/pulumi/config.d.ts.map +1 -0
- package/dist/pulumi/config.js +17 -0
- package/dist/pulumi/config.js.map +1 -0
- package/dist/pulumi/require-server-replace-guard.d.ts +4 -0
- package/dist/pulumi/require-server-replace-guard.d.ts.map +1 -0
- package/dist/pulumi/require-server-replace-guard.js +120 -0
- package/dist/pulumi/require-server-replace-guard.js.map +1 -0
- package/dist/react/config.d.ts +4 -0
- package/dist/react/config.d.ts.map +1 -0
- package/dist/react/config.js +13 -0
- package/dist/react/config.js.map +1 -0
- package/dist/react/require-combobox-popover-modal.d.ts +10 -0
- package/dist/react/require-combobox-popover-modal.d.ts.map +1 -0
- package/dist/react/require-combobox-popover-modal.js +101 -0
- package/dist/react/require-combobox-popover-modal.js.map +1 -0
- package/dist/react-router/config.d.ts +3 -0
- package/dist/react-router/config.d.ts.map +1 -0
- package/dist/react-router/config.js +34 -0
- package/dist/react-router/config.js.map +1 -0
- package/dist/react-router/no-internal-anchor.d.ts +8 -0
- package/dist/react-router/no-internal-anchor.d.ts.map +1 -0
- package/dist/react-router/no-internal-anchor.js +14 -0
- package/dist/react-router/no-internal-anchor.js.map +1 -0
- package/dist/react-router/no-raw-route-path.d.ts +9 -0
- package/dist/react-router/no-raw-route-path.d.ts.map +1 -0
- package/dist/react-router/no-raw-route-path.js +17 -0
- package/dist/react-router/no-raw-route-path.js.map +1 -0
- package/dist/react-router/require-loader-data-type-argument.d.ts +10 -0
- package/dist/react-router/require-loader-data-type-argument.d.ts.map +1 -0
- package/dist/react-router/require-loader-data-type-argument.js +16 -0
- package/dist/react-router/require-loader-data-type-argument.js.map +1 -0
- package/dist/tailwind/class-name-check.d.ts +12 -0
- package/dist/tailwind/class-name-check.d.ts.map +1 -0
- package/dist/tailwind/class-name-check.js +16 -0
- package/dist/tailwind/class-name-check.js.map +1 -0
- package/dist/tailwind/config.d.ts +3 -0
- package/dist/tailwind/config.d.ts.map +1 -0
- package/dist/tailwind/config.js +18 -0
- package/dist/tailwind/config.js.map +1 -0
- package/dist/tailwind/no-arbitrary-z-index.d.ts +3 -0
- package/dist/tailwind/no-arbitrary-z-index.d.ts.map +1 -0
- package/dist/tailwind/no-arbitrary-z-index.js +7 -0
- package/dist/tailwind/no-arbitrary-z-index.js.map +1 -0
- package/dist/tailwind/no-h-screen.d.ts +3 -0
- package/dist/tailwind/no-h-screen.d.ts.map +1 -0
- package/dist/tailwind/no-h-screen.js +7 -0
- package/dist/tailwind/no-h-screen.js.map +1 -0
- package/dist/tailwind/no-will-change.d.ts +3 -0
- package/dist/tailwind/no-will-change.d.ts.map +1 -0
- package/dist/tailwind/no-will-change.js +7 -0
- package/dist/tailwind/no-will-change.js.map +1 -0
- package/dist/test-files/config.d.ts +3 -0
- package/dist/test-files/config.d.ts.map +1 -0
- package/dist/test-files/config.js +16 -0
- package/dist/test-files/config.js.map +1 -0
- package/dist/test-files/no-test-lifecycle-hooks.d.ts +3 -0
- package/dist/test-files/no-test-lifecycle-hooks.d.ts.map +1 -0
- package/dist/test-files/no-test-lifecycle-hooks.js +9 -0
- package/dist/test-files/no-test-lifecycle-hooks.js.map +1 -0
- package/dist/test-files/require-msw-error-on-unhandled-request.d.ts +10 -0
- package/dist/test-files/require-msw-error-on-unhandled-request.d.ts.map +1 -0
- package/dist/test-files/require-msw-error-on-unhandled-request.js +16 -0
- package/dist/test-files/require-msw-error-on-unhandled-request.js.map +1 -0
- package/dist/workspace/project-manifest.d.ts +14 -0
- package/dist/workspace/project-manifest.d.ts.map +1 -0
- package/dist/workspace/project-manifest.js +45 -0
- package/dist/workspace/project-manifest.js.map +1 -0
- package/dist/workspace/workspace-root.d.ts +12 -0
- package/dist/workspace/workspace-root.d.ts.map +1 -0
- package/dist/workspace/workspace-root.js +46 -0
- package/dist/workspace/workspace-root.js.map +1 -0
- package/package.json +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ezzabuzaid
|
|
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,138 @@
|
|
|
1
|
+
# @zukhruf/eslint
|
|
2
|
+
|
|
3
|
+
ESLint rules and configs that several repos share. Each check is a named rule, such as `zukhruf/no-enum`. A repo's own config cannot silently erase it, and a repo can turn off one check by name.
|
|
4
|
+
|
|
5
|
+
The words in this document have one meaning each. See the glossary in [CONTEXT.md](./CONTEXT.md).
|
|
6
|
+
|
|
7
|
+
## The problem
|
|
8
|
+
|
|
9
|
+
The same rules were copied from repo to repo, and the copies broke in three ways:
|
|
10
|
+
|
|
11
|
+
1. **One key, many checks.** The hand-written bans (no enums, no test hooks, …) were entries of one rule, `no-restricted-syntax`. Flat config keeps one options list for each rule. A framework preset set `no-restricted-syntax` again, and every ban was gone.
|
|
12
|
+
2. **Globs that depend on the folder.** `nx lint` runs ESLint from the project folder. When a project's config re-exports the root config, a glob such as `apps/**` matches nothing.
|
|
13
|
+
3. **Partial options.** A project set `@nx/dependency-checks` with only its own `ignoredFiles`. The rule's defaults filled in the rest, and the shared policy was lost.
|
|
14
|
+
|
|
15
|
+
This package gives each check its own key, writes every glob as `**/…`, scopes by the linted file's path, and builds options with factories that add a repo's entries to the shared ones.
|
|
16
|
+
|
|
17
|
+
## Use it
|
|
18
|
+
|
|
19
|
+
You need ESLint 9.30 or later, and Node.js 24 or later.
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
npm install --save-dev @zukhruf/eslint
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
// eslint.config.mjs
|
|
27
|
+
import { defineConfig } from 'eslint/config';
|
|
28
|
+
|
|
29
|
+
import zukhruf from '@zukhruf/eslint';
|
|
30
|
+
|
|
31
|
+
export default defineConfig({
|
|
32
|
+
plugins: { zukhruf },
|
|
33
|
+
extends: ['zukhruf/base', 'zukhruf/tests'],
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Put framework presets (for example Nx's `flat/react`) **before** the zukhruf configs. The package's own checks survive any order. But `base` also changes some third-party rules, for example it turns `@typescript-eslint/no-unused-vars` off, and a preset that comes later sets them again.
|
|
38
|
+
|
|
39
|
+
## Configs
|
|
40
|
+
|
|
41
|
+
| Config | Files | Rules |
|
|
42
|
+
| ----------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
43
|
+
| `base` | all source | `@eslint/js` and `typescript-eslint` recommended; `consistent-type-assertions` (never), `no-namespace`, `zukhruf/no-enum`; `import-x/no-duplicates`, `import-x/no-unassigned-import`; `functional/no-let` (module scope); `zukhruf/no-state-managers`, `zukhruf/no-deepagents-agent`, `zukhruf/no-playwright-test`; typed lint: `no-floating-promises`, `no-misused-promises` |
|
|
44
|
+
| `tests` | test files | `no-test-lifecycle-hooks`, `require-msw-error-on-unhandled-request` |
|
|
45
|
+
| `diagnostics` | source, not tests | `no-fabricated-fallback`, `no-hardcoded-id-shape`, `consistent-event-name` |
|
|
46
|
+
| `env` | TypeScript, not tests | `no-undeclared-process-env`, `no-single-env-read-wrapper`, `no-default-for-path-env` |
|
|
47
|
+
| `packaged-app` | TypeScript, not tests | `no-bare-spawn` |
|
|
48
|
+
| `react` | `.tsx`, `.jsx` | `require-combobox-popover-modal` |
|
|
49
|
+
| `react-router` | source | `require-loader-data-type-argument`; outside tests: `no-raw-route-path`, `no-internal-anchor` |
|
|
50
|
+
| `hono` | TypeScript, not tests | `no-untyped-empty-json` |
|
|
51
|
+
| `tailwind` | source | `no-h-screen`, `no-arbitrary-z-index`, `no-will-change` |
|
|
52
|
+
| `pulumi` | TypeScript | `require-server-replace-guard` |
|
|
53
|
+
| `nx-project-json` | `project.json` | `no-missing-asset-input` |
|
|
54
|
+
|
|
55
|
+
`base` turns on typed lint (`parserOptions.projectService`). Each linted TypeScript file must be in a tsconfig.
|
|
56
|
+
|
|
57
|
+
### Options for a repo
|
|
58
|
+
|
|
59
|
+
Give repo-specific values as rule options. For example:
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
{
|
|
63
|
+
rules: {
|
|
64
|
+
'zukhruf/no-bare-spawn': ['error', { roots: ['apps/desktop/src'] }],
|
|
65
|
+
'zukhruf/no-undeclared-process-env': ['error', { startupFile: 'src/env.ts' }],
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`roots` are folders relative to the workspace root. A rule finds the workspace root from the linted file: the nearest folder with `nx.json`, `pnpm-workspace.yaml`, or a `package.json` that declares `workspaces`.
|
|
71
|
+
|
|
72
|
+
To extend a `base` rule, use its factory. Do not declare the key again with only your entries: that replaces the base options.
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
import { noFloatingPromises, noLet } from '@zukhruf/eslint';
|
|
76
|
+
|
|
77
|
+
{
|
|
78
|
+
rules: {
|
|
79
|
+
'functional/no-let': ['error', noLet({ ignoreIdentifierPattern: ['^cached'] })],
|
|
80
|
+
'@typescript-eslint/no-floating-promises': ['error', noFloatingPromises({ allowForKnownSafeCalls: [/* … */] })],
|
|
81
|
+
},
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Nx: islands and module boundaries
|
|
86
|
+
|
|
87
|
+
`@zukhruf/eslint/nx` needs `@nx/eslint-plugin`.
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
import nx from '@nx/eslint-plugin';
|
|
91
|
+
import { defineConfig } from 'eslint/config';
|
|
92
|
+
|
|
93
|
+
import zukhruf from '@zukhruf/eslint';
|
|
94
|
+
import island, { islandConstraint, moduleBoundaries } from '@zukhruf/eslint/nx';
|
|
95
|
+
|
|
96
|
+
export default defineConfig(
|
|
97
|
+
nx.configs['flat/base'],
|
|
98
|
+
{
|
|
99
|
+
plugins: { zukhruf, island },
|
|
100
|
+
extends: ['zukhruf/base', 'zukhruf/tests', 'island/recommended'],
|
|
101
|
+
settings: { island: { libraries: ['packages/**/*.ts'] } },
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
files: ['**/*.ts'],
|
|
105
|
+
rules: {
|
|
106
|
+
'@nx/enforce-module-boundaries': [
|
|
107
|
+
'error',
|
|
108
|
+
moduleBoundaries({
|
|
109
|
+
depConstraints: [islandConstraint(['hono', 'hono/*'])],
|
|
110
|
+
}),
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
An island is an Nx project tagged `layer:island`. Its tags come from its `project.json` or its `package.json` `nx.tags`. `island/recommended` has these rules:
|
|
118
|
+
|
|
119
|
+
- `island/no-generic-port` and `island/no-explicit-any` in islands.
|
|
120
|
+
- `island/no-process-env` and `island/no-console` in islands and in the files that `settings.island.libraries` names.
|
|
121
|
+
- `island/dependency-checks` on each island's `package.json`, with `dependencyPolicy()`.
|
|
122
|
+
|
|
123
|
+
`moduleBoundaries()` and `dependencyPolicy()` add your entries to the shared options:
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
'island/dependency-checks': ['error', dependencyPolicy({ ignoredDependencies: ['electron'] })]
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Development
|
|
130
|
+
|
|
131
|
+
The workspace lints itself with this package's source: the root `eslint.config.mjs` imports `packages/eslint/src/index.ts`. Nx loads that config while it builds the project graph, before anything is built. A rule edit applies on the next lint.
|
|
132
|
+
|
|
133
|
+
```sh
|
|
134
|
+
npx nx run eslint:test # builds, then runs the rule and composition tests
|
|
135
|
+
npm run verify:packages # installs the packed tarball and imports each export
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The composition tests run the real ESLint CLI on temporary workspaces. They load the built package, which is what consumers install.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Rule } from 'eslint';
|
|
2
|
+
/** An AST node whose fields are read one by one, each narrowed where it is read. */
|
|
3
|
+
export type AstNode = Rule.Node & Record<string, unknown>;
|
|
4
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
5
|
+
/** The string entries of `value[key]`: options and manifests arrive as `unknown`. */
|
|
6
|
+
export declare function stringsAt(value: unknown, key: string): string[];
|
|
7
|
+
export declare function isAstNode(value: unknown): value is AstNode;
|
|
8
|
+
export declare function isType(value: unknown, type: string): value is AstNode;
|
|
9
|
+
export declare function identifierName(value: unknown): string | undefined;
|
|
10
|
+
//# sourceMappingURL=ast.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../src/authoring/ast.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC,oFAAoF;AACpF,MAAM,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE1D,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,qFAAqF;AACrF,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAK/D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,CAE1D;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,KAAK,IAAI,OAAO,CAErE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAIjE"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function isRecord(value) {
|
|
2
|
+
return typeof value === 'object' && value !== null;
|
|
3
|
+
}
|
|
4
|
+
/** The string entries of `value[key]`: options and manifests arrive as `unknown`. */
|
|
5
|
+
export function stringsAt(value, key) {
|
|
6
|
+
const list = isRecord(value) ? value[key] : undefined;
|
|
7
|
+
return Array.isArray(list)
|
|
8
|
+
? list.filter((item) => typeof item === 'string')
|
|
9
|
+
: [];
|
|
10
|
+
}
|
|
11
|
+
export function isAstNode(value) {
|
|
12
|
+
return isRecord(value) && typeof value['type'] === 'string';
|
|
13
|
+
}
|
|
14
|
+
export function isType(value, type) {
|
|
15
|
+
return isAstNode(value) && value['type'] === type;
|
|
16
|
+
}
|
|
17
|
+
export function identifierName(value) {
|
|
18
|
+
return isType(value, 'Identifier') && typeof value['name'] === 'string'
|
|
19
|
+
? value['name']
|
|
20
|
+
: undefined;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=ast.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast.js","sourceRoot":"","sources":["../../src/authoring/ast.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACrD,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,SAAS,CAAC,KAAc,EAAE,GAAW;IACnD,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACtD,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QACxB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC;QACjE,CAAC,CAAC,EAAE,CAAC;AACT,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,OAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,KAAc,EAAE,IAAY;IACjD,OAAO,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,MAAM,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,QAAQ;QACrE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC;QACf,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Rule } from 'eslint';
|
|
2
|
+
export interface ImportBan {
|
|
3
|
+
readonly description: string;
|
|
4
|
+
readonly message: string;
|
|
5
|
+
/** Package names; each also bans its subpaths (`name/…`). */
|
|
6
|
+
readonly packages: readonly string[];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A ban that used to be one pattern group of `no-restricted-imports`, as a rule
|
|
10
|
+
* of its own, so a repo's own import bans cannot replace it. Covers static
|
|
11
|
+
* imports, re-exports and literal dynamic imports, type-only ones included.
|
|
12
|
+
*/
|
|
13
|
+
export declare function importBanRule({ description, message, packages, }: ImportBan): Rule.RuleModule;
|
|
14
|
+
//# sourceMappingURL=import-ban.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-ban.d.ts","sourceRoot":"","sources":["../../src/authoring/import-ban.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAInC,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAC5B,WAAW,EACX,OAAO,EACP,QAAQ,GACT,EAAE,SAAS,GAAG,IAAI,CAAC,UAAU,CA0B7B"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { isType } from "./ast.js";
|
|
2
|
+
/**
|
|
3
|
+
* A ban that used to be one pattern group of `no-restricted-imports`, as a rule
|
|
4
|
+
* of its own, so a repo's own import bans cannot replace it. Covers static
|
|
5
|
+
* imports, re-exports and literal dynamic imports, type-only ones included.
|
|
6
|
+
*/
|
|
7
|
+
export function importBanRule({ description, message, packages, }) {
|
|
8
|
+
const isBanned = (source) => typeof source === 'string' &&
|
|
9
|
+
packages.some((name) => source === name || source.startsWith(`${name}/`));
|
|
10
|
+
return {
|
|
11
|
+
meta: {
|
|
12
|
+
type: 'problem',
|
|
13
|
+
docs: { description },
|
|
14
|
+
schema: [],
|
|
15
|
+
messages: { banned: message },
|
|
16
|
+
},
|
|
17
|
+
create(context) {
|
|
18
|
+
const check = (node, source) => {
|
|
19
|
+
if (isType(source, 'Literal') && isBanned(source['value'])) {
|
|
20
|
+
context.report({ node, messageId: 'banned' });
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
ImportDeclaration: (node) => check(node, node.source),
|
|
25
|
+
ExportNamedDeclaration: (node) => check(node, node.source),
|
|
26
|
+
ExportAllDeclaration: (node) => check(node, node.source),
|
|
27
|
+
ImportExpression: (node) => check(node, node.source),
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=import-ban.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-ban.js","sourceRoot":"","sources":["../../src/authoring/import-ban.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AASlC;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,EAC5B,WAAW,EACX,OAAO,EACP,QAAQ,GACE;IACV,MAAM,QAAQ,GAAG,CAAC,MAAe,EAAE,EAAE,CACnC,OAAO,MAAM,KAAK,QAAQ;QAC1B,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;IAE5E,OAAO;QACL,IAAI,EAAE;YACJ,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,EAAE,WAAW,EAAE;YACrB,MAAM,EAAE,EAAE;YACV,QAAQ,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE;SAC9B;QACD,MAAM,CAAC,OAAO;YACZ,MAAM,KAAK,GAAG,CAAC,IAAe,EAAE,MAAe,EAAE,EAAE;gBACjD,IAAI,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;oBAC3D,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC;gBAChD,CAAC;YACH,CAAC,CAAC;YACF,OAAO;gBACL,iBAAiB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;gBACrD,sBAAsB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;gBAC1D,oBAAoB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;gBACxD,gBAAgB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;aACrD,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare function isTestFile(filename: string): boolean;
|
|
2
|
+
/**
|
|
3
|
+
* Whether `filename` lies under one of `roots`, workspace-relative folders such
|
|
4
|
+
* as `apps/backend/src`. An empty list means everywhere. Scoping lives in the
|
|
5
|
+
* rule rather than in a config's `files` glob because `nx lint` runs ESLint from
|
|
6
|
+
* the project folder: a project config that re-exports the root config resolves
|
|
7
|
+
* the root's globs from the project, and a glob like `apps/**` matches nothing.
|
|
8
|
+
*/
|
|
9
|
+
export declare function inRoots(filename: string, roots: readonly string[]): boolean;
|
|
10
|
+
/** Whether `filename`, relative to its workspace, matches one of `globs`. */
|
|
11
|
+
export declare function matchesAnyGlob(filename: string, globs: readonly string[]): boolean;
|
|
12
|
+
//# sourceMappingURL=path-scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-scope.d.ts","sourceRoot":"","sources":["../../src/authoring/path-scope.ts"],"names":[],"mappings":"AAMA,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAO3E;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,OAAO,CAGT"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { matchesGlob } from 'node:path';
|
|
2
|
+
import { relativeToWorkspace } from "../workspace/workspace-root.js";
|
|
3
|
+
const TEST_FILE = /\.(test|spec)\.[cm]?[jt]sx?$/;
|
|
4
|
+
export function isTestFile(filename) {
|
|
5
|
+
return TEST_FILE.test(filename);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Whether `filename` lies under one of `roots`, workspace-relative folders such
|
|
9
|
+
* as `apps/backend/src`. An empty list means everywhere. Scoping lives in the
|
|
10
|
+
* rule rather than in a config's `files` glob because `nx lint` runs ESLint from
|
|
11
|
+
* the project folder: a project config that re-exports the root config resolves
|
|
12
|
+
* the root's globs from the project, and a glob like `apps/**` matches nothing.
|
|
13
|
+
*/
|
|
14
|
+
export function inRoots(filename, roots) {
|
|
15
|
+
if (roots.length === 0)
|
|
16
|
+
return true;
|
|
17
|
+
const path = relativeToWorkspace(filename);
|
|
18
|
+
return roots.some((root) => {
|
|
19
|
+
const folder = root.replace(/\/+$/, '');
|
|
20
|
+
return path === folder || path.startsWith(`${folder}/`);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/** Whether `filename`, relative to its workspace, matches one of `globs`. */
|
|
24
|
+
export function matchesAnyGlob(filename, globs) {
|
|
25
|
+
const path = relativeToWorkspace(filename);
|
|
26
|
+
return globs.some((glob) => matchesGlob(path, glob));
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=path-scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-scope.js","sourceRoot":"","sources":["../../src/authoring/path-scope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAErE,MAAM,SAAS,GAAG,8BAA8B,CAAC;AAEjD,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,OAAO,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,OAAO,CAAC,QAAgB,EAAE,KAAwB;IAChE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,IAAI,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAC3C,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;QACzB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACxC,OAAO,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;AACL,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,cAAc,CAC5B,QAAgB,EAChB,KAAwB;IAExB,MAAM,IAAI,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAC3C,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Rule } from 'eslint';
|
|
2
|
+
/**
|
|
3
|
+
* typescript-eslint and Nx type their rules with their own RuleModule types,
|
|
4
|
+
* which ESLint's `Rule.RuleModule` does not accept. Checking the one member
|
|
5
|
+
* ESLint calls lets this package reuse those rules without a type assertion.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isRuleModule(value: unknown): value is Rule.RuleModule;
|
|
8
|
+
/**
|
|
9
|
+
* The rule `name` of an ESLint plugin; throws when the plugin no longer has it.
|
|
10
|
+
* Takes the plugin as `unknown`: typescript-eslint types its plugin without the
|
|
11
|
+
* `rules` it has at runtime.
|
|
12
|
+
*/
|
|
13
|
+
export declare function ruleOf(plugin: unknown, name: string): Rule.RuleModule;
|
|
14
|
+
//# sourceMappingURL=rule-module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-module.d.ts","sourceRoot":"","sources":["../../src/authoring/rule-module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAInC;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,IAAI,CAAC,UAAU,CAErE;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,UAAU,CAOrE"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { isRecord } from "./ast.js";
|
|
2
|
+
/**
|
|
3
|
+
* typescript-eslint and Nx type their rules with their own RuleModule types,
|
|
4
|
+
* which ESLint's `Rule.RuleModule` does not accept. Checking the one member
|
|
5
|
+
* ESLint calls lets this package reuse those rules without a type assertion.
|
|
6
|
+
*/
|
|
7
|
+
export function isRuleModule(value) {
|
|
8
|
+
return isRecord(value) && typeof value['create'] === 'function';
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The rule `name` of an ESLint plugin; throws when the plugin no longer has it.
|
|
12
|
+
* Takes the plugin as `unknown`: typescript-eslint types its plugin without the
|
|
13
|
+
* `rules` it has at runtime.
|
|
14
|
+
*/
|
|
15
|
+
export function ruleOf(plugin, name) {
|
|
16
|
+
const rules = isRecord(plugin) ? plugin['rules'] : undefined;
|
|
17
|
+
const rule = isRecord(rules) ? rules[name] : undefined;
|
|
18
|
+
if (!isRuleModule(rule)) {
|
|
19
|
+
throw new Error(`The plugin has no rule named "${name}".`);
|
|
20
|
+
}
|
|
21
|
+
return rule;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=rule-module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-module.js","sourceRoot":"","sources":["../../src/authoring/rule-module.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEpC;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,QAAQ,CAAC,KAAK,UAAU,CAAC;AAClE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAC,MAAe,EAAE,IAAY;IAClD,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7D,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACvD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,iCAAiC,IAAI,IAAI,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Rule } from 'eslint';
|
|
2
|
+
/**
|
|
3
|
+
* `rule`, active only on the files `applies` accepts. Scoping by the linted
|
|
4
|
+
* file, rather than by a config's `files` glob, follows project tags and works
|
|
5
|
+
* whichever folder ESLint runs from.
|
|
6
|
+
*/
|
|
7
|
+
export declare function scopedRule(rule: Rule.RuleModule, applies: (context: Rule.RuleContext) => boolean): Rule.RuleModule;
|
|
8
|
+
//# sourceMappingURL=scoped-rule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoped-rule.d.ts","sourceRoot":"","sources":["../../src/authoring/scoped-rule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,IAAI,CAAC,UAAU,EACrB,OAAO,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,KAAK,OAAO,GAC9C,IAAI,CAAC,UAAU,CAKjB"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `rule`, active only on the files `applies` accepts. Scoping by the linted
|
|
3
|
+
* file, rather than by a config's `files` glob, follows project tags and works
|
|
4
|
+
* whichever folder ESLint runs from.
|
|
5
|
+
*/
|
|
6
|
+
export function scopedRule(rule, applies) {
|
|
7
|
+
return {
|
|
8
|
+
...rule,
|
|
9
|
+
create: (context) => (applies(context) ? rule.create(context) : {}),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=scoped-rule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoped-rule.js","sourceRoot":"","sources":["../../src/authoring/scoped-rule.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,UAAU,UAAU,CACxB,IAAqB,EACrB,OAA+C;IAE/C,OAAO;QACL,GAAG,IAAI;QACP,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KACpE,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Rule } from 'eslint';
|
|
2
|
+
export interface SelectorCheck {
|
|
3
|
+
readonly description: string;
|
|
4
|
+
readonly message: string;
|
|
5
|
+
/** esquery selectors; a node that matches any of them is reported. */
|
|
6
|
+
readonly selectors: readonly string[];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A check that used to be one entry of `no-restricted-syntax`, as a rule of its
|
|
10
|
+
* own. Flat config keeps one options array per rule key, so the next config to
|
|
11
|
+
* set `no-restricted-syntax` (a framework preset, a project override) replaced
|
|
12
|
+
* every entry at once. Under its own key, a check can only be turned off by
|
|
13
|
+
* name. esquery's substring operator `*=` never matches a Literal's `value`;
|
|
14
|
+
* write regex selectors (`[value=/…/]`) instead.
|
|
15
|
+
*/
|
|
16
|
+
export declare function selectorRule({ description, message, selectors, }: SelectorCheck): Rule.RuleModule;
|
|
17
|
+
//# sourceMappingURL=selector-rule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selector-rule.d.ts","sourceRoot":"","sources":["../../src/authoring/selector-rule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,EAC3B,WAAW,EACX,OAAO,EACP,SAAS,GACV,EAAE,aAAa,GAAG,IAAI,CAAC,UAAU,CAgBjC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A check that used to be one entry of `no-restricted-syntax`, as a rule of its
|
|
3
|
+
* own. Flat config keeps one options array per rule key, so the next config to
|
|
4
|
+
* set `no-restricted-syntax` (a framework preset, a project override) replaced
|
|
5
|
+
* every entry at once. Under its own key, a check can only be turned off by
|
|
6
|
+
* name. esquery's substring operator `*=` never matches a Literal's `value`;
|
|
7
|
+
* write regex selectors (`[value=/…/]`) instead.
|
|
8
|
+
*/
|
|
9
|
+
export function selectorRule({ description, message, selectors, }) {
|
|
10
|
+
return {
|
|
11
|
+
meta: {
|
|
12
|
+
type: 'problem',
|
|
13
|
+
docs: { description },
|
|
14
|
+
schema: [],
|
|
15
|
+
messages: { matched: message },
|
|
16
|
+
},
|
|
17
|
+
create(context) {
|
|
18
|
+
const report = (node) => context.report({ node, messageId: 'matched' });
|
|
19
|
+
return Object.fromEntries(selectors.map((selector) => [selector, report]));
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=selector-rule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selector-rule.js","sourceRoot":"","sources":["../../src/authoring/selector-rule.ts"],"names":[],"mappings":"AASA;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,EAC3B,WAAW,EACX,OAAO,EACP,SAAS,GACK;IACd,OAAO;QACL,IAAI,EAAE;YACJ,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,EAAE,WAAW,EAAE;YACrB,MAAM,EAAE,EAAE;YACV,QAAQ,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE;SAC/B;QACD,MAAM,CAAC,OAAO;YACZ,MAAM,MAAM,GAAG,CAAC,IAAe,EAAE,EAAE,CACjC,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC;YACjD,OAAO,MAAM,CAAC,WAAW,CACvB,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAChD,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Concept } from '../concept.ts';
|
|
2
|
+
/**
|
|
3
|
+
* What every TypeScript repo gets: the recommended sets of @eslint/js and
|
|
4
|
+
* typescript-eslint, embedded first so a framework preset can never sit between
|
|
5
|
+
* them and this config's changes, then the taste rules and the typed checks.
|
|
6
|
+
*/
|
|
7
|
+
export declare const base: Concept;
|
|
8
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/base/config.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,OAAO,EAAU,MAAM,eAAe,CAAC;AAwBrD;;;;GAIG;AACH,eAAO,MAAM,IAAI,EAAE,OAyGlB,CAAC"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import js from '@eslint/js';
|
|
2
|
+
import functional from 'eslint-plugin-functional';
|
|
3
|
+
import importX from 'eslint-plugin-import-x';
|
|
4
|
+
import { defineConfig } from 'eslint/config';
|
|
5
|
+
import globals from 'globals';
|
|
6
|
+
import tseslint from 'typescript-eslint';
|
|
7
|
+
import { enable } from "../concept.js";
|
|
8
|
+
import { BUILD_OUTPUT, FIXTURES, JAVASCRIPT, SOURCE, TESTS, TYPESCRIPT, } from "../files.js";
|
|
9
|
+
import noDeepagentsAgent from "../imports/no-deepagents-agent.js";
|
|
10
|
+
import noPlaywrightTest from "../imports/no-playwright-test.js";
|
|
11
|
+
import noStateManagers from "../imports/no-state-managers.js";
|
|
12
|
+
import noEnum from "../language/no-enum.js";
|
|
13
|
+
import { workspaceRootOf } from "../workspace/workspace-root.js";
|
|
14
|
+
import { noFloatingPromises, noLet } from "./options.js";
|
|
15
|
+
const importBans = {
|
|
16
|
+
'no-state-managers': noStateManagers,
|
|
17
|
+
'no-deepagents-agent': noDeepagentsAgent,
|
|
18
|
+
'no-playwright-test': noPlaywrightTest,
|
|
19
|
+
};
|
|
20
|
+
const rules = { 'no-enum': noEnum, ...importBans };
|
|
21
|
+
/**
|
|
22
|
+
* What every TypeScript repo gets: the recommended sets of @eslint/js and
|
|
23
|
+
* typescript-eslint, embedded first so a framework preset can never sit between
|
|
24
|
+
* them and this config's changes, then the taste rules and the typed checks.
|
|
25
|
+
*/
|
|
26
|
+
export const base = {
|
|
27
|
+
name: 'base',
|
|
28
|
+
rules,
|
|
29
|
+
config: (plugins) => defineConfig({ name: 'zukhruf/base/ignores', ignores: [...BUILD_OUTPUT, '**/.nx'] }, {
|
|
30
|
+
name: 'zukhruf/base/recommended',
|
|
31
|
+
files: SOURCE,
|
|
32
|
+
extends: [js.configs.recommended, tseslint.configs.recommended],
|
|
33
|
+
}, {
|
|
34
|
+
name: 'zukhruf/base/javascript-globals',
|
|
35
|
+
files: JAVASCRIPT,
|
|
36
|
+
languageOptions: { globals: { ...globals.browser, ...globals.node } },
|
|
37
|
+
}, {
|
|
38
|
+
name: 'zukhruf/base/recommended-changes',
|
|
39
|
+
files: SOURCE,
|
|
40
|
+
rules: {
|
|
41
|
+
'no-redeclare': 'off',
|
|
42
|
+
'@typescript-eslint/no-redeclare': 'error',
|
|
43
|
+
'no-unused-private-class-members': 'off',
|
|
44
|
+
'@typescript-eslint/no-non-null-assertion': 'off',
|
|
45
|
+
'@typescript-eslint/no-explicit-any': 'off',
|
|
46
|
+
'@typescript-eslint/no-unused-vars': 'off',
|
|
47
|
+
'no-empty': 'off',
|
|
48
|
+
'@typescript-eslint/no-empty-function': 'off',
|
|
49
|
+
'@typescript-eslint/parameter-properties': 'error',
|
|
50
|
+
'@typescript-eslint/adjacent-overload-signatures': 'error',
|
|
51
|
+
'@typescript-eslint/prefer-namespace-keyword': 'error',
|
|
52
|
+
'@typescript-eslint/no-inferrable-types': 'error',
|
|
53
|
+
},
|
|
54
|
+
}, {
|
|
55
|
+
name: 'zukhruf/base/typescript',
|
|
56
|
+
files: TYPESCRIPT,
|
|
57
|
+
plugins,
|
|
58
|
+
rules: {
|
|
59
|
+
// `as X` papers over a type error instead of fixing it: narrow with
|
|
60
|
+
// control flow. `as const` and `satisfies` stay allowed.
|
|
61
|
+
'@typescript-eslint/consistent-type-assertions': [
|
|
62
|
+
'error',
|
|
63
|
+
{ assertionStyle: 'never' },
|
|
64
|
+
],
|
|
65
|
+
// Enums and value namespaces need a runtime transform, which Node's
|
|
66
|
+
// strip-only `.ts` execution does not do.
|
|
67
|
+
'@typescript-eslint/no-namespace': 'error',
|
|
68
|
+
'zukhruf/no-enum': 'error',
|
|
69
|
+
},
|
|
70
|
+
// A disable directive that suppresses nothing outlived its reason.
|
|
71
|
+
linterOptions: { reportUnusedDisableDirectives: 'error' },
|
|
72
|
+
}, {
|
|
73
|
+
name: 'zukhruf/base/imports',
|
|
74
|
+
files: SOURCE,
|
|
75
|
+
plugins: { ...plugins, 'import-x': importX },
|
|
76
|
+
rules: {
|
|
77
|
+
...enable(importBans),
|
|
78
|
+
// Two imports of one module read as unrelated; type imports stay a
|
|
79
|
+
// separate statement so they remain erasable on their own.
|
|
80
|
+
'import-x/no-duplicates': ['error', { 'prefer-inline': false }],
|
|
81
|
+
// A bare `import './x.ts'` hides a registration behind evaluation
|
|
82
|
+
// order; a module you need is a value you bind.
|
|
83
|
+
'import-x/no-unassigned-import': 'error',
|
|
84
|
+
},
|
|
85
|
+
}, {
|
|
86
|
+
// A module-scope `let` is a global, and the functions that read it
|
|
87
|
+
// cannot be closures over it, so their other dependencies get drilled
|
|
88
|
+
// through their signatures. Keep mutable state in its owner.
|
|
89
|
+
name: 'zukhruf/base/module-state',
|
|
90
|
+
files: TYPESCRIPT,
|
|
91
|
+
ignores: [...TESTS, ...FIXTURES],
|
|
92
|
+
plugins: { functional },
|
|
93
|
+
rules: { 'functional/no-let': ['error', noLet()] },
|
|
94
|
+
}, {
|
|
95
|
+
// Typed lint: the project service gives each file its nearest
|
|
96
|
+
// tsconfig.json. The root comes from where ESLint runs, walked up to the
|
|
97
|
+
// workspace, so it is right under `nx lint` (cwd = the project).
|
|
98
|
+
name: 'zukhruf/base/typed',
|
|
99
|
+
files: TYPESCRIPT,
|
|
100
|
+
languageOptions: {
|
|
101
|
+
parserOptions: {
|
|
102
|
+
projectService: true,
|
|
103
|
+
tsconfigRootDir: workspaceRootOf(process.cwd()) ?? process.cwd(),
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
rules: {
|
|
107
|
+
// A promise nobody awaits has nowhere to send its rejection; state
|
|
108
|
+
// the intent with `void`, never `.catch(() => {})`.
|
|
109
|
+
'@typescript-eslint/no-floating-promises': [
|
|
110
|
+
'error',
|
|
111
|
+
noFloatingPromises(),
|
|
112
|
+
],
|
|
113
|
+
// A promise-returning function in a void slot loses its rejection
|
|
114
|
+
// the same way.
|
|
115
|
+
'@typescript-eslint/no-misused-promises': [
|
|
116
|
+
'error',
|
|
117
|
+
{ checksConditionals: false, checksSpreads: false },
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
}),
|
|
121
|
+
};
|
|
122
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/base/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface NoLetOptions {
|
|
2
|
+
/** Module-scope `let` names to allow, as regexes. */
|
|
3
|
+
ignoreIdentifierPattern?: string[];
|
|
4
|
+
}
|
|
5
|
+
/** Options for `functional/no-let`: no mutable state at module scope; locals and loop counters stay legal. */
|
|
6
|
+
export declare function noLet({ ignoreIdentifierPattern }?: NoLetOptions): {
|
|
7
|
+
allowInFunctions: boolean;
|
|
8
|
+
ignoreIdentifierPattern: string[];
|
|
9
|
+
};
|
|
10
|
+
export interface SafeCall {
|
|
11
|
+
from: 'file' | 'lib' | 'package';
|
|
12
|
+
name: string | string[];
|
|
13
|
+
package?: string;
|
|
14
|
+
path?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface NoFloatingPromisesOptions {
|
|
17
|
+
allowForKnownSafeCalls?: SafeCall[];
|
|
18
|
+
}
|
|
19
|
+
/** Options for `@typescript-eslint/no-floating-promises`, with node:test's calls already allowed. */
|
|
20
|
+
export declare function noFloatingPromises({ allowForKnownSafeCalls, }?: NoFloatingPromisesOptions): {
|
|
21
|
+
allowForKnownSafeCalls: SafeCall[];
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/base/options.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,uBAAuB,CAAC,EAAE,MAAM,EAAE,CAAC;CACpC;AAED,8GAA8G;AAC9G,wBAAgB,KAAK,CAAC,EAAE,uBAA4B,EAAE,GAAE,YAAiB;;;EAExE;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;IACjC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,yBAAyB;IACxC,sBAAsB,CAAC,EAAE,QAAQ,EAAE,CAAC;CACrC;AASD,qGAAqG;AACrG,wBAAgB,kBAAkB,CAAC,EACjC,sBAA2B,GAC5B,GAAE,yBAA8B;;EAIhC"}
|