eslint-plugin-ts-fortress 1.2.4 → 1.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 +102 -4
- package/dist/plugin.d.mts +5 -1
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +2 -0
- package/dist/plugin.mjs.map +1 -1
- package/dist/rule-types.d.mts +43 -4
- package/dist/rule-types.d.mts.map +1 -1
- package/dist/rules/ast-utils.d.mts +15 -0
- package/dist/rules/ast-utils.d.mts.map +1 -0
- package/dist/rules/ast-utils.mjs +24 -0
- package/dist/rules/ast-utils.mjs.map +1 -0
- package/dist/rules/export-usage.d.mts +19 -0
- package/dist/rules/export-usage.d.mts.map +1 -0
- package/dist/rules/export-usage.mjs +252 -0
- package/dist/rules/export-usage.mjs.map +1 -0
- package/dist/rules/no-type-only-codec.d.mts +25 -0
- package/dist/rules/no-type-only-codec.d.mts.map +1 -0
- package/dist/rules/no-type-only-codec.mjs +270 -0
- package/dist/rules/no-type-only-codec.mjs.map +1 -0
- package/dist/rules/package-entry-points.d.mts +22 -0
- package/dist/rules/package-entry-points.d.mts.map +1 -0
- package/dist/rules/package-entry-points.mjs +195 -0
- package/dist/rules/package-entry-points.mjs.map +1 -0
- package/dist/rules/prefer-canonical-length-constrained-type.d.mts.map +1 -1
- package/dist/rules/prefer-canonical-length-constrained-type.mjs +44 -14
- package/dist/rules/prefer-canonical-length-constrained-type.mjs.map +1 -1
- package/dist/rules/prefer-schema-over-guard-chain.d.mts.map +1 -1
- package/dist/rules/prefer-schema-over-guard-chain.mjs +36 -21
- package/dist/rules/prefer-schema-over-guard-chain.mjs.map +1 -1
- package/dist/rules/rules.d.mts +3 -0
- package/dist/rules/rules.d.mts.map +1 -1
- package/dist/rules/rules.mjs +2 -0
- package/dist/rules/rules.mjs.map +1 -1
- package/dist/types.d.mts +2 -2
- package/dist/types.d.mts.map +1 -1
- package/package.json +3 -3
- package/src/plugin.mts +6 -4
- package/src/plugin.test.mts +0 -13
- package/src/rule-types.mts +48 -4
- package/src/rules/ast-utils.mts +35 -0
- package/src/rules/export-usage.mts +403 -0
- package/src/rules/no-type-only-codec.mts +398 -0
- package/src/rules/no-type-only-codec.test.mts +799 -0
- package/src/rules/package-entry-points.mts +294 -0
- package/src/rules/prefer-canonical-length-constrained-type.mts +65 -17
- package/src/rules/prefer-canonical-length-constrained-type.test.mts +145 -0
- package/src/rules/prefer-schema-over-guard-chain.mts +46 -29
- package/src/rules/prefer-schema-over-guard-chain.test.mts +82 -0
- package/src/rules/rules.mts +3 -1
- package/src/types.mts +2 -2
package/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
ESLint rules that steer schema definitions toward
|
|
4
4
|
[`ts-fortress`](https://www.npmjs.com/package/ts-fortress) idioms. Every rule except
|
|
5
|
-
`prefer-schema-over-guard-chain` (which only
|
|
6
|
-
rewrite is **type-preserving**.
|
|
5
|
+
`prefer-schema-over-guard-chain` and `no-type-only-codec` (which only report) is
|
|
6
|
+
auto-fixable, and every rewrite is **type-preserving**.
|
|
7
7
|
|
|
8
8
|
## Installation
|
|
9
9
|
|
|
@@ -11,8 +11,10 @@ rewrite is **type-preserving**.
|
|
|
11
11
|
npm install --save-dev eslint-plugin-ts-fortress
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
Requires ESLint 9+ (flat config) and TypeScript.
|
|
15
|
-
|
|
14
|
+
Requires ESLint 9+ (flat config) and TypeScript. A configured TypeScript project
|
|
15
|
+
is not required: only `no-type-only-codec` reads type information, and without it
|
|
16
|
+
the rule checks less rather than failing (see
|
|
17
|
+
[Type information](#type-information)).
|
|
16
18
|
|
|
17
19
|
## Usage (flat config)
|
|
18
20
|
|
|
@@ -73,6 +75,7 @@ export default [
|
|
|
73
75
|
| `prefer-canonical-length-constrained-type` | Normalize a length-constrained array combinator with degenerate bounds to its canonical form. |
|
|
74
76
|
| `prefer-namespace-import` | Require `ts-fortress` to be imported as a namespace rather than by name. |
|
|
75
77
|
| `prefer-schema-over-guard-chain` | Report a hand-written chain of type guards on one value once it is long enough to be a schema. |
|
|
78
|
+
| `no-type-only-codec` | Report a codec whose value nothing reads, only the type `TypeOf` derives from it. |
|
|
76
79
|
|
|
77
80
|
### `prefer-canonical-length-constrained-type`
|
|
78
81
|
|
|
@@ -296,6 +299,101 @@ least interested in, while the chain of five narrows the result of a
|
|
|
296
299
|
length of the chain that prompted the rule. That chain of five is the one site
|
|
297
300
|
the rule reported when it was turned on, and it has since been rewritten.
|
|
298
301
|
|
|
302
|
+
### `no-type-only-codec`
|
|
303
|
+
|
|
304
|
+
A codec exists to check values at runtime. One whose value nothing reads — it is
|
|
305
|
+
there only so that `t.TypeOf` can derive a type from it — is a type written the
|
|
306
|
+
long way round: it costs a runtime object, keeps `ts-fortress` in the bundle of
|
|
307
|
+
whatever imports it, and reads as if something validated with it.
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
// ❌ — every import of `User` elsewhere is `import type`
|
|
311
|
+
export const User = t.record({ name: t.string() });
|
|
312
|
+
|
|
313
|
+
export type User = t.TypeOf<typeof User>;
|
|
314
|
+
|
|
315
|
+
// ✅
|
|
316
|
+
export type User = Readonly<{ name: string }>;
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
The rule looks at a top-level `const X` paired with a top-level
|
|
320
|
+
`type Y = t.TypeOf<typeof X>` — `Y` is usually `X` itself, or `X` is named
|
|
321
|
+
`yTypeDef` — and reports `X` when none of these reads its value:
|
|
322
|
+
|
|
323
|
+
- **its own file**: any reference outside a type. `typeof X` in another type
|
|
324
|
+
does not count; `t.array(X)` in another codec does.
|
|
325
|
+
- **the other files of the TypeScript project**: an import that is not
|
|
326
|
+
`import type` / `import { type X }`, whatever the importer then does with it;
|
|
327
|
+
a namespace import read as `ns.X` or `ns['X']` outside a type, or used whole
|
|
328
|
+
(`f(ns)`, `ns[key]`); a dynamic `import()`. Re-exports are followed, so a
|
|
329
|
+
barrel is transparent.
|
|
330
|
+
- **an entry point** — a file the package publishes (see
|
|
331
|
+
[Entry points](#entry-points)) — that re-exports it as a value, directly or
|
|
332
|
+
through any chain of `export { … } from` / `export * from`.
|
|
333
|
+
|
|
334
|
+
The rule reports; there is no fix, since writing the type out is not
|
|
335
|
+
mechanical.
|
|
336
|
+
|
|
337
|
+
#### Type information
|
|
338
|
+
|
|
339
|
+
Whether another file reads an exported codec is a question about the whole
|
|
340
|
+
TypeScript project, so the rule answers it from the program a file is linted
|
|
341
|
+
with. Turn type information on for it to check exported codecs at all:
|
|
342
|
+
|
|
343
|
+
```ts
|
|
344
|
+
// eslint.config.mts
|
|
345
|
+
import { eslintPluginTsFortress } from 'eslint-plugin-ts-fortress';
|
|
346
|
+
|
|
347
|
+
export default [
|
|
348
|
+
eslintPluginTsFortress.configs.recommended,
|
|
349
|
+
{
|
|
350
|
+
languageOptions: {
|
|
351
|
+
parserOptions: {
|
|
352
|
+
projectService: true,
|
|
353
|
+
tsconfigRootDir: import.meta.dirname,
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
];
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Without it, a codec that is not exported is still checked — that needs nothing
|
|
361
|
+
but its own file — and an exported one is left alone.
|
|
362
|
+
|
|
363
|
+
The rule sees the files of that program and nothing else. An importer counts
|
|
364
|
+
only if the `tsconfig.json` linting the codec's file includes it, so tests under
|
|
365
|
+
a separate project import nothing as far as the rule can tell, and another
|
|
366
|
+
package that imports the codec reaches it through an entry point.
|
|
367
|
+
|
|
368
|
+
#### Entry points
|
|
369
|
+
|
|
370
|
+
A codec the package publishes is public API, whoever uses it. The entry points
|
|
371
|
+
are read from the `package.json` nearest to the file — `exports`, every subpath
|
|
372
|
+
and condition of it, and the legacy `main`, `module`, `types` and `typings`
|
|
373
|
+
fields — and traced back to source: a target that is a file of the project is
|
|
374
|
+
taken as it is, and an output such as `./dist/index.mjs` becomes the source it
|
|
375
|
+
was built from, `src/index.mts` (the project's `outDir` and `rootDir` are used
|
|
376
|
+
when set; otherwise a leading `dist`, `lib`, `build`, `out`, `esm` or `cjs`
|
|
377
|
+
directory becomes `src`). The conditions of one subpath describe one module, so
|
|
378
|
+
a subpath counts as traced when any of its targets is, and a `types` file a
|
|
379
|
+
build writes on its own does not need a source. A package that publishes nothing
|
|
380
|
+
— an application — has no entry points.
|
|
381
|
+
|
|
382
|
+
When some subpath cannot be traced back to a file of the project, what the
|
|
383
|
+
package publishes is not known, and the rule leaves its exported codecs alone
|
|
384
|
+
rather than report public API. Name the entry points yourself for such a layout:
|
|
385
|
+
|
|
386
|
+
```ts
|
|
387
|
+
'ts-fortress/no-type-only-codec': [
|
|
388
|
+
'error',
|
|
389
|
+
{ entryPoints: [path.resolve(import.meta.dirname, 'src/public-api.mts')] },
|
|
390
|
+
],
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
- `entryPoints` (default: read from `package.json`) — the files whose exports
|
|
394
|
+
are public API, replacing what `package.json` says. Relative paths resolve
|
|
395
|
+
against the directory ESLint runs in, so pass absolute ones, as above.
|
|
396
|
+
|
|
299
397
|
## License
|
|
300
398
|
|
|
301
399
|
[Apache-2.0](https://github.com/noshiro-pf/mono/blob/main/libs/eslint-plugin-ts-fortress/LICENSE)
|
package/dist/plugin.d.mts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ESLintPlugin } from './types.mjs';
|
|
2
2
|
export declare const eslintPluginTsFortress: {
|
|
3
3
|
readonly meta: {
|
|
4
4
|
readonly name: 'eslint-plugin-ts-fortress';
|
|
5
5
|
};
|
|
6
6
|
readonly rules: {
|
|
7
|
+
readonly 'no-type-only-codec': import("@typescript-eslint/utils/ts-eslint").RuleModule<"typeOnlyCodec", readonly [(Readonly<{
|
|
8
|
+
entryPoints?: readonly string[];
|
|
9
|
+
}> | undefined)?], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
7
10
|
readonly 'prefer-canonical-length-constrained-type': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useCanonicalType", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
8
11
|
readonly 'prefer-namespace-import': import("@typescript-eslint/utils/ts-eslint").RuleModule<"removeSideEffectImport" | "useNamespaceImport", readonly [(Readonly<{
|
|
9
12
|
namespaceName?: string;
|
|
@@ -21,6 +24,7 @@ export declare const eslintPluginTsFortress: {
|
|
|
21
24
|
readonly 'ts-fortress': ESLintPlugin;
|
|
22
25
|
};
|
|
23
26
|
readonly rules: {
|
|
27
|
+
readonly 'ts-fortress/no-type-only-codec': 'error';
|
|
24
28
|
readonly 'ts-fortress/prefer-canonical-length-constrained-type': 'error';
|
|
25
29
|
readonly 'ts-fortress/prefer-namespace-import': 'error';
|
|
26
30
|
readonly 'ts-fortress/prefer-schema-over-guard-chain': 'error';
|
package/dist/plugin.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.mts","sourceRoot":"","sources":["../src/plugin.mts"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"plugin.d.mts","sourceRoot":"","sources":["../src/plugin.mts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAoB,YAAY,EAAE,MAAM,aAAa,CAAC;AAmClE,eAAO,MAAM,sBAAsB;;iBAE/B,IAAI,EAAE,2BAA2B;;;;;;;;;;;;;;;;QAIjC,oDAAoD;iBACpD,WAAW;2BAtBP,yBAAyB;;yBAQzB,aAAa,EAAI,YAAY;;;2DAlBD,OAAO;iFACe,OAAO;gEACxB,OAAO;uEACA,OAAO;;;;CA+BtB,CAAC"}
|
package/dist/plugin.mjs
CHANGED
|
@@ -5,7 +5,9 @@ import { tsFortressRules } from './rules/index.mjs';
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
|
|
8
9
|
const recommendedRules = {
|
|
10
|
+
'ts-fortress/no-type-only-codec': 'error',
|
|
9
11
|
'ts-fortress/prefer-canonical-length-constrained-type': 'error',
|
|
10
12
|
'ts-fortress/prefer-namespace-import': 'error',
|
|
11
13
|
'ts-fortress/prefer-schema-over-guard-chain': 'error',
|
package/dist/plugin.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.mjs","sourceRoot":"","sources":["../src/plugin.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD
|
|
1
|
+
{"version":3,"file":"plugin.mjs","sourceRoot":"","sources":["../src/plugin.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD;;;;;;GAMG;AACH,MAAM,gBAAgB,GAAG;IACvB,gCAAgC,EAAE,OAAO;IACzC,sDAAsD,EAAE,OAAO;IAC/D,qCAAqC,EAAE,OAAO;IAC9C,4CAA4C,EAAE,OAAO;CAItD,CAAC;AAEF,MAAM,iBAAiB,GAAG;IACxB,IAAI,EAAE,yBAAyB;IAC/B,OAAO,EAAE;QACP,uEAAuE;QACvE,wEAAwE;QACxE,wEAAwE;QACxE,mDAAmD;QACnD,0EAA0E;QAC1E,qDAAqD;QACrD,IAAI,aAAa;YACf,OAAO,sBAAsB,CAAC;QAChC,CAAC;KACF;IACD,KAAK,EAAE,gBAAgB;CACY,CAAC;AAEtC,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,IAAI,EAAE;QACJ,IAAI,EAAE,2BAA2B;KAClC;IACD,KAAK,EAAE,eAAe;IACtB,OAAO,EAAE;QACP,oDAAoD;QACpD,WAAW,EAAE,iBAAiB;KAC/B;CAC8B,CAAC"}
|
package/dist/rule-types.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import type { TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
import type { Linter } from 'eslint';
|
|
3
|
+
import type { TypeEq } from 'ts-type-forge';
|
|
4
|
+
import type { tsFortressRules } from './rules/index.mjs';
|
|
5
5
|
type SpreadOptionsIfIsArray<T extends readonly [Linter.StringSeverity, unknown]> = T[1] extends readonly unknown[] ? readonly [Linter.StringSeverity, ...T[1]] : T;
|
|
6
6
|
/**
|
|
7
7
|
* Extracts the options tuple type of a rule from its TypeScript
|
|
@@ -9,6 +9,43 @@ type SpreadOptionsIfIsArray<T extends readonly [Linter.StringSeverity, unknown]>
|
|
|
9
9
|
* without converting `meta.schema` (JSON Schema) back into TypeScript types.
|
|
10
10
|
*/
|
|
11
11
|
type RuleOptionsOf<Name extends keyof typeof tsFortressRules> = (typeof tsFortressRules)[Name] extends TSESLint.RuleModule<infer _MessageIds extends string, infer Options extends readonly unknown[]> ? Options : never;
|
|
12
|
+
/**
|
|
13
|
+
* @description Report a ts-fortress codec `X` paired with `type Y = t.TypeOf<typeof X>` whose value nothing reads, so that `Y` is declared as a plain type instead.
|
|
14
|
+
*
|
|
15
|
+
* ```md
|
|
16
|
+
* | key | value |
|
|
17
|
+
* | :--------- | :--------- |
|
|
18
|
+
* | type | suggestion |
|
|
19
|
+
* | deprecated | false |
|
|
20
|
+
* | fixable | false |
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
declare namespace NoTypeOnlyCodec {
|
|
24
|
+
/**
|
|
25
|
+
* ### schema
|
|
26
|
+
*
|
|
27
|
+
* ```json
|
|
28
|
+
* [
|
|
29
|
+
* {
|
|
30
|
+
* "type": "object",
|
|
31
|
+
* "properties": {
|
|
32
|
+
* "entryPoints": {
|
|
33
|
+
* "type": "array",
|
|
34
|
+
* "items": {
|
|
35
|
+
* "type": "string"
|
|
36
|
+
* },
|
|
37
|
+
* "uniqueItems": true,
|
|
38
|
+
* "description": "Files whose exports are public API. A codec they export, directly or through re-exports, is never reported. Replaces the entry points read from the nearest package.json, for a layout they cannot be traced back to source from. Relative paths are resolved against the working directory ESLint runs in; pass absolute paths to make the config independent of it."
|
|
39
|
+
* }
|
|
40
|
+
* },
|
|
41
|
+
* "additionalProperties": false
|
|
42
|
+
* }
|
|
43
|
+
* ]
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
type Options = NonNullable<RuleOptionsOf<'no-type-only-codec'>[0]>;
|
|
47
|
+
type RuleEntry = 'off' | Linter.Severity | SpreadOptionsIfIsArray<readonly [Linter.StringSeverity, Options]>;
|
|
48
|
+
}
|
|
12
49
|
/**
|
|
13
50
|
* @description Normalize a length-constrained ts-fortress array combinator whose bounds are degenerate (e.g. `minLengthArray(1, x)`, `boundedLengthTuple(n, n, x)`) to the combinator that names that constraint directly.
|
|
14
51
|
*
|
|
@@ -99,11 +136,13 @@ declare namespace PreferSchemaOverGuardChain {
|
|
|
99
136
|
type RuleEntry = 'off' | Linter.Severity | SpreadOptionsIfIsArray<readonly [Linter.StringSeverity, Options]>;
|
|
100
137
|
}
|
|
101
138
|
export type EslintTsFortressRules = Readonly<{
|
|
139
|
+
'ts-fortress/no-type-only-codec': NoTypeOnlyCodec.RuleEntry;
|
|
102
140
|
'ts-fortress/prefer-canonical-length-constrained-type': PreferCanonicalLengthConstrainedType.RuleEntry;
|
|
103
141
|
'ts-fortress/prefer-namespace-import': PreferNamespaceImport.RuleEntry;
|
|
104
142
|
'ts-fortress/prefer-schema-over-guard-chain': PreferSchemaOverGuardChain.RuleEntry;
|
|
105
143
|
}>;
|
|
106
144
|
export type EslintTsFortressRulesOption = Readonly<{
|
|
145
|
+
'ts-fortress/no-type-only-codec': NoTypeOnlyCodec.Options;
|
|
107
146
|
'ts-fortress/prefer-namespace-import': PreferNamespaceImport.Options;
|
|
108
147
|
'ts-fortress/prefer-schema-over-guard-chain': PreferSchemaOverGuardChain.Options;
|
|
109
148
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-types.d.mts","sourceRoot":"","sources":["../src/rule-types.mts"],"names":[],"mappings":"AAKA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"rule-types.d.mts","sourceRoot":"","sources":["../src/rule-types.mts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type TSESTree } from '@typescript-eslint/utils';
|
|
2
|
+
/**
|
|
3
|
+
* `node` with every `as`, `satisfies`, `!` and `<T>` around it taken off: the
|
|
4
|
+
* expression whose value it is. Judge syntax on what this returns, so that a
|
|
5
|
+
* wrapper does not hide a pattern (`isString((x as R).a)`), and keep the
|
|
6
|
+
* original node for fix text. See `docs/writing-lint-rules.md` at the
|
|
7
|
+
* repository root.
|
|
8
|
+
*
|
|
9
|
+
* A copy of the helper in `eslint-config-typed`, which this package, published
|
|
10
|
+
* on its own, cannot import.
|
|
11
|
+
*/
|
|
12
|
+
export declare const skipTypeWrappers: (node: TSESTree.Node) => TSESTree.Node;
|
|
13
|
+
export declare const isTypeWrapper: (node: TSESTree.Node) => node is TypeWrapper;
|
|
14
|
+
export type TypeWrapper = TSESTree.TSAsExpression | TSESTree.TSNonNullExpression | TSESTree.TSSatisfiesExpression | TSESTree.TSTypeAssertion;
|
|
15
|
+
//# sourceMappingURL=ast-utils.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast-utils.d.mts","sourceRoot":"","sources":["../../src/rules/ast-utils.mts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzE;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,SAErB,QAAQ,CAAC,IAAI,KAClB,QAAQ,CAAC,IACoD,CAAC;AAEjE,eAAO,MAAM,aAAa,SAElB,QAAQ,CAAC,IAAI,KAClB,IAAI,IAAI,WAA2C,CAAC;AAEvD,MAAM,MAAM,WAAW,GACnB,QAAQ,CAAC,cAAc,GACvB,QAAQ,CAAC,mBAAmB,GAC5B,QAAQ,CAAC,qBAAqB,GAC9B,QAAQ,CAAC,eAAe,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
export const skipTypeWrappers = (
|
|
13
|
+
|
|
14
|
+
node) => isTypeWrapper(node) ? skipTypeWrappers(node.expression) : node;
|
|
15
|
+
export const isTypeWrapper = (
|
|
16
|
+
|
|
17
|
+
node) => TYPE_WRAPPERS.has(node.type);
|
|
18
|
+
const TYPE_WRAPPERS = new Set([
|
|
19
|
+
AST_NODE_TYPES.TSAsExpression,
|
|
20
|
+
AST_NODE_TYPES.TSNonNullExpression,
|
|
21
|
+
AST_NODE_TYPES.TSSatisfiesExpression,
|
|
22
|
+
AST_NODE_TYPES.TSTypeAssertion,
|
|
23
|
+
]);
|
|
24
|
+
//# sourceMappingURL=ast-utils.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast-utils.mjs","sourceRoot":"","sources":["../../src/rules/ast-utils.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAiB,MAAM,0BAA0B,CAAC;AAEzE;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;AAC9B,8EAA8E;AAC9E,IAAmB,EACJ,EAAE,CACjB,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAEjE,MAAM,CAAC,MAAM,aAAa,GAAG;AAC3B,8EAA8E;AAC9E,IAAmB,EACE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAQvD,MAAM,aAAa,GAAgC,IAAI,GAAG,CAAC;IACzD,cAAc,CAAC,cAAc;IAC7B,cAAc,CAAC,mBAAmB;IAClC,cAAc,CAAC,qBAAqB;IACpC,cAAc,CAAC,eAAe;CAC/B,CAAC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* Whether the value `fileName` exports as `exportName` may be read at runtime
|
|
4
|
+
* by a file of `program`, or is public API because one of `entryPoints`
|
|
5
|
+
* re-exports it.
|
|
6
|
+
*
|
|
7
|
+
* Answered from the import and export declarations of every file in the
|
|
8
|
+
* program, nothing more, and it errs towards "used" wherever they do not
|
|
9
|
+
* decide it: a non-type import counts as a value use whatever the importer does
|
|
10
|
+
* with it, and a namespace or a module that leaves the file whole (a dynamic
|
|
11
|
+
* `import()`, `export { ns }`, `f(ns)`) uses every export. A type-only import or
|
|
12
|
+
* re-export is never a use. A re-export is followed to what imports the
|
|
13
|
+
* re-exporting file, so a barrel is transparent.
|
|
14
|
+
*
|
|
15
|
+
* Files outside `program` are not seen at all, so an importer only counts when
|
|
16
|
+
* the TypeScript project that lints `fileName` includes it.
|
|
17
|
+
*/
|
|
18
|
+
export declare const isExportedValueUsed: (program: ts.Program, fileName: string, exportName: string, entryPoints: ReadonlySet<string>) => boolean;
|
|
19
|
+
//# sourceMappingURL=export-usage.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-usage.d.mts","sourceRoot":"","sources":["../../src/rules/export-usage.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAEjC;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,mBAAmB,YACrB,EAAE,CAAC,OAAO,YACT,MAAM,cACJ,MAAM,eACL,WAAW,CAAC,MAAM,CAAC,KAC/B,OACyE,CAAC"}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
export const isExportedValueUsed = (program, fileName, exportName, entryPoints) => isUsed(importEdges(program), entryPoints, fileName, exportName, new Set());
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
const importEdgesCache = new WeakMap();
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
const namespaceMembersCache = new WeakMap();
|
|
30
|
+
|
|
31
|
+
const DYNAMIC_IMPORT_PATTERN = /\b(?:import|require)\s*\(/u;
|
|
32
|
+
|
|
33
|
+
const isUsed = (edges, entryPoints, fileName, exportName, mut_visited) => {
|
|
34
|
+
if (entryPoints.has(fileName)) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
const key = `${fileName}\0${exportName}`;
|
|
38
|
+
|
|
39
|
+
if (mut_visited.has(key)) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
mut_visited.add(key);
|
|
43
|
+
const follow = (importer, name) => isUsed(edges, entryPoints, importer, name, mut_visited);
|
|
44
|
+
return (edges.get(fileName) ?? []).some((edge) => {
|
|
45
|
+
switch (edge.kind) {
|
|
46
|
+
case 'everything':
|
|
47
|
+
return true;
|
|
48
|
+
case 'named':
|
|
49
|
+
return edge.name === exportName;
|
|
50
|
+
case 'namespace': {
|
|
51
|
+
const members = namespaceMembers(edge);
|
|
52
|
+
return members === 'all' || members.has(exportName);
|
|
53
|
+
}
|
|
54
|
+
case 're-export':
|
|
55
|
+
return edge.name === exportName && follow(edge.importer, edge.as);
|
|
56
|
+
case 're-export-all':
|
|
57
|
+
|
|
58
|
+
return exportName !== 'default' && follow(edge.importer, exportName);
|
|
59
|
+
case 're-export-namespace':
|
|
60
|
+
return follow(edge.importer, edge.as);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const importEdges = (program) => {
|
|
66
|
+
const cached = importEdgesCache.get(program);
|
|
67
|
+
if (cached !== undefined) {
|
|
68
|
+
return cached;
|
|
69
|
+
}
|
|
70
|
+
const checker = program.getTypeChecker();
|
|
71
|
+
const mut_edges = new Map();
|
|
72
|
+
const add = (moduleSpecifier, ...edges) => {
|
|
73
|
+
const target = resolveModule(checker, moduleSpecifier);
|
|
74
|
+
if (target === undefined) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const mut_existing = mut_edges.get(target);
|
|
78
|
+
if (mut_existing === undefined) {
|
|
79
|
+
mut_edges.set(target, Array.from(edges));
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
mut_existing.push(...edges);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
for (const sourceFile of program.getSourceFiles()) {
|
|
86
|
+
if (sourceFile.isDeclarationFile ||
|
|
87
|
+
program.isSourceFileFromExternalLibrary(sourceFile) ||
|
|
88
|
+
program.isSourceFileDefaultLibrary(sourceFile)) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
for (const statement of sourceFile.statements) {
|
|
92
|
+
collectStatementEdges(sourceFile, statement, add);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
if (DYNAMIC_IMPORT_PATTERN.test(sourceFile.text)) {
|
|
97
|
+
collectDynamicImportEdges(sourceFile, add);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
importEdgesCache.set(program, mut_edges);
|
|
101
|
+
return mut_edges;
|
|
102
|
+
};
|
|
103
|
+
const collectStatementEdges = (sourceFile, statement, add) => {
|
|
104
|
+
if (ts.isImportDeclaration(statement)) {
|
|
105
|
+
const clause = statement.importClause;
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
if (clause === undefined || ts.isTypeOnlyImportDeclaration(clause)) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const bindings = clause.namedBindings;
|
|
112
|
+
add(statement.moduleSpecifier, ...(clause.name === undefined
|
|
113
|
+
? []
|
|
114
|
+
: [{ kind: 'named', name: 'default' }]), ...(bindings === undefined
|
|
115
|
+
? []
|
|
116
|
+
: ts.isNamespaceImport(bindings)
|
|
117
|
+
? [
|
|
118
|
+
{
|
|
119
|
+
kind: 'namespace',
|
|
120
|
+
importer: sourceFile,
|
|
121
|
+
localName: bindings.name.text,
|
|
122
|
+
},
|
|
123
|
+
]
|
|
124
|
+
: bindings.elements
|
|
125
|
+
.filter((element) => !element.isTypeOnly)
|
|
126
|
+
.map((element) => ({
|
|
127
|
+
kind: 'named',
|
|
128
|
+
name: (element.propertyName ?? element.name).text,
|
|
129
|
+
}))));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (ts.isExportDeclaration(statement)) {
|
|
133
|
+
if (statement.moduleSpecifier === undefined || statement.isTypeOnly) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const exportClause = statement.exportClause;
|
|
137
|
+
const importer = sourceFile.fileName;
|
|
138
|
+
add(statement.moduleSpecifier, ...(exportClause === undefined
|
|
139
|
+
? [{ kind: 're-export-all', importer }]
|
|
140
|
+
: ts.isNamespaceExport(exportClause)
|
|
141
|
+
? [
|
|
142
|
+
{
|
|
143
|
+
kind: 're-export-namespace',
|
|
144
|
+
importer,
|
|
145
|
+
as: exportClause.name.text,
|
|
146
|
+
},
|
|
147
|
+
]
|
|
148
|
+
: exportClause.elements
|
|
149
|
+
.filter((element) => !element.isTypeOnly)
|
|
150
|
+
.map((element) => ({
|
|
151
|
+
kind: 're-export',
|
|
152
|
+
importer,
|
|
153
|
+
name: (element.propertyName ?? element.name).text,
|
|
154
|
+
as: element.name.text,
|
|
155
|
+
}))));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (ts.isImportEqualsDeclaration(statement) &&
|
|
159
|
+
!statement.isTypeOnly &&
|
|
160
|
+
ts.isExternalModuleReference(statement.moduleReference)) {
|
|
161
|
+
add(statement.moduleReference.expression, { kind: 'everything' });
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
const collectDynamicImportEdges = (sourceFile, add) => {
|
|
170
|
+
const visit = (node) => {
|
|
171
|
+
if (ts.isCallExpression(node) &&
|
|
172
|
+
(node.expression.kind === ts.SyntaxKind.ImportKeyword ||
|
|
173
|
+
(ts.isIdentifier(node.expression) &&
|
|
174
|
+
node.expression.text === 'require'))) {
|
|
175
|
+
const [argument] = node.arguments;
|
|
176
|
+
if (argument !== undefined && ts.isStringLiteralLike(argument)) {
|
|
177
|
+
add(argument, { kind: 'everything' });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
ts.forEachChild(node, visit);
|
|
181
|
+
};
|
|
182
|
+
visit(sourceFile);
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
const resolveModule = (checker, moduleSpecifier) => checker
|
|
190
|
+
.getSymbolAtLocation(moduleSpecifier)
|
|
191
|
+
?.declarations?.find(ts.isSourceFile)?.fileName;
|
|
192
|
+
const namespaceMembers = (edge) => {
|
|
193
|
+
const cached = namespaceMembersCache.get(edge);
|
|
194
|
+
if (cached !== undefined) {
|
|
195
|
+
return cached;
|
|
196
|
+
}
|
|
197
|
+
const result = collectNamespaceMembers(edge.importer, edge.localName);
|
|
198
|
+
namespaceMembersCache.set(edge, result);
|
|
199
|
+
return result;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
const collectNamespaceMembers = (sourceFile, localName) => {
|
|
209
|
+
const mut_names = new Set();
|
|
210
|
+
const isNamespace = (node) => ts.isIdentifier(node) && node.text === localName;
|
|
211
|
+
|
|
212
|
+
const visit = (node) => {
|
|
213
|
+
if (ts.isImportDeclaration(node) ||
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
(ts.isTypeNode(node) && !ts.isExpressionWithTypeArguments(node))) {
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
if (ts.isPropertyAccessExpression(node) && isNamespace(node.expression)) {
|
|
221
|
+
mut_names.add(node.name.text);
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
if (ts.isElementAccessExpression(node) && isNamespace(node.expression)) {
|
|
225
|
+
if (!ts.isStringLiteralLike(node.argumentExpression)) {
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
mut_names.add(node.argumentExpression.text);
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
return ((isNamespace(node) && !isPropertyName(node)) ||
|
|
232
|
+
ts.forEachChild(node, visit));
|
|
233
|
+
};
|
|
234
|
+
return visit(sourceFile) === true ? 'all' : mut_names;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
const isPropertyName = (node) => {
|
|
241
|
+
const parent = node.parent;
|
|
242
|
+
return (((ts.isPropertyAccessExpression(parent) ||
|
|
243
|
+
ts.isPropertyAssignment(parent) ||
|
|
244
|
+
ts.isPropertyDeclaration(parent) ||
|
|
245
|
+
ts.isMethodDeclaration(parent) ||
|
|
246
|
+
ts.isGetAccessorDeclaration(parent) ||
|
|
247
|
+
ts.isSetAccessorDeclaration(parent) ||
|
|
248
|
+
ts.isEnumMember(parent)) &&
|
|
249
|
+
parent.name === node) ||
|
|
250
|
+
(ts.isBindingElement(parent) && parent.propertyName === node));
|
|
251
|
+
};
|
|
252
|
+
//# sourceMappingURL=export-usage.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-usage.mjs","sourceRoot":"","sources":["../../src/rules/export-usage.mts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
type Options = readonly [
|
|
3
|
+
Readonly<{
|
|
4
|
+
entryPoints?: readonly string[];
|
|
5
|
+
}>?
|
|
6
|
+
];
|
|
7
|
+
type MessageIds = 'typeOnlyCodec';
|
|
8
|
+
/**
|
|
9
|
+
* A codec exists to check values at runtime. One declared next to
|
|
10
|
+
* `type Y = t.TypeOf<typeof X>` and never read as a value is a type written the
|
|
11
|
+
* long way round: it costs a runtime object and a bundle import, and it reads
|
|
12
|
+
* as if something validated with it. This rule reports such a codec, so that
|
|
13
|
+
* `Y` becomes a plain type alias.
|
|
14
|
+
*
|
|
15
|
+
* "Never read as a value" means: no value reference in its own file outside
|
|
16
|
+
* `typeof`, every import of it elsewhere `import type`, and no entry point
|
|
17
|
+
* re-exporting it. The entry points are what the nearest `package.json`
|
|
18
|
+
* publishes, traced back to source, unless the `entryPoints` option names them.
|
|
19
|
+
* The cross-file part reads the importers from the TypeScript program; without
|
|
20
|
+
* type information, or when the published entry points cannot all be traced
|
|
21
|
+
* back to source, an exported codec is left alone.
|
|
22
|
+
*/
|
|
23
|
+
export declare const noTypeOnlyCodec: TSESLint.RuleModule<MessageIds, Options>;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=no-type-only-codec.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-type-only-codec.d.mts","sourceRoot":"","sources":["../../src/rules/no-type-only-codec.mts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,0BAA0B,CAAC;AAQlC,KAAK,OAAO,GAAG,SAAS;IACtB,QAAQ,CAAC;QACP,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;KACjC,CAAC,CAAC;CACJ,CAAC;AAEF,KAAK,UAAU,GAAG,eAAe,CAAC;AAKlC;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,UAAU,CAAC,UAAU,EAAE,OAAO,CA2F3D,CAAC"}
|