@zukhruf/eslint 0.2.0 → 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 +68 -14
- package/dist/async-values/no-promise-field.d.ts +3 -0
- package/dist/async-values/no-promise-field.d.ts.map +1 -0
- package/dist/async-values/no-promise-field.js +63 -0
- package/dist/async-values/no-promise-field.js.map +1 -0
- package/dist/authoring/rule-module.d.ts +2 -0
- package/dist/authoring/rule-module.d.ts.map +1 -1
- package/dist/authoring/rule-module.js +7 -0
- package/dist/authoring/rule-module.js.map +1 -1
- package/dist/base/config.d.ts.map +1 -1
- package/dist/base/config.js +20 -1
- package/dist/base/config.js.map +1 -1
- package/dist/class-state/no-phase-flag.d.ts +3 -0
- package/dist/class-state/no-phase-flag.d.ts.map +1 -0
- package/dist/class-state/no-phase-flag.js +110 -0
- package/dist/class-state/no-phase-flag.js.map +1 -0
- package/dist/hermes/no-missing-console.d.ts +3 -0
- package/dist/hermes/no-missing-console.d.ts.map +1 -0
- package/dist/hermes/no-missing-console.js +16 -0
- package/dist/hermes/no-missing-console.js.map +1 -0
- package/dist/hermes/plugin.d.ts +19 -0
- package/dist/hermes/plugin.d.ts.map +1 -0
- package/dist/hermes/plugin.js +39 -0
- package/dist/hermes/plugin.js.map +1 -0
- package/dist/language/no-enum.js +1 -1
- package/dist/language/no-enum.js.map +1 -1
- package/dist/manifest/dependency-checks.d.ts +11 -0
- package/dist/manifest/dependency-checks.d.ts.map +1 -0
- package/dist/manifest/dependency-checks.js +91 -0
- package/dist/manifest/dependency-checks.js.map +1 -0
- package/dist/manifest/manifest-policy.d.ts +27 -0
- package/dist/manifest/manifest-policy.d.ts.map +1 -0
- package/dist/manifest/manifest-policy.js +27 -0
- package/dist/manifest/manifest-policy.js.map +1 -0
- package/dist/manifest/plugin.d.ts +20 -0
- package/dist/manifest/plugin.d.ts.map +1 -0
- package/dist/manifest/plugin.js +27 -0
- package/dist/manifest/plugin.js.map +1 -0
- package/dist/manifest/project-shape.d.ts +19 -0
- package/dist/manifest/project-shape.d.ts.map +1 -0
- package/dist/manifest/project-shape.js +37 -0
- package/dist/manifest/project-shape.js.map +1 -0
- package/dist/nx.d.ts +3 -0
- package/dist/nx.d.ts.map +1 -1
- package/dist/nx.js +3 -0
- package/dist/nx.js.map +1 -1
- package/dist/react-native.d.ts +2 -0
- package/dist/react-native.d.ts.map +1 -0
- package/dist/react-native.js +2 -0
- package/dist/react-native.js.map +1 -0
- package/dist/workspace/project-manifest.d.ts +2 -0
- package/dist/workspace/project-manifest.d.ts.map +1 -1
- package/dist/workspace/project-manifest.js +2 -1
- package/dist/workspace/project-manifest.js.map +1 -1
- package/dist/workspace/workspace-packages.d.ts +7 -0
- package/dist/workspace/workspace-packages.d.ts.map +1 -0
- package/dist/workspace/workspace-packages.js +35 -0
- package/dist/workspace/workspace-packages.js.map +1 -0
- package/package.json +13 -2
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ You need ESLint 9.30 or later, and Node.js 24 or later.
|
|
|
22
22
|
npm install --save-dev @zukhruf/eslint
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
Its peers are `eslint`, `@eslint/js`, `typescript-eslint`, `typescript`, `eslint-plugin-import-x`, `eslint-plugin-functional` and `jsonc-eslint-parser`. `@zukhruf/eslint/nx` also needs `@nx/eslint-plugin`. npm installs the peers for you. With `legacy-peer-deps=true` in `.npmrc`, npm does not, so add them to your devDependencies.
|
|
25
|
+
Its peers are `eslint`, `@eslint/js`, `typescript-eslint`, `typescript`, `eslint-plugin-import-x`, `eslint-plugin-functional` and `jsonc-eslint-parser`. `@zukhruf/eslint/nx` also needs `@nx/eslint-plugin`, and `@zukhruf/eslint/react-native` needs `eslint-plugin-es-x`. npm installs the peers for you. With `legacy-peer-deps=true` in `.npmrc`, npm does not, so add them to your devDependencies.
|
|
26
26
|
|
|
27
27
|
```js
|
|
28
28
|
// eslint.config.mjs
|
|
@@ -40,22 +40,26 @@ Put framework presets (for example Nx's `flat/react`) **before** the zukhruf con
|
|
|
40
40
|
|
|
41
41
|
## Configs
|
|
42
42
|
|
|
43
|
-
| Config | Files | Rules
|
|
44
|
-
| ----------------- | --------------------- |
|
|
45
|
-
| `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-playwright-test`; typed lint: `no-floating-promises`, `no-misused-promises` |
|
|
46
|
-
| `tests` | test files | `no-test-lifecycle-hooks`, `require-msw-error-on-unhandled-request`
|
|
47
|
-
| `diagnostics` | source, not tests | `no-fabricated-fallback`, `no-hardcoded-id-shape`, `consistent-event-name`
|
|
48
|
-
| `env` | TypeScript, not tests | `no-undeclared-process-env`, `no-single-env-read-wrapper`, `no-default-for-path-env`
|
|
49
|
-
| `packaged-app` | TypeScript, not tests | `no-bare-spawn`
|
|
50
|
-
| `react` | `.tsx`, `.jsx` | `require-combobox-popover-modal`
|
|
51
|
-
| `react-router` | source | `require-loader-data-type-argument`; outside tests: `no-raw-route-path`, `no-internal-anchor`
|
|
52
|
-
| `hono` | TypeScript, not tests | `no-untyped-empty-json`
|
|
53
|
-
| `tailwind` | source | `no-h-screen`, `no-arbitrary-z-index`, `no-will-change`
|
|
54
|
-
| `pulumi` | TypeScript | `require-server-replace-guard`
|
|
55
|
-
| `nx-project-json` | `project.json` | `no-missing-asset-input`
|
|
43
|
+
| Config | Files | Rules |
|
|
44
|
+
| ----------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
45
|
+
| `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-playwright-test`; typed lint: `no-floating-promises`, `no-misused-promises`, `only-throw-error`, `switch-exhaustiveness-check` (a `default` counts as exhaustive), `zukhruf/no-promise-field`, `zukhruf/no-phase-flag` |
|
|
46
|
+
| `tests` | test files | `no-test-lifecycle-hooks`, `require-msw-error-on-unhandled-request` |
|
|
47
|
+
| `diagnostics` | source, not tests | `no-fabricated-fallback`, `no-hardcoded-id-shape`, `consistent-event-name` |
|
|
48
|
+
| `env` | TypeScript, not tests | `no-undeclared-process-env`, `no-single-env-read-wrapper`, `no-default-for-path-env` |
|
|
49
|
+
| `packaged-app` | TypeScript, not tests | `no-bare-spawn` |
|
|
50
|
+
| `react` | `.tsx`, `.jsx` | `require-combobox-popover-modal` |
|
|
51
|
+
| `react-router` | source | `require-loader-data-type-argument`; outside tests: `no-raw-route-path`, `no-internal-anchor` |
|
|
52
|
+
| `hono` | TypeScript, not tests | `no-untyped-empty-json` |
|
|
53
|
+
| `tailwind` | source | `no-h-screen`, `no-arbitrary-z-index`, `no-will-change` |
|
|
54
|
+
| `pulumi` | TypeScript | `require-server-replace-guard` |
|
|
55
|
+
| `nx-project-json` | `project.json` | `no-missing-asset-input` |
|
|
56
56
|
|
|
57
57
|
`base` turns on typed lint (`parserOptions.projectService`). Each linted TypeScript file must be in a tsconfig.
|
|
58
58
|
|
|
59
|
+
`zukhruf/no-promise-field` reads each class field's type, so it also sees a promise behind an alias, in a type argument (`Map<string, Promise<X>>`), or inferred from the initializer. A field that holds a function returning a promise is fine: that is a memoized function. To wait until something has happened, use a state object or a one-shot latch.
|
|
60
|
+
|
|
61
|
+
`zukhruf/no-phase-flag` reads each class field's type too. It reports a field that is a boolean, or that can be `undefined`, when code outside the constructor sets it. A callback that the constructor creates runs later, so its assignment counts as outside. Such a field records a phase, and every reader must know which phase holds. Push the fact as an event, wait on a one-shot latch, keep the phases in state objects, or ask the platform for the fact.
|
|
62
|
+
|
|
59
63
|
### Options for a repo
|
|
60
64
|
|
|
61
65
|
Give repo-specific values as rule options. For example:
|
|
@@ -128,6 +132,56 @@ An island is an Nx project tagged `layer:island`. Its tags come from its `projec
|
|
|
128
132
|
'island/dependency-checks': ['error', dependencyPolicy({ ignoredDependencies: ['electron'] })]
|
|
129
133
|
```
|
|
130
134
|
|
|
135
|
+
### Every other manifest
|
|
136
|
+
|
|
137
|
+
`manifest/recommended` checks the `package.json` of every project that is not an island. What a manifest must declare depends on the project's shape:
|
|
138
|
+
|
|
139
|
+
- A **bundled** project is an application whose build inlines the workspace packages it imports. Their manifests are not there when it is deployed, so its own manifest declares every npm package they pull in. Workspace packages themselves are not demanded.
|
|
140
|
+
- Every other project is **unbundled**. It declares what its own code imports.
|
|
141
|
+
|
|
142
|
+
An application bundles unless its build is `nx:noop`, or esbuild with `bundle: false`. A build target that an Nx plugin infers (Vite's, for example) counts as bundling.
|
|
143
|
+
|
|
144
|
+
```js
|
|
145
|
+
import island, { manifest } from '@zukhruf/eslint/nx';
|
|
146
|
+
|
|
147
|
+
export default defineConfig({
|
|
148
|
+
plugins: { island, manifest },
|
|
149
|
+
extends: ['island/recommended', 'manifest/recommended'],
|
|
150
|
+
rules: {
|
|
151
|
+
'manifest/dependency-checks': [
|
|
152
|
+
'error',
|
|
153
|
+
{
|
|
154
|
+
ignoredDependencies: ['electron'],
|
|
155
|
+
projects: {
|
|
156
|
+
'apps/website': { ignoredDependencies: ['@radix-ui/react-slot'] },
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The rule decides each manifest's options itself, so your options add to the shared policy and never replace it. `projects` keys are folders relative to the workspace root. Like Nx's own rule, it needs the project graph that `nx` caches; without it, the rule checks nothing. It reads the workspace's package names once per process, so an editor's ESLint server sees a newly added workspace package only after a restart.
|
|
165
|
+
|
|
166
|
+
## React Native: Hermes
|
|
167
|
+
|
|
168
|
+
`@zukhruf/eslint/react-native` needs `eslint-plugin-es-x`. es-x 10 needs ESLint 10.6 or later; on ESLint 9, install `eslint-plugin-es-x@9`. `hermes/recommended` checks the code that ships in the app, and leaves tests, config files and scripts alone, because those run in Node:
|
|
169
|
+
|
|
170
|
+
- the `Intl` APIs Hermes does not have: es-x's `no-intl-displaynames`, `no-intl-durationformat`, `no-intl-listformat`, `no-intl-locale`, `no-intl-pluralrules`, `no-intl-relativetimeformat` and `no-intl-segmenter`;
|
|
171
|
+
- Node's built-in modules: `import-x/no-nodejs-modules`;
|
|
172
|
+
- `hermes/no-missing-console`: `console.clear`, `dir`, `dirxml`, `profile`, `profileEnd` and `timeLog`, which React Native release builds do not have, and `console[name]` with a computed name.
|
|
173
|
+
|
|
174
|
+
```js
|
|
175
|
+
import hermes from '@zukhruf/eslint/react-native';
|
|
176
|
+
|
|
177
|
+
export default defineConfig({
|
|
178
|
+
plugins: { zukhruf, hermes },
|
|
179
|
+
extends: ['zukhruf/base', 'hermes/recommended'],
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
If the app installs an npm polyfill that has a Node module's name, allow it: `'import-x/no-nodejs-modules': ['error', { allow: ['buffer'] }]`.
|
|
184
|
+
|
|
131
185
|
## Development
|
|
132
186
|
|
|
133
187
|
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.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-promise-field.d.ts","sourceRoot":"","sources":["../../src/async-values/no-promise-field.ts"],"names":[],"mappings":";AAqFA,wBAAgC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
import { isThenableType, isTypeReference, unionConstituents, } from 'ts-api-utils';
|
|
3
|
+
import { eslintRule } from "../authoring/rule-module.js";
|
|
4
|
+
/**
|
|
5
|
+
* Whether `type` is a promise, or carries one as a type argument
|
|
6
|
+
* (`Map<string, Promise<X>>`, `Promise<X>[]`). A function type is not entered:
|
|
7
|
+
* a field that holds `() => Promise<X>` holds the memoized function this rule
|
|
8
|
+
* asks for.
|
|
9
|
+
*/
|
|
10
|
+
function holdsPromise(checker, node, type, seen = new Set()) {
|
|
11
|
+
if (seen.has(type))
|
|
12
|
+
return false;
|
|
13
|
+
seen.add(type);
|
|
14
|
+
if (isThenableType(checker, node, type))
|
|
15
|
+
return true;
|
|
16
|
+
return unionConstituents(type).some((part) => isTypeReference(part) &&
|
|
17
|
+
checker
|
|
18
|
+
.getTypeArguments(part)
|
|
19
|
+
.some((argument) => holdsPromise(checker, node, argument, seen)));
|
|
20
|
+
}
|
|
21
|
+
/** The node that names a field: its key, or a parameter property's binding. */
|
|
22
|
+
function fieldName(node) {
|
|
23
|
+
if (node.type !== 'TSParameterProperty')
|
|
24
|
+
return node.key;
|
|
25
|
+
return node.parameter.type === 'AssignmentPattern'
|
|
26
|
+
? node.parameter.left
|
|
27
|
+
: node.parameter;
|
|
28
|
+
}
|
|
29
|
+
const rule = ESLintUtils.RuleCreator.withoutDocs({
|
|
30
|
+
meta: {
|
|
31
|
+
type: 'problem',
|
|
32
|
+
docs: {
|
|
33
|
+
description: 'Disallow class fields that hold a promise, which caches its rejection and races when reset',
|
|
34
|
+
},
|
|
35
|
+
schema: [],
|
|
36
|
+
messages: {
|
|
37
|
+
promiseField: 'Do not keep a promise in a field: it caches a rejection, can reject before anyone awaits it, and races when it is reset. Memoize the async function (p-memoize), model the phase as a State object, or wait on a one-shot latch.',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
defaultOptions: [],
|
|
41
|
+
create(context) {
|
|
42
|
+
// Throws the standard "requires type information" error without typed lint.
|
|
43
|
+
const services = ESLintUtils.getParserServices(context);
|
|
44
|
+
const checker = services.program.getTypeChecker();
|
|
45
|
+
const check = (node) => {
|
|
46
|
+
const name = fieldName(node);
|
|
47
|
+
const type = services.getTypeAtLocation(name);
|
|
48
|
+
const tsNode = services.esTreeNodeToTSNodeMap.get(name);
|
|
49
|
+
if (holdsPromise(checker, tsNode, type)) {
|
|
50
|
+
context.report({ node, messageId: 'promiseField' });
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
PropertyDefinition: check,
|
|
55
|
+
AccessorProperty: check,
|
|
56
|
+
TSAbstractPropertyDefinition: check,
|
|
57
|
+
TSAbstractAccessorProperty: check,
|
|
58
|
+
TSParameterProperty: check,
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
export default eslintRule(rule);
|
|
63
|
+
//# sourceMappingURL=no-promise-field.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-promise-field.js","sourceRoot":"","sources":["../../src/async-values/no-promise-field.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AACtE,OAAO,EACL,cAAc,EACd,eAAe,EACf,iBAAiB,GAClB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAEzD;;;;;GAKG;AACH,SAAS,YAAY,CACnB,OAAuB,EACvB,IAAa,EACb,IAAa,EACb,OAAO,IAAI,GAAG,EAAW;IAEzB,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACjC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACf,IAAI,cAAc,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACrD,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC,IAAI,CACjC,CAAC,IAAI,EAAE,EAAE,CACP,eAAe,CAAC,IAAI,CAAC;QACrB,OAAO;aACJ,gBAAgB,CAAC,IAAI,CAAC;aACtB,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CACrE,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,SAAS,SAAS,CAChB,IAKgC;IAEhC,IAAI,IAAI,CAAC,IAAI,KAAK,qBAAqB;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC;IACzD,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,KAAK,mBAAmB;QAChD,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI;QACrB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;AACrB,CAAC;AAED,MAAM,IAAI,GAAG,WAAW,CAAC,WAAW,CAAC,WAAW,CAAC;IAC/C,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,4FAA4F;SAC/F;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,YAAY,EACV,kOAAkO;SACrO;KACF;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,4EAA4E;QAC5E,MAAM,QAAQ,GAAG,WAAW,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QACxD,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC;QAClD,MAAM,KAAK,GAAG,CAAC,IAAqC,EAAE,EAAE;YACtD,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;YAC7B,MAAM,IAAI,GAAG,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAC9C,MAAM,MAAM,GAAG,QAAQ,CAAC,qBAAqB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACxD,IAAI,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC;gBACxC,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC,CAAC;YACtD,CAAC;QACH,CAAC,CAAC;QACF,OAAO;YACL,kBAAkB,EAAE,KAAK;YACzB,gBAAgB,EAAE,KAAK;YACvB,4BAA4B,EAAE,KAAK;YACnC,0BAA0B,EAAE,KAAK;YACjC,mBAAmB,EAAE,KAAK;SAC3B,CAAC;IACJ,CAAC;CACF,CAAC,CAAC;AAEH,eAAe,UAAU,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -5,6 +5,8 @@ import type { Rule } from 'eslint';
|
|
|
5
5
|
* ESLint calls lets this package reuse those rules without a type assertion.
|
|
6
6
|
*/
|
|
7
7
|
export declare function isRuleModule(value: unknown): value is Rule.RuleModule;
|
|
8
|
+
/** A rule written with typescript-eslint's `RuleCreator`, as ESLint's type. */
|
|
9
|
+
export declare function eslintRule(rule: unknown): Rule.RuleModule;
|
|
8
10
|
/**
|
|
9
11
|
* The rule `name` of an ESLint plugin; throws when the plugin no longer has it.
|
|
10
12
|
* Takes the plugin as `unknown`: typescript-eslint types its plugin without the
|
|
@@ -1 +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"}
|
|
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,+EAA+E;AAC/E,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC,UAAU,CAKzD;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,UAAU,CAOrE"}
|
|
@@ -7,6 +7,13 @@ import { isRecord } from "./ast.js";
|
|
|
7
7
|
export function isRuleModule(value) {
|
|
8
8
|
return isRecord(value) && typeof value['create'] === 'function';
|
|
9
9
|
}
|
|
10
|
+
/** A rule written with typescript-eslint's `RuleCreator`, as ESLint's type. */
|
|
11
|
+
export function eslintRule(rule) {
|
|
12
|
+
if (!isRuleModule(rule)) {
|
|
13
|
+
throw new TypeError('The value is not an ESLint rule.');
|
|
14
|
+
}
|
|
15
|
+
return rule;
|
|
16
|
+
}
|
|
10
17
|
/**
|
|
11
18
|
* The rule `name` of an ESLint plugin; throws when the plugin no longer has it.
|
|
12
19
|
* Takes the plugin as `unknown`: typescript-eslint types its plugin without the
|
|
@@ -1 +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"}
|
|
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,+EAA+E;AAC/E,MAAM,UAAU,UAAU,CAAC,IAAa;IACtC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,SAAS,CAAC,kCAAkC,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/base/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/base/config.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,KAAK,OAAO,EAAU,MAAM,eAAe,CAAC;AA2BrD;;;;GAIG;AACH,eAAO,MAAM,IAAI,EAAE,OAqHlB,CAAC"}
|
package/dist/base/config.js
CHANGED
|
@@ -4,6 +4,8 @@ import importX from 'eslint-plugin-import-x';
|
|
|
4
4
|
import { defineConfig } from 'eslint/config';
|
|
5
5
|
import globals from 'globals';
|
|
6
6
|
import tseslint from 'typescript-eslint';
|
|
7
|
+
import noPromiseField from "../async-values/no-promise-field.js";
|
|
8
|
+
import noPhaseFlag from "../class-state/no-phase-flag.js";
|
|
7
9
|
import { enable } from "../concept.js";
|
|
8
10
|
import { BUILD_OUTPUT, FIXTURES, JAVASCRIPT, SOURCE, TESTS, TYPESCRIPT, } from "../files.js";
|
|
9
11
|
import noPlaywrightTest from "../imports/no-playwright-test.js";
|
|
@@ -15,7 +17,12 @@ const importBans = {
|
|
|
15
17
|
'no-state-managers': noStateManagers,
|
|
16
18
|
'no-playwright-test': noPlaywrightTest,
|
|
17
19
|
};
|
|
18
|
-
const rules = {
|
|
20
|
+
const rules = {
|
|
21
|
+
'no-enum': noEnum,
|
|
22
|
+
'no-promise-field': noPromiseField,
|
|
23
|
+
'no-phase-flag': noPhaseFlag,
|
|
24
|
+
...importBans,
|
|
25
|
+
};
|
|
19
26
|
/**
|
|
20
27
|
* What every TypeScript repo gets: the recommended sets of @eslint/js and
|
|
21
28
|
* typescript-eslint, embedded first so a framework preset can never sit between
|
|
@@ -95,6 +102,7 @@ export const base = {
|
|
|
95
102
|
// workspace, so it is right under `nx lint` (cwd = the project).
|
|
96
103
|
name: 'zukhruf/base/typed',
|
|
97
104
|
files: TYPESCRIPT,
|
|
105
|
+
plugins,
|
|
98
106
|
languageOptions: {
|
|
99
107
|
parserOptions: {
|
|
100
108
|
projectService: true,
|
|
@@ -114,6 +122,17 @@ export const base = {
|
|
|
114
122
|
'error',
|
|
115
123
|
{ checksConditionals: false, checksSpreads: false },
|
|
116
124
|
],
|
|
125
|
+
// A thrown string or object has no stack, and `catch (error)` code
|
|
126
|
+
// reads `error.message`.
|
|
127
|
+
'@typescript-eslint/only-throw-error': 'error',
|
|
128
|
+
// A switch over a union that misses a member and has no `default`
|
|
129
|
+
// silently does nothing for it.
|
|
130
|
+
'@typescript-eslint/switch-exhaustiveness-check': [
|
|
131
|
+
'error',
|
|
132
|
+
{ considerDefaultExhaustiveForUnions: true },
|
|
133
|
+
],
|
|
134
|
+
'zukhruf/no-promise-field': 'error',
|
|
135
|
+
'zukhruf/no-phase-flag': 'error',
|
|
117
136
|
},
|
|
118
137
|
}),
|
|
119
138
|
};
|
package/dist/base/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/base/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,UAAU,MAAM,0BAA0B,CAAC;AAClD,OAAO,OAAO,MAAM,wBAAwB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,QAAQ,MAAM,mBAAmB,CAAC;AAEzC,OAAO,EAAgB,MAAM,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,MAAM,EACN,KAAK,EACL,UAAU,GACX,MAAM,aAAa,CAAC;AACrB,OAAO,gBAAgB,MAAM,kCAAkC,CAAC;AAChE,OAAO,eAAe,MAAM,iCAAiC,CAAC;AAC9D,OAAO,MAAM,MAAM,wBAAwB,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAEzD,MAAM,UAAU,GAAG;IACjB,mBAAmB,EAAE,eAAe;IACpC,oBAAoB,EAAE,gBAAgB;CACvC,CAAC;AAEF,MAAM,KAAK,GAAG,
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/base/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-phase-flag.d.ts","sourceRoot":"","sources":["../../src/class-state/no-phase-flag.ts"],"names":[],"mappings":";AAsIA,wBAAgC"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
import { isBooleanLiteralType, isIntrinsicBooleanType, isIntrinsicUndefinedType, unionConstituents, } from 'ts-api-utils';
|
|
3
|
+
import { eslintRule } from "../authoring/rule-module.js";
|
|
4
|
+
/** Whether `type` is a flag, or can be `undefined` until something sets it. */
|
|
5
|
+
function holdsFlag(type) {
|
|
6
|
+
return unionConstituents(type).some((part) => isBooleanLiteralType(part) ||
|
|
7
|
+
isIntrinsicBooleanType(part) ||
|
|
8
|
+
isIntrinsicUndefinedType(part));
|
|
9
|
+
}
|
|
10
|
+
/** A private field and a public field of the same name are different fields. */
|
|
11
|
+
function keyOf(name) {
|
|
12
|
+
if (name.type === 'PrivateIdentifier')
|
|
13
|
+
return `#${name.name}`;
|
|
14
|
+
if (name.type === 'Identifier')
|
|
15
|
+
return name.name;
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Whether an assignment runs while the constructor runs. A callback that the
|
|
20
|
+
* constructor creates runs later, for example on an event, so its assignment
|
|
21
|
+
* changes the field after construction.
|
|
22
|
+
*/
|
|
23
|
+
function inConstructor(ancestors) {
|
|
24
|
+
for (const node of ancestors.toReversed()) {
|
|
25
|
+
if (node.type === 'ArrowFunctionExpression')
|
|
26
|
+
return false;
|
|
27
|
+
if (node.type === 'FunctionExpression' ||
|
|
28
|
+
node.type === 'FunctionDeclaration') {
|
|
29
|
+
return (node.parent.type === 'MethodDefinition' &&
|
|
30
|
+
node.parent.kind === 'constructor');
|
|
31
|
+
}
|
|
32
|
+
if (node.type === 'PropertyDefinition' ||
|
|
33
|
+
node.type === 'StaticBlock' ||
|
|
34
|
+
node.type === 'ClassBody') {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const rule = ESLintUtils.RuleCreator.withoutDocs({
|
|
41
|
+
meta: {
|
|
42
|
+
type: 'problem',
|
|
43
|
+
docs: {
|
|
44
|
+
description: 'Disallow boolean or optional class fields that change after the constructor, which store a phase',
|
|
45
|
+
},
|
|
46
|
+
schema: [],
|
|
47
|
+
messages: {
|
|
48
|
+
phaseFlag: 'Do not keep a phase in a flag or optional field that changes after the constructor: every reader must know which phase holds. Model the phases as State objects, wait on a one-shot latch, push the fact as an event, or ask the platform for it.',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
defaultOptions: [],
|
|
52
|
+
create(context) {
|
|
53
|
+
// Throws the standard "requires type information" error without typed lint.
|
|
54
|
+
const services = ESLintUtils.getParserServices(context);
|
|
55
|
+
const classes = [];
|
|
56
|
+
const current = () => classes.at(-1);
|
|
57
|
+
const record = (field) => {
|
|
58
|
+
const key = keyOf(field.name);
|
|
59
|
+
if (key)
|
|
60
|
+
current()?.fields.set(key, field);
|
|
61
|
+
};
|
|
62
|
+
return {
|
|
63
|
+
ClassBody() {
|
|
64
|
+
classes.push({ fields: new Map(), changed: new Set() });
|
|
65
|
+
},
|
|
66
|
+
'ClassBody > PropertyDefinition'(node) {
|
|
67
|
+
if (node.static || node.readonly || node.computed)
|
|
68
|
+
return;
|
|
69
|
+
record({ node, name: node.key, optional: node.optional });
|
|
70
|
+
},
|
|
71
|
+
TSParameterProperty(node) {
|
|
72
|
+
if (node.readonly || node.static)
|
|
73
|
+
return;
|
|
74
|
+
const name = node.parameter.type === 'AssignmentPattern'
|
|
75
|
+
? node.parameter.left
|
|
76
|
+
: node.parameter;
|
|
77
|
+
const optional = 'optional' in name && name.optional === true;
|
|
78
|
+
record({ node, name, optional });
|
|
79
|
+
},
|
|
80
|
+
AssignmentExpression(node) {
|
|
81
|
+
const target = node.left;
|
|
82
|
+
if (target.type !== 'MemberExpression' ||
|
|
83
|
+
target.computed ||
|
|
84
|
+
target.object.type !== 'ThisExpression') {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (inConstructor(context.sourceCode.getAncestors(node)))
|
|
88
|
+
return;
|
|
89
|
+
const key = keyOf(target.property);
|
|
90
|
+
if (key)
|
|
91
|
+
current()?.changed.add(key);
|
|
92
|
+
},
|
|
93
|
+
'ClassBody:exit'() {
|
|
94
|
+
const body = classes.pop();
|
|
95
|
+
if (!body)
|
|
96
|
+
return;
|
|
97
|
+
for (const [key, field] of body.fields) {
|
|
98
|
+
if (!body.changed.has(key))
|
|
99
|
+
continue;
|
|
100
|
+
if (field.optional ||
|
|
101
|
+
holdsFlag(services.getTypeAtLocation(field.name))) {
|
|
102
|
+
context.report({ node: field.node, messageId: 'phaseFlag' });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
export default eslintRule(rule);
|
|
110
|
+
//# sourceMappingURL=no-phase-flag.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-phase-flag.js","sourceRoot":"","sources":["../../src/class-state/no-phase-flag.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-missing-console.d.ts","sourceRoot":"","sources":["../../src/hermes/no-missing-console.ts"],"names":[],"mappings":";AAOA,wBAWG"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { selectorRule } from "../authoring/selector-rule.js";
|
|
2
|
+
// React Native's console polyfill (@react-native/js-polyfills) forwards these
|
|
3
|
+
// to the host console only under __DEV__, and never defines timeLog. A release
|
|
4
|
+
// build has none of them.
|
|
5
|
+
const MISSING = '/^(clear|dir|dirxml|profile|profileEnd|timeLog)$/';
|
|
6
|
+
export default selectorRule({
|
|
7
|
+
description: 'Disallow console methods that React Native release builds do not have',
|
|
8
|
+
selectors: [
|
|
9
|
+
`MemberExpression[object.name='console'][computed=false][property.name=${MISSING}]`,
|
|
10
|
+
`MemberExpression[object.name='console'][computed=true][property.value=${MISSING}]`,
|
|
11
|
+
// A computed name can be any of the missing methods.
|
|
12
|
+
`MemberExpression[object.name='console'][computed=true]:not([property.type='Literal'])`,
|
|
13
|
+
],
|
|
14
|
+
message: 'React Native release builds have no console.clear, dir, dirxml, profile, profileEnd or timeLog: the call throws "undefined is not a function". A computed console[name] can name one of them. Use console.log, info, warn, error, debug, trace, table, group or assert.',
|
|
15
|
+
});
|
|
16
|
+
//# sourceMappingURL=no-missing-console.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-missing-console.js","sourceRoot":"","sources":["../../src/hermes/no-missing-console.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D,8EAA8E;AAC9E,+EAA+E;AAC/E,0BAA0B;AAC1B,MAAM,OAAO,GAAG,mDAAmD,CAAC;AAEpE,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT,uEAAuE;IACzE,SAAS,EAAE;QACT,yEAAyE,OAAO,GAAG;QACnF,yEAAyE,OAAO,GAAG;QACnF,qDAAqD;QACrD,uFAAuF;KACxF;IACD,OAAO,EACL,yQAAyQ;CAC5Q,CAAC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Linter } from 'eslint';
|
|
2
|
+
/**
|
|
3
|
+
* Hermes: the JavaScript engine of React Native. Code that ships in the app
|
|
4
|
+
* runs without Node's built-in modules, with only part of `Intl`, and with a
|
|
5
|
+
* console that lacks some methods in release builds. Tests, config files and
|
|
6
|
+
* scripts run in Node, so they are left alone.
|
|
7
|
+
*/
|
|
8
|
+
declare const plugin: {
|
|
9
|
+
meta: {
|
|
10
|
+
name: string;
|
|
11
|
+
namespace: string;
|
|
12
|
+
};
|
|
13
|
+
rules: {
|
|
14
|
+
'no-missing-console': import("eslint").Rule.RuleModule;
|
|
15
|
+
};
|
|
16
|
+
configs: Record<string, Linter.Config<import("@eslint/core", { with: { "resolution-mode": "require" } }).RulesConfig>[]>;
|
|
17
|
+
};
|
|
18
|
+
export default plugin;
|
|
19
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/hermes/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,MAAM,EAAE,MAAM,QAAQ,CAAC;AAW7C;;;;;GAKG;AACH,QAAA,MAAM,MAAM;;;;;;;;;CAIa,CAAC;AAuB1B,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import esx from 'eslint-plugin-es-x';
|
|
2
|
+
import importX from 'eslint-plugin-import-x';
|
|
3
|
+
import { CONFIG_FILES, SCRIPTS, SOURCE, TESTS } from "../files.js";
|
|
4
|
+
import noMissingConsole from "./no-missing-console.js";
|
|
5
|
+
const rules = { 'no-missing-console': noMissingConsole };
|
|
6
|
+
const configs = {};
|
|
7
|
+
/**
|
|
8
|
+
* Hermes: the JavaScript engine of React Native. Code that ships in the app
|
|
9
|
+
* runs without Node's built-in modules, with only part of `Intl`, and with a
|
|
10
|
+
* console that lacks some methods in release builds. Tests, config files and
|
|
11
|
+
* scripts run in Node, so they are left alone.
|
|
12
|
+
*/
|
|
13
|
+
const plugin = {
|
|
14
|
+
meta: { name: '@zukhruf/eslint/react-native', namespace: 'hermes' },
|
|
15
|
+
rules,
|
|
16
|
+
configs,
|
|
17
|
+
};
|
|
18
|
+
configs['recommended'] = [
|
|
19
|
+
{
|
|
20
|
+
name: 'hermes/recommended',
|
|
21
|
+
files: SOURCE,
|
|
22
|
+
ignores: [...TESTS, ...CONFIG_FILES, ...SCRIPTS],
|
|
23
|
+
plugins: { hermes: plugin, 'es-x': esx, 'import-x': importX },
|
|
24
|
+
rules: {
|
|
25
|
+
'hermes/no-missing-console': 'error',
|
|
26
|
+
// Hermes implements Collator, DateTimeFormat and NumberFormat only.
|
|
27
|
+
'es-x/no-intl-displaynames': 'error',
|
|
28
|
+
'es-x/no-intl-durationformat': 'error',
|
|
29
|
+
'es-x/no-intl-listformat': 'error',
|
|
30
|
+
'es-x/no-intl-locale': 'error',
|
|
31
|
+
'es-x/no-intl-pluralrules': 'error',
|
|
32
|
+
'es-x/no-intl-relativetimeformat': 'error',
|
|
33
|
+
'es-x/no-intl-segmenter': 'error',
|
|
34
|
+
'import-x/no-nodejs-modules': 'error',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
export default plugin;
|
|
39
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/hermes/plugin.ts"],"names":[],"mappings":"AACA,OAAO,GAAG,MAAM,oBAAoB,CAAC;AACrC,OAAO,OAAO,MAAM,wBAAwB,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,gBAAgB,MAAM,yBAAyB,CAAC;AAEvD,MAAM,KAAK,GAAG,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,CAAC;AAEzD,MAAM,OAAO,GAAoC,EAAE,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,MAAM,GAAG;IACb,IAAI,EAAE,EAAE,IAAI,EAAE,8BAA8B,EAAE,SAAS,EAAE,QAAQ,EAAE;IACnE,KAAK;IACL,OAAO;CACgB,CAAC;AAE1B,OAAO,CAAC,aAAa,CAAC,GAAG;IACvB;QACE,IAAI,EAAE,oBAAoB;QAC1B,KAAK,EAAE,MAAM;QACb,OAAO,EAAE,CAAC,GAAG,KAAK,EAAE,GAAG,YAAY,EAAE,GAAG,OAAO,CAAC;QAChD,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE;QAC7D,KAAK,EAAE;YACL,2BAA2B,EAAE,OAAO;YACpC,oEAAoE;YACpE,2BAA2B,EAAE,OAAO;YACpC,6BAA6B,EAAE,OAAO;YACtC,yBAAyB,EAAE,OAAO;YAClC,qBAAqB,EAAE,OAAO;YAC9B,0BAA0B,EAAE,OAAO;YACnC,iCAAiC,EAAE,OAAO;YAC1C,wBAAwB,EAAE,OAAO;YACjC,4BAA4B,EAAE,OAAO;SACtC;KACF;CACF,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
package/dist/language/no-enum.js
CHANGED
|
@@ -2,6 +2,6 @@ import { selectorRule } from "../authoring/selector-rule.js";
|
|
|
2
2
|
export default selectorRule({
|
|
3
3
|
description: 'Disallow TypeScript enums, which Node cannot run with types stripped',
|
|
4
4
|
selectors: ['TSEnumDeclaration'],
|
|
5
|
-
message: 'No TS enums — they need a runtime transform and break Node strip-only `.ts` execution (tsc/bundlers hide it). Use a `const` object + a union type instead.',
|
|
5
|
+
message: 'No TS enums — they need a runtime transform and break Node strip-only `.ts` execution (tsc/bundlers hide it). Use a `const` object + a union type under a different name instead (`const Colors = {…} as const; type Color = (typeof Colors)[keyof typeof Colors]`) — `no-redeclare` flags a value and type sharing a name.',
|
|
6
6
|
});
|
|
7
7
|
//# sourceMappingURL=no-enum.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-enum.js","sourceRoot":"","sources":["../../src/language/no-enum.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT,sEAAsE;IACxE,SAAS,EAAE,CAAC,mBAAmB,CAAC;IAChC,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"no-enum.js","sourceRoot":"","sources":["../../src/language/no-enum.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT,sEAAsE;IACxE,SAAS,EAAE,CAAC,mBAAmB,CAAC;IAChC,OAAO,EACL,6TAA6T;CAChU,CAAC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Rule } from 'eslint';
|
|
2
|
+
/**
|
|
3
|
+
* Nx's `dependency-checks`, with the options each manifest's project needs.
|
|
4
|
+
* Flat config gives a rule one set of options per file pattern, and a pattern
|
|
5
|
+
* cannot tell a bundled app from an unbundled one, so the rule decides per linted
|
|
6
|
+
* manifest. Islands are left to `island/dependency-checks`, so a finding is
|
|
7
|
+
* never reported twice.
|
|
8
|
+
*/
|
|
9
|
+
declare const rule: Rule.RuleModule;
|
|
10
|
+
export default rule;
|
|
11
|
+
//# sourceMappingURL=dependency-checks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependency-checks.d.ts","sourceRoot":"","sources":["../../src/manifest/dependency-checks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAsDnC;;;;;;GAMG;AACH,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UAuChB,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import nx from '@nx/eslint-plugin';
|
|
2
|
+
import { isRecord, stringsAt } from "../authoring/ast.js";
|
|
3
|
+
import { ruleOf } from "../authoring/rule-module.js";
|
|
4
|
+
import { inIsland } from "../island/island-scope.js";
|
|
5
|
+
import { projectOf } from "../workspace/project-manifest.js";
|
|
6
|
+
import { manifestPolicy, } from "./manifest-policy.js";
|
|
7
|
+
const nxDependencyChecks = ruleOf(nx, 'dependency-checks');
|
|
8
|
+
const strings = { type: 'array', items: { type: 'string' } };
|
|
9
|
+
const FLAGS = [
|
|
10
|
+
'checkMissingDependencies',
|
|
11
|
+
'checkObsoleteDependencies',
|
|
12
|
+
'checkVersionMismatches',
|
|
13
|
+
];
|
|
14
|
+
function projectsOf(value) {
|
|
15
|
+
if (!isRecord(value))
|
|
16
|
+
return {};
|
|
17
|
+
return Object.fromEntries(Object.entries(value).map(([folder, extras]) => [
|
|
18
|
+
folder,
|
|
19
|
+
{ ignoredDependencies: stringsAt(extras, 'ignoredDependencies') },
|
|
20
|
+
]));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The repo's options, as the schema let them through. A flag the repo left
|
|
24
|
+
* out stays out: an `undefined` would let Nx's own default (obsolete checks
|
|
25
|
+
* on) replace the policy's.
|
|
26
|
+
*/
|
|
27
|
+
function optionsOf(value) {
|
|
28
|
+
if (!isRecord(value))
|
|
29
|
+
return {};
|
|
30
|
+
const options = {
|
|
31
|
+
ignoredDependencies: stringsAt(value, 'ignoredDependencies'),
|
|
32
|
+
ignoredFiles: stringsAt(value, 'ignoredFiles'),
|
|
33
|
+
projects: projectsOf(value['projects']),
|
|
34
|
+
};
|
|
35
|
+
if ('buildTargets' in value) {
|
|
36
|
+
options.buildTargets = stringsAt(value, 'buildTargets');
|
|
37
|
+
}
|
|
38
|
+
for (const flag of FLAGS) {
|
|
39
|
+
const set = value[flag];
|
|
40
|
+
if (typeof set === 'boolean')
|
|
41
|
+
options[flag] = set;
|
|
42
|
+
}
|
|
43
|
+
return options;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Nx's `dependency-checks`, with the options each manifest's project needs.
|
|
47
|
+
* Flat config gives a rule one set of options per file pattern, and a pattern
|
|
48
|
+
* cannot tell a bundled app from an unbundled one, so the rule decides per linted
|
|
49
|
+
* manifest. Islands are left to `island/dependency-checks`, so a finding is
|
|
50
|
+
* never reported twice.
|
|
51
|
+
*/
|
|
52
|
+
const rule = {
|
|
53
|
+
meta: {
|
|
54
|
+
...nxDependencyChecks.meta,
|
|
55
|
+
docs: {
|
|
56
|
+
description: "Check that a package.json declares the packages its project needs, as the project's shape demands",
|
|
57
|
+
},
|
|
58
|
+
schema: [
|
|
59
|
+
{
|
|
60
|
+
type: 'object',
|
|
61
|
+
properties: {
|
|
62
|
+
ignoredDependencies: strings,
|
|
63
|
+
ignoredFiles: strings,
|
|
64
|
+
buildTargets: strings,
|
|
65
|
+
...Object.fromEntries(FLAGS.map((flag) => [flag, { type: 'boolean' }])),
|
|
66
|
+
projects: {
|
|
67
|
+
type: 'object',
|
|
68
|
+
additionalProperties: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
properties: { ignoredDependencies: strings },
|
|
71
|
+
additionalProperties: false,
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
additionalProperties: false,
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
create(context) {
|
|
80
|
+
const project = projectOf(context.physicalFilename);
|
|
81
|
+
if (!project || inIsland(context))
|
|
82
|
+
return {};
|
|
83
|
+
const options = manifestPolicy(project.root, optionsOf(context.options[0]));
|
|
84
|
+
const projectContext = Object.create(context, {
|
|
85
|
+
options: { value: [options] },
|
|
86
|
+
});
|
|
87
|
+
return nxDependencyChecks.create(projectContext);
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
export default rule;
|
|
91
|
+
//# sourceMappingURL=dependency-checks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependency-checks.js","sourceRoot":"","sources":["../../src/manifest/dependency-checks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type DependencyPolicyOptions } from '../nx-policy/dependency-policy.ts';
|
|
2
|
+
export interface ProjectExtras {
|
|
3
|
+
/** Packages this project's manifest may leave out, on top of the repo's. */
|
|
4
|
+
ignoredDependencies?: string[];
|
|
5
|
+
}
|
|
6
|
+
/** The project's shape decides `includeTransitiveDependencies`, so a repo does not set it. */
|
|
7
|
+
export interface ManifestPolicyOptions extends Omit<DependencyPolicyOptions, 'includeTransitiveDependencies'> {
|
|
8
|
+
/** Extras for one project, keyed by its folder relative to the workspace root. */
|
|
9
|
+
projects?: Record<string, ProjectExtras>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The complete `@nx/dependency-checks` options for the project at
|
|
13
|
+
* `projectRoot`: the shared policy, the repo's entries, the project's own
|
|
14
|
+
* extras, and what its shape demands. A bundled project declares the npm
|
|
15
|
+
* packages its workspace imports pull in, but not the workspace packages
|
|
16
|
+
* themselves, which its build inlines.
|
|
17
|
+
*/
|
|
18
|
+
export declare function manifestPolicy(projectRoot: string, { projects, ignoredDependencies, ...options }?: ManifestPolicyOptions): {
|
|
19
|
+
ignoredFiles: string[];
|
|
20
|
+
ignoredDependencies: string[];
|
|
21
|
+
includeTransitiveDependencies: boolean;
|
|
22
|
+
checkMissingDependencies: boolean;
|
|
23
|
+
checkObsoleteDependencies: boolean;
|
|
24
|
+
checkVersionMismatches: boolean;
|
|
25
|
+
buildTargets?: string[];
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=manifest-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest-policy.d.ts","sourceRoot":"","sources":["../../src/manifest/manifest-policy.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,uBAAuB,EAE7B,MAAM,mCAAmC,CAAC;AAK3C,MAAM,WAAW,aAAa;IAC5B,4EAA4E;IAC5E,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC;AAED,8FAA8F;AAC9F,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CACjD,uBAAuB,EACvB,+BAA+B,CAChC;IACC,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CAC1C;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,EACE,QAAa,EACb,mBAAwB,EACxB,GAAG,OAAO,EACX,GAAE,qBAA0B;;;;;;;;EAc9B"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { relative, sep } from 'node:path';
|
|
2
|
+
import { dependencyPolicy, } from "../nx-policy/dependency-policy.js";
|
|
3
|
+
import { workspacePackages } from "../workspace/workspace-packages.js";
|
|
4
|
+
import { workspaceRootOf } from "../workspace/workspace-root.js";
|
|
5
|
+
import { projectShape } from "./project-shape.js";
|
|
6
|
+
/**
|
|
7
|
+
* The complete `@nx/dependency-checks` options for the project at
|
|
8
|
+
* `projectRoot`: the shared policy, the repo's entries, the project's own
|
|
9
|
+
* extras, and what its shape demands. A bundled project declares the npm
|
|
10
|
+
* packages its workspace imports pull in, but not the workspace packages
|
|
11
|
+
* themselves, which its build inlines.
|
|
12
|
+
*/
|
|
13
|
+
export function manifestPolicy(projectRoot, { projects = {}, ignoredDependencies = [], ...options } = {}) {
|
|
14
|
+
const workspaceRoot = workspaceRootOf(projectRoot) ?? projectRoot;
|
|
15
|
+
const folder = relative(workspaceRoot, projectRoot).split(sep).join('/');
|
|
16
|
+
const bundled = projectShape(projectRoot) === 'bundled';
|
|
17
|
+
return dependencyPolicy({
|
|
18
|
+
...options,
|
|
19
|
+
includeTransitiveDependencies: bundled,
|
|
20
|
+
ignoredDependencies: [
|
|
21
|
+
...(bundled ? workspacePackages(workspaceRoot) : []),
|
|
22
|
+
...ignoredDependencies,
|
|
23
|
+
...(projects[folder]?.ignoredDependencies ?? []),
|
|
24
|
+
],
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=manifest-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest-policy.js","sourceRoot":"","sources":["../../src/manifest/manifest-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAEL,gBAAgB,GACjB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAgBlD;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,WAAmB,EACnB,EACE,QAAQ,GAAG,EAAE,EACb,mBAAmB,GAAG,EAAE,EACxB,GAAG,OAAO,KACe,EAAE;IAE7B,MAAM,aAAa,GAAG,eAAe,CAAC,WAAW,CAAC,IAAI,WAAW,CAAC;IAClE,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzE,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,CAAC,KAAK,SAAS,CAAC;IACxD,OAAO,gBAAgB,CAAC;QACtB,GAAG,OAAO;QACV,6BAA6B,EAAE,OAAO;QACtC,mBAAmB,EAAE;YACnB,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACpD,GAAG,mBAAmB;YACtB,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,mBAAmB,IAAI,EAAE,CAAC;SACjD;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Linter } from 'eslint';
|
|
2
|
+
/**
|
|
3
|
+
* Manifests: every project's package.json declares the npm packages its
|
|
4
|
+
* shipped code needs. How many depends on the project's shape: a bundled app
|
|
5
|
+
* declares the whole closure its workspace imports pull in, an unbundled
|
|
6
|
+
* project only its own imports. Islands are checked by
|
|
7
|
+
* `island/dependency-checks`.
|
|
8
|
+
*/
|
|
9
|
+
declare const plugin: {
|
|
10
|
+
meta: {
|
|
11
|
+
name: string;
|
|
12
|
+
namespace: string;
|
|
13
|
+
};
|
|
14
|
+
rules: {
|
|
15
|
+
'dependency-checks': import("eslint").Rule.RuleModule;
|
|
16
|
+
};
|
|
17
|
+
configs: Record<string, Linter.Config<import("@eslint/core", { with: { "resolution-mode": "require" } }).RulesConfig>[]>;
|
|
18
|
+
};
|
|
19
|
+
export default plugin;
|
|
20
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/manifest/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,MAAM,EAAE,MAAM,QAAQ,CAAC;AAQ7C;;;;;;GAMG;AACH,QAAA,MAAM,MAAM;;;;;;;;;CAIa,CAAC;AAY1B,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as jsonc from 'jsonc-eslint-parser';
|
|
2
|
+
import { PACKAGE_JSON } from "../files.js";
|
|
3
|
+
import dependencyChecks from "./dependency-checks.js";
|
|
4
|
+
const configs = {};
|
|
5
|
+
/**
|
|
6
|
+
* Manifests: every project's package.json declares the npm packages its
|
|
7
|
+
* shipped code needs. How many depends on the project's shape: a bundled app
|
|
8
|
+
* declares the whole closure its workspace imports pull in, an unbundled
|
|
9
|
+
* project only its own imports. Islands are checked by
|
|
10
|
+
* `island/dependency-checks`.
|
|
11
|
+
*/
|
|
12
|
+
const plugin = {
|
|
13
|
+
meta: { name: '@zukhruf/eslint/nx/manifest', namespace: 'manifest' },
|
|
14
|
+
rules: { 'dependency-checks': dependencyChecks },
|
|
15
|
+
configs,
|
|
16
|
+
};
|
|
17
|
+
configs['recommended'] = [
|
|
18
|
+
{
|
|
19
|
+
name: 'manifest/recommended',
|
|
20
|
+
files: PACKAGE_JSON,
|
|
21
|
+
languageOptions: { parser: jsonc },
|
|
22
|
+
plugins: { manifest: plugin },
|
|
23
|
+
rules: { 'manifest/dependency-checks': 'error' },
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
export default plugin;
|
|
27
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/manifest/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,qBAAqB,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,gBAAgB,MAAM,wBAAwB,CAAC;AAEtD,MAAM,OAAO,GAAoC,EAAE,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,MAAM,GAAG;IACb,IAAI,EAAE,EAAE,IAAI,EAAE,6BAA6B,EAAE,SAAS,EAAE,UAAU,EAAE;IACpE,KAAK,EAAE,EAAE,mBAAmB,EAAE,gBAAgB,EAAE;IAChD,OAAO;CACgB,CAAC;AAE1B,OAAO,CAAC,aAAa,CAAC,GAAG;IACvB;QACE,IAAI,EAAE,sBAAsB;QAC5B,KAAK,EAAE,YAAY;QACnB,eAAe,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;QAClC,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE;QAC7B,KAAK,EAAE,EAAE,4BAA4B,EAAE,OAAO,EAAE;KACjD;CACF,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a project's package.json must declare.
|
|
3
|
+
*
|
|
4
|
+
* - `bundled`: an application whose build inlines the workspace packages it
|
|
5
|
+
* imports. Their manifests are not there when it is deployed, so its own
|
|
6
|
+
* manifest declares every npm package they pull in, transitively.
|
|
7
|
+
* - `unbundled`: everything else. It declares only what its own code imports;
|
|
8
|
+
* whoever installs it resolves the rest.
|
|
9
|
+
*/
|
|
10
|
+
export type ProjectShape = 'bundled' | 'unbundled';
|
|
11
|
+
/**
|
|
12
|
+
* The shape of the project at `projectRoot`, from its build target. An
|
|
13
|
+
* application bundles unless its build is `nx:noop` (it ships what other
|
|
14
|
+
* projects built) or esbuild with `bundle: false` (it transpiles file by file).
|
|
15
|
+
* esbuild bundles by default. A build target that an Nx plugin infers, such as
|
|
16
|
+
* Vite's, is not in project.json, and counts as bundling.
|
|
17
|
+
*/
|
|
18
|
+
export declare function projectShape(projectRoot: string): ProjectShape;
|
|
19
|
+
//# sourceMappingURL=project-shape.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-shape.d.ts","sourceRoot":"","sources":["../../src/manifest/project-shape.ts"],"names":[],"mappings":"AAKA;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,WAAW,CAAC;AAWnD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,CAc9D"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { isRecord } from "../authoring/ast.js";
|
|
3
|
+
import { readJson } from "../workspace/project-manifest.js";
|
|
4
|
+
/** project.json, or the `nx` field of package.json for a project without one. */
|
|
5
|
+
function nxConfiguration(projectRoot) {
|
|
6
|
+
const projectJson = readJson(join(projectRoot, 'project.json'));
|
|
7
|
+
if (isRecord(projectJson))
|
|
8
|
+
return projectJson;
|
|
9
|
+
const packageJson = readJson(join(projectRoot, 'package.json'));
|
|
10
|
+
const nx = isRecord(packageJson) ? packageJson['nx'] : undefined;
|
|
11
|
+
return isRecord(nx) ? nx : {};
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The shape of the project at `projectRoot`, from its build target. An
|
|
15
|
+
* application bundles unless its build is `nx:noop` (it ships what other
|
|
16
|
+
* projects built) or esbuild with `bundle: false` (it transpiles file by file).
|
|
17
|
+
* esbuild bundles by default. A build target that an Nx plugin infers, such as
|
|
18
|
+
* Vite's, is not in project.json, and counts as bundling.
|
|
19
|
+
*/
|
|
20
|
+
export function projectShape(projectRoot) {
|
|
21
|
+
const project = nxConfiguration(projectRoot);
|
|
22
|
+
if (project['projectType'] !== 'application')
|
|
23
|
+
return 'unbundled';
|
|
24
|
+
const targets = project['targets'];
|
|
25
|
+
const build = isRecord(targets) ? targets['build'] : undefined;
|
|
26
|
+
const executor = isRecord(build) ? build['executor'] : undefined;
|
|
27
|
+
if (executor === 'nx:noop')
|
|
28
|
+
return 'unbundled';
|
|
29
|
+
if (typeof executor === 'string' && executor.includes('esbuild')) {
|
|
30
|
+
const options = isRecord(build) ? build['options'] : undefined;
|
|
31
|
+
return isRecord(options) && options['bundle'] === false
|
|
32
|
+
? 'unbundled'
|
|
33
|
+
: 'bundled';
|
|
34
|
+
}
|
|
35
|
+
return 'bundled';
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=project-shape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-shape.js","sourceRoot":"","sources":["../../src/manifest/project-shape.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAa5D,iFAAiF;AACjF,SAAS,eAAe,CAAC,WAAmB;IAC1C,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC,CAAC;IAChE,IAAI,QAAQ,CAAC,WAAW,CAAC;QAAE,OAAO,WAAW,CAAC;IAC9C,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC,CAAC;IAChE,MAAM,EAAE,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACjE,OAAO,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAChC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,WAAmB;IAC9C,MAAM,OAAO,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;IAC7C,IAAI,OAAO,CAAC,aAAa,CAAC,KAAK,aAAa;QAAE,OAAO,WAAW,CAAC;IACjE,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/D,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACjE,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,WAAW,CAAC;IAC/C,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACjE,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC/D,OAAO,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,KAAK;YACrD,CAAC,CAAC,WAAW;YACb,CAAC,CAAC,SAAS,CAAC;IAChB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
package/dist/nx.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export { default } from './island/plugin.ts';
|
|
2
|
+
export { default as manifest } from './manifest/plugin.ts';
|
|
3
|
+
export { manifestPolicy, type ManifestPolicyOptions, type ProjectExtras, } from './manifest/manifest-policy.ts';
|
|
4
|
+
export { projectShape, type ProjectShape } from './manifest/project-shape.ts';
|
|
2
5
|
export { dependencyPolicy, type DependencyPolicyOptions, } from './nx-policy/dependency-policy.ts';
|
|
3
6
|
export { ISLAND_TAG, islandConstraint } from './nx-policy/island-constraint.ts';
|
|
4
7
|
export { type DepConstraint, moduleBoundaries, type ModuleBoundaryOptions, } from './nx-policy/module-boundaries.ts';
|
package/dist/nx.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nx.d.ts","sourceRoot":"","sources":["../src/nx.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EACL,gBAAgB,EAChB,KAAK,uBAAuB,GAC7B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAChF,OAAO,EACL,KAAK,aAAa,EAClB,gBAAgB,EAChB,KAAK,qBAAqB,GAC3B,MAAM,kCAAkC,CAAC"}
|
|
1
|
+
{"version":3,"file":"nx.d.ts","sourceRoot":"","sources":["../src/nx.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EACL,cAAc,EACd,KAAK,qBAAqB,EAC1B,KAAK,aAAa,GACnB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC9E,OAAO,EACL,gBAAgB,EAChB,KAAK,uBAAuB,GAC7B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAChF,OAAO,EACL,KAAK,aAAa,EAClB,gBAAgB,EAChB,KAAK,qBAAqB,GAC3B,MAAM,kCAAkC,CAAC"}
|
package/dist/nx.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export { default } from "./island/plugin.js";
|
|
2
|
+
export { default as manifest } from "./manifest/plugin.js";
|
|
3
|
+
export { manifestPolicy, } from "./manifest/manifest-policy.js";
|
|
4
|
+
export { projectShape } from "./manifest/project-shape.js";
|
|
2
5
|
export { dependencyPolicy, } from "./nx-policy/dependency-policy.js";
|
|
3
6
|
export { ISLAND_TAG, islandConstraint } from "./nx-policy/island-constraint.js";
|
|
4
7
|
export { moduleBoundaries, } from "./nx-policy/module-boundaries.js";
|
package/dist/nx.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nx.js","sourceRoot":"","sources":["../src/nx.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EACL,gBAAgB,GAEjB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAChF,OAAO,EAEL,gBAAgB,GAEjB,MAAM,kCAAkC,CAAC"}
|
|
1
|
+
{"version":3,"file":"nx.js","sourceRoot":"","sources":["../src/nx.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EACL,cAAc,GAGf,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,YAAY,EAAqB,MAAM,6BAA6B,CAAC;AAC9E,OAAO,EACL,gBAAgB,GAEjB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAChF,OAAO,EAEL,gBAAgB,GAEjB,MAAM,kCAAkC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-native.d.ts","sourceRoot":"","sources":["../src/react-native.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-native.js","sourceRoot":"","sources":["../src/react-native.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -4,6 +4,8 @@ export interface Project {
|
|
|
4
4
|
/** Tags from project.json `tags` and package.json `nx.tags`. */
|
|
5
5
|
readonly tags: readonly string[];
|
|
6
6
|
}
|
|
7
|
+
/** The parsed JSON file at `path`, or `undefined` when there is none. */
|
|
8
|
+
export declare function readJson(path: string): unknown;
|
|
7
9
|
/**
|
|
8
10
|
* The project that holds `filename`, as Nx defines one: the nearest folder with
|
|
9
11
|
* a project.json or a package.json. Its tags are read from those manifests
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-manifest.d.ts","sourceRoot":"","sources":["../../src/workspace/project-manifest.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,OAAO;IACtB,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;
|
|
1
|
+
{"version":3,"file":"project-manifest.d.ts","sourceRoot":"","sources":["../../src/workspace/project-manifest.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,OAAO;IACtB,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAID,yEAAyE;AACzE,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9C;AA+BD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAE/D"}
|
|
@@ -2,7 +2,8 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
2
2
|
import { dirname, join, resolve } from 'node:path';
|
|
3
3
|
import { isRecord, stringsAt } from "../authoring/ast.js";
|
|
4
4
|
const projectByDirectory = new Map();
|
|
5
|
-
|
|
5
|
+
/** The parsed JSON file at `path`, or `undefined` when there is none. */
|
|
6
|
+
export function readJson(path) {
|
|
6
7
|
return existsSync(path) ? JSON.parse(readFileSync(path, 'utf8')) : undefined;
|
|
7
8
|
}
|
|
8
9
|
function projectAt(directory) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-manifest.js","sourceRoot":"","sources":["../../src/workspace/project-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAS1D,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAA+B,CAAC;AAElE,
|
|
1
|
+
{"version":3,"file":"project-manifest.js","sourceRoot":"","sources":["../../src/workspace/project-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAS1D,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAA+B,CAAC;AAElE,yEAAyE;AACzE,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/E,CAAC;AAED,SAAS,SAAS,CAAC,SAAiB;IAClC,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IACpD,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IACpD,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,SAAS,CAAC;IAC3E,MAAM,eAAe,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC9C,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACzE,OAAO;QACL,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,GAAG,SAAS,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;YAC3C,GAAG,SAAS,CAAC,EAAE,EAAE,MAAM,CAAC;SACzB;KACF,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,SAAiB;IACpC,MAAM,OAAO,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IACrC,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAC5B,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAClC,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,kBAAkB,CAAC,SAAiB;IAC3C,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;QACvC,kBAAkB,CAAC,GAAG,CAAC,SAAS,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,kBAAkB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,QAAgB;IACxC,OAAO,kBAAkB,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AACxD,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The name of every package in the workspace, the root manifest aside. The
|
|
3
|
+
* folders are scanned, not the root `workspaces` globs: a single-level `*`
|
|
4
|
+
* there misses nested packages.
|
|
5
|
+
*/
|
|
6
|
+
export declare function workspacePackages(workspaceRoot: string): readonly string[];
|
|
7
|
+
//# sourceMappingURL=workspace-packages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-packages.d.ts","sourceRoot":"","sources":["../../src/workspace/workspace-packages.ts"],"names":[],"mappings":"AA0BA;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAM1E"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { globSync } from 'node:fs';
|
|
2
|
+
import { basename, join } from 'node:path';
|
|
3
|
+
import { isRecord } from "../authoring/ast.js";
|
|
4
|
+
import { readJson } from "./project-manifest.js";
|
|
5
|
+
// Build output, installed packages, and dot-folders (worktrees of other
|
|
6
|
+
// checkouts among them) hold manifests that are not this workspace's packages.
|
|
7
|
+
const skipped = (path) => {
|
|
8
|
+
const name = basename(path);
|
|
9
|
+
return name === 'node_modules' || name === 'dist' || name.startsWith('.');
|
|
10
|
+
};
|
|
11
|
+
// One scan per workspace for the life of the process.
|
|
12
|
+
const packagesByRoot = new Map();
|
|
13
|
+
function scan(workspaceRoot) {
|
|
14
|
+
return globSync('**/package.json', { cwd: workspaceRoot, exclude: skipped })
|
|
15
|
+
.filter((path) => path !== 'package.json')
|
|
16
|
+
.flatMap((path) => {
|
|
17
|
+
const manifest = readJson(join(workspaceRoot, path));
|
|
18
|
+
const name = isRecord(manifest) ? manifest['name'] : undefined;
|
|
19
|
+
return typeof name === 'string' ? [name] : [];
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The name of every package in the workspace, the root manifest aside. The
|
|
24
|
+
* folders are scanned, not the root `workspaces` globs: a single-level `*`
|
|
25
|
+
* there misses nested packages.
|
|
26
|
+
*/
|
|
27
|
+
export function workspacePackages(workspaceRoot) {
|
|
28
|
+
const known = packagesByRoot.get(workspaceRoot);
|
|
29
|
+
if (known)
|
|
30
|
+
return known;
|
|
31
|
+
const names = scan(workspaceRoot);
|
|
32
|
+
packagesByRoot.set(workspaceRoot, names);
|
|
33
|
+
return names;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=workspace-packages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-packages.js","sourceRoot":"","sources":["../../src/workspace/workspace-packages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,wEAAwE;AACxE,+EAA+E;AAC/E,MAAM,OAAO,GAAG,CAAC,IAAY,EAAE,EAAE;IAC/B,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,IAAI,KAAK,cAAc,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;AAC5E,CAAC,CAAC;AAEF,sDAAsD;AACtD,MAAM,cAAc,GAAG,IAAI,GAAG,EAA6B,CAAC;AAE5D,SAAS,IAAI,CAAC,aAAqB;IACjC,OAAO,QAAQ,CAAC,iBAAiB,EAAE,EAAE,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;SACzE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,cAAc,CAAC;SACzC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAChB,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,CAAC;QACrD,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC/D,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAChD,CAAC,CAAC,CAAC;AACP,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,aAAqB;IACrD,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAChD,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IACxB,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC;IAClC,cAAc,CAAC,GAAG,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zukhruf/eslint",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "ESLint rules and configs shared across workspaces. Every check is its own named rule, so a repo's own config cannot silently erase it.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -34,6 +34,11 @@
|
|
|
34
34
|
"types": "./dist/nx.d.ts",
|
|
35
35
|
"import": "./dist/nx.js",
|
|
36
36
|
"default": "./dist/nx.js"
|
|
37
|
+
},
|
|
38
|
+
"./react-native": {
|
|
39
|
+
"types": "./dist/react-native.d.ts",
|
|
40
|
+
"import": "./dist/react-native.js",
|
|
41
|
+
"default": "./dist/react-native.js"
|
|
37
42
|
}
|
|
38
43
|
},
|
|
39
44
|
"files": [
|
|
@@ -46,13 +51,16 @@
|
|
|
46
51
|
"access": "public"
|
|
47
52
|
},
|
|
48
53
|
"dependencies": {
|
|
54
|
+
"@typescript-eslint/utils": "^8.47.0",
|
|
49
55
|
"globals": "^17.13.0",
|
|
50
|
-
"ignore": "^7.0.12"
|
|
56
|
+
"ignore": "^7.0.12",
|
|
57
|
+
"ts-api-utils": "^2.5.0"
|
|
51
58
|
},
|
|
52
59
|
"peerDependencies": {
|
|
53
60
|
"@eslint/js": "^9.30.0 || ^10.0.0",
|
|
54
61
|
"@nx/eslint-plugin": ">=21.0.0",
|
|
55
62
|
"eslint": "^9.30.0 || ^10.0.0",
|
|
63
|
+
"eslint-plugin-es-x": "^9.7.0 || ^10.0.0",
|
|
56
64
|
"eslint-plugin-functional": "^10.0.0",
|
|
57
65
|
"eslint-plugin-import-x": "^4.16.0",
|
|
58
66
|
"jsonc-eslint-parser": "^2.4.0 || ^3.0.0",
|
|
@@ -62,6 +70,9 @@
|
|
|
62
70
|
"peerDependenciesMeta": {
|
|
63
71
|
"@nx/eslint-plugin": {
|
|
64
72
|
"optional": true
|
|
73
|
+
},
|
|
74
|
+
"eslint-plugin-es-x": {
|
|
75
|
+
"optional": true
|
|
65
76
|
}
|
|
66
77
|
}
|
|
67
78
|
}
|