@jeremybarbet/oxlint-plugin 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +126 -0
- package/dist/glob.d.mts +1 -0
- package/dist/glob.mjs +33 -0
- package/dist/glob.mjs.map +1 -0
- package/dist/index.d.mts +21 -0
- package/dist/index.mjs +24 -0
- package/dist/index.mjs.map +1 -0
- package/dist/project-index.d.mts +37 -0
- package/dist/project-index.mjs +311 -0
- package/dist/project-index.mjs.map +1 -0
- package/dist/rules/no-export-all.d.mts +3 -0
- package/dist/rules/no-export-all.mjs +22 -0
- package/dist/rules/no-export-all.mjs.map +1 -0
- package/dist/rules/no-unused-class-members.d.mts +3 -0
- package/dist/rules/no-unused-class-members.mjs +116 -0
- package/dist/rules/no-unused-class-members.mjs.map +1 -0
- package/dist/rules/no-unused-modules.d.mts +3 -0
- package/dist/rules/no-unused-modules.mjs +115 -0
- package/dist/rules/no-unused-modules.mjs.map +1 -0
- package/dist/rules/no-unused-styles.d.mts +3 -0
- package/dist/rules/no-unused-styles.mjs +149 -0
- package/dist/rules/no-unused-styles.mjs.map +1 -0
- package/dist/rules/require-cache-control.d.mts +3 -0
- package/dist/rules/require-cache-control.mjs +105 -0
- package/dist/rules/require-cache-control.mjs.map +1 -0
- package/dist/types.d.mts +70 -0
- package/dist/types.mjs +9 -0
- package/dist/types.mjs.map +1 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# @jeremybarbet/oxlint-plugin
|
|
2
|
+
|
|
3
|
+
Shared [oxlint](https://oxc.rs) JS plugin.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
yarn add -D @jeremybarbet/oxlint-plugin
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
// oxlint.config.mts
|
|
13
|
+
import { defineConfig } from 'oxlint';
|
|
14
|
+
|
|
15
|
+
export default defineConfig({
|
|
16
|
+
jsPlugins: ['@jeremybarbet/oxlint-plugin'],
|
|
17
|
+
rules: {
|
|
18
|
+
'op/no-export-all': 'error',
|
|
19
|
+
'op/no-unused-modules': 'error',
|
|
20
|
+
'op/no-unused-class-members': 'error',
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Rules are prefixed `op/` by default. To use a different prefix — for instance to keep configs that
|
|
26
|
+
already say `local/*` working unchanged — use oxlint's alias form:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
jsPlugins: [{ name: 'local', specifier: '@jeremybarbet/oxlint-plugin' }],
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Rules
|
|
33
|
+
|
|
34
|
+
### `no-unused-modules`
|
|
35
|
+
|
|
36
|
+
Reports exports nothing else imports, and optionally files with no exports at all. A port of
|
|
37
|
+
`import/no-unused-modules` from `eslint-plugin-import`.
|
|
38
|
+
|
|
39
|
+
It overlaps with `knip`, but the diagnostic lands on the export itself, in the editor, on the same
|
|
40
|
+
pass as every other lint rule — rather than as a batch report from a separate command.
|
|
41
|
+
|
|
42
|
+
Not reported:
|
|
43
|
+
|
|
44
|
+
- Exports tagged `/** @public */` or `/** @beta */`, the same opt-out knip honours.
|
|
45
|
+
- Exports consumed inside their own file. Those are over-exposed, not dead — the fix is dropping the
|
|
46
|
+
`export` keyword, not deleting the declaration. Most exported return types look like this.
|
|
47
|
+
- Anything matching `**/*.config.*`, always. A config file is loaded by path by the tool that owns
|
|
48
|
+
it, so its default export is an entry point by definition.
|
|
49
|
+
|
|
50
|
+
| Option | Default | |
|
|
51
|
+
| --- | --- | --- |
|
|
52
|
+
| `unusedExports` | `true` | Report exports nothing imports. |
|
|
53
|
+
| `missingExports` | `false` | Report files with no exports. Noisy — every spec and mock qualifies. |
|
|
54
|
+
| `ignoreExports` | `[]` | Globs whose exports are never reported. Put your entry points here. |
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
'op/no-unused-modules': ['error', {
|
|
58
|
+
ignoreExports: ['scripts/**'],
|
|
59
|
+
}],
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### `no-unused-class-members`
|
|
63
|
+
|
|
64
|
+
Reports public class members that nothing outside the declaring file references.
|
|
65
|
+
|
|
66
|
+
This is the gap that motivated the package. `knip` analyses module-level exports only, so an
|
|
67
|
+
exported class that *is* used hides its entire body from it; `tsc`'s `noUnusedLocals` covers
|
|
68
|
+
`private` members alone. In a codebase where most logic lives on service and controller classes,
|
|
69
|
+
that leaves most of the code unchecked.
|
|
70
|
+
|
|
71
|
+
The check is name-based — oxlint exposes no type checker to JS plugins — so it is deliberately
|
|
72
|
+
conservative. A member whose name is referenced anywhere else, even on an unrelated class, counts
|
|
73
|
+
as used. It under-reports rather than pointing at live code.
|
|
74
|
+
|
|
75
|
+
Not reported:
|
|
76
|
+
|
|
77
|
+
- `private` and `#private` members, and constructor parameter properties.
|
|
78
|
+
- Any member carrying a decorator. A decorator means something outside the type system reaches it —
|
|
79
|
+
a `@Cron` schedule, a GraphQL `@Field`, a `class-validator` constraint.
|
|
80
|
+
- Members of classes that `extend` or `implement` anything, since those can be invoked
|
|
81
|
+
polymorphically by a base class or by whatever holds the interface.
|
|
82
|
+
- Members referenced only from test files (mirrors `knip --production`).
|
|
83
|
+
|
|
84
|
+
| Option | Default | |
|
|
85
|
+
| --- | --- | --- |
|
|
86
|
+
| `ignoreNames` | framework lifecycle hooks | Member names never reported. |
|
|
87
|
+
| `ignoreUsagesIn` | specs, mocks, `src/test/**` | Globs whose references do not count as usage. |
|
|
88
|
+
| `reportPrivateCandidates` | `false` | Also report members used *only* inside their own file, suggesting `private`. Off by default: those are a refactor prompt, not dead code. |
|
|
89
|
+
|
|
90
|
+
### `no-export-all`
|
|
91
|
+
|
|
92
|
+
Disallows `export *`, which defeats tree-shaking and invites name clashes.
|
|
93
|
+
|
|
94
|
+
### `no-unused-styles`
|
|
95
|
+
|
|
96
|
+
React Native. Reports entries in a `StyleSheet.create` object that nothing consumes. Understands the
|
|
97
|
+
plain object form and both function forms `react-native-unistyles` uses.
|
|
98
|
+
|
|
99
|
+
### `require-cache-control`
|
|
100
|
+
|
|
101
|
+
NestJS + Apollo. Requires `@CacheField` over `@Field` for non-scalar GraphQL object type fields, so a
|
|
102
|
+
nested type cannot silently break Apollo's cache-control computation. Ships an autofix suggestion.
|
|
103
|
+
|
|
104
|
+
## Project discovery
|
|
105
|
+
|
|
106
|
+
`no-unused-modules` and `no-unused-class-members` need a whole-project view, so they crawl and parse
|
|
107
|
+
the project once per process and answer per-file from the result. (Oxlint's plugin API is per-file
|
|
108
|
+
with no end-of-run hook, so this is the same approach `eslint-plugin-import` takes.) It costs roughly
|
|
109
|
+
120ms on an 830-file repo.
|
|
110
|
+
|
|
111
|
+
Discovery is zero-config: everything under the oxlint working directory that looks like source,
|
|
112
|
+
skipping `node_modules`, `dist`, `build`, `coverage`, `android`, `ios` and dotfile directories.
|
|
113
|
+
Import specifiers are resolved through `tsconfig.json` `paths`, including the nodenext `.js` → `.ts`
|
|
114
|
+
rewrite.
|
|
115
|
+
|
|
116
|
+
If the defaults are not enough, narrow it via `settings`. The key is the package name, not the rule
|
|
117
|
+
prefix, so it does not move when you alias the plugin:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
settings: {
|
|
121
|
+
'@jeremybarbet/oxlint-plugin': {
|
|
122
|
+
roots: ['src'],
|
|
123
|
+
ignore: ['src/api/generated'],
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
```
|
package/dist/glob.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const matchesGlob: (path: string, patterns: readonly string[]) => boolean;
|
package/dist/glob.mjs
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal glob matcher for the `ignoreExports` / `ignoreUsagesIn` rule options.
|
|
3
|
+
*
|
|
4
|
+
* Only the three forms the options actually use are supported — `**`, `*` and literal segments —
|
|
5
|
+
* which is not worth pulling a dependency in for. Patterns are matched against paths relative to
|
|
6
|
+
* the project root, with forward slashes.
|
|
7
|
+
*/
|
|
8
|
+
const toRegExp = (pattern) => {
|
|
9
|
+
const segments = pattern.split('/');
|
|
10
|
+
let source = '';
|
|
11
|
+
segments.forEach((segment, index) => {
|
|
12
|
+
const isLast = index === segments.length - 1;
|
|
13
|
+
if (segment === '**') {
|
|
14
|
+
// `**` spans zero or more segments, so `a/**/b` matches `a/b` and `**/x.ts` matches `x.ts`.
|
|
15
|
+
source += isLast ? '(?:[^/]+(?:/[^/]+)*)?' : '(?:[^/]+/)*';
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
source += segment.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '[^/]*');
|
|
19
|
+
if (!isLast) {
|
|
20
|
+
source += '/';
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
return new RegExp(`^${source}$`);
|
|
24
|
+
};
|
|
25
|
+
const cache = new Map();
|
|
26
|
+
export const matchesGlob = (path, patterns) => patterns.some(pattern => {
|
|
27
|
+
let regex = cache.get(pattern);
|
|
28
|
+
if (!regex) {
|
|
29
|
+
cache.set(pattern, (regex = toRegExp(pattern)));
|
|
30
|
+
}
|
|
31
|
+
return regex.test(path);
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=glob.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glob.mjs","sourceRoot":"","sources":["../src/glob.mts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,QAAQ,GAAG,CAAC,OAAe,EAAE,EAAE;IACnC,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,MAAM,GAAG,EAAE,CAAC;IAEhB,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QAClC,MAAM,MAAM,GAAG,KAAK,KAAK,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;QAE7C,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,4FAA4F;YAC5F,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,aAAa,CAAC;YAE3D,OAAO;QACT,CAAC;QAED,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAE/E,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,CAAC;QAChB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,IAAI,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,CAAC;AACnC,CAAC,CAAC;AAEF,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;AAExC,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,QAA2B,EAAE,EAAE,CACvE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;IACtB,IAAI,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAE/B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAClD,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC,CAAC,CAAC"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `meta.name` is the rule prefix when the plugin is loaded by bare specifier. Consumers that want a
|
|
3
|
+
* different prefix use oxlint's alias form instead:
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* jsPlugins: [{ name: 'local', specifier: '@jeremybarbet/oxlint-plugin' }]
|
|
7
|
+
* ```
|
|
8
|
+
*/
|
|
9
|
+
declare const _default: {
|
|
10
|
+
meta: {
|
|
11
|
+
name: string;
|
|
12
|
+
};
|
|
13
|
+
rules: {
|
|
14
|
+
'no-export-all': import("./types.mts").Rule;
|
|
15
|
+
'no-unused-class-members': import("./types.mts").Rule;
|
|
16
|
+
'no-unused-modules': import("./types.mts").Rule;
|
|
17
|
+
'no-unused-styles': import("./types.mts").Rule;
|
|
18
|
+
'require-cache-control': import("./types.mts").Rule;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export default _default;
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import noExportAll from "./rules/no-export-all.mjs";
|
|
2
|
+
import noUnusedClassMembers from "./rules/no-unused-class-members.mjs";
|
|
3
|
+
import noUnusedModules from "./rules/no-unused-modules.mjs";
|
|
4
|
+
import noUnusedStyles from "./rules/no-unused-styles.mjs";
|
|
5
|
+
import requireCacheControl from "./rules/require-cache-control.mjs";
|
|
6
|
+
/**
|
|
7
|
+
* `meta.name` is the rule prefix when the plugin is loaded by bare specifier. Consumers that want a
|
|
8
|
+
* different prefix use oxlint's alias form instead:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* jsPlugins: [{ name: 'local', specifier: '@jeremybarbet/oxlint-plugin' }]
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export default {
|
|
15
|
+
meta: { name: 'op' },
|
|
16
|
+
rules: {
|
|
17
|
+
'no-export-all': noExportAll,
|
|
18
|
+
'no-unused-class-members': noUnusedClassMembers,
|
|
19
|
+
'no-unused-modules': noUnusedModules,
|
|
20
|
+
'no-unused-styles': noUnusedStyles,
|
|
21
|
+
'require-cache-control': requireCacheControl,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.mts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,2BAA2B,CAAC;AACpD,OAAO,oBAAoB,MAAM,qCAAqC,CAAC;AACvE,OAAO,eAAe,MAAM,+BAA+B,CAAC;AAC5D,OAAO,cAAc,MAAM,8BAA8B,CAAC;AAC1D,OAAO,mBAAmB,MAAM,mCAAmC,CAAC;AAEpE;;;;;;;GAOG;AACH,eAAe;IACb,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;IACpB,KAAK,EAAE;QACL,eAAe,EAAE,WAAW;QAC5B,yBAAyB,EAAE,oBAAoB;QAC/C,mBAAmB,EAAE,eAAe;QACpC,kBAAkB,EAAE,cAAc;QAClC,uBAAuB,EAAE,mBAAmB;KAC7C;CACF,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface ProjectSettings {
|
|
2
|
+
/** Directories to crawl, relative to the project root. Defaults to the whole root. */
|
|
3
|
+
roots?: string[];
|
|
4
|
+
/** Extra path fragments to skip, matched as substrings of the absolute path. */
|
|
5
|
+
ignore?: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare const readProjectSettings: (context: {
|
|
8
|
+
settings?: Record<string, unknown>;
|
|
9
|
+
}) => ProjectSettings;
|
|
10
|
+
interface FileIndex {
|
|
11
|
+
path: string;
|
|
12
|
+
isTest: boolean;
|
|
13
|
+
/** Exported name -> byte offset of the exported binding, for reporting. `default` for default exports. */
|
|
14
|
+
exports: Map<string, number>;
|
|
15
|
+
/** Resolved absolute path -> names this file pulls in. `*` means a namespace import. */
|
|
16
|
+
imports: Map<string, Set<string>>;
|
|
17
|
+
/** Every identifier used in a member-access position: `o.x`, `o?.x`, `o['x']`, `{ x: … }`. */
|
|
18
|
+
memberNames: Set<string>;
|
|
19
|
+
/** Names referenced somewhere in this file other than their own declaration or export clause. */
|
|
20
|
+
localReferences: Set<string>;
|
|
21
|
+
/** Exports carrying a `@public` / `@beta` JSDoc tag, the same opt-out knip honours. */
|
|
22
|
+
intentionalExports: Set<string>;
|
|
23
|
+
}
|
|
24
|
+
export interface ProjectIndex {
|
|
25
|
+
root: string;
|
|
26
|
+
files: Map<string, FileIndex>;
|
|
27
|
+
/** Resolved absolute path -> names imported from it by *other* files. */
|
|
28
|
+
importedFrom: Map<string, Set<string>>;
|
|
29
|
+
/** Member name -> absolute paths of the files referencing it. */
|
|
30
|
+
memberUsage: Map<string, Set<string>>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Built once per root and memoised. Oxlint may run JS plugins on more than one worker, in which
|
|
34
|
+
* case this runs once per worker — still cheap enough not to warrant an on-disk cache.
|
|
35
|
+
*/
|
|
36
|
+
export declare const getProjectIndex: (cwd: string, settings?: ProjectSettings) => ProjectIndex;
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, resolve } from 'node:path';
|
|
3
|
+
import { parseSync } from 'oxc-parser';
|
|
4
|
+
/**
|
|
5
|
+
* Whole-project import/export and member-usage index, shared by `no-unused-modules` and
|
|
6
|
+
* `no-unused-class-members`.
|
|
7
|
+
*
|
|
8
|
+
* Oxlint's JS plugin API is per-file: `before`/`after` hooks fire around each file, and there is
|
|
9
|
+
* no end-of-run hook a rule could report from. So, like `eslint-plugin-import` before it, the
|
|
10
|
+
* rules answer cross-file questions by crawling and parsing the project themselves, once, and
|
|
11
|
+
* consulting the result while linting each file. The whole crawl is ~120ms for an 830-file repo,
|
|
12
|
+
* which is why there is no on-disk cache — see the note in `getProjectIndex`.
|
|
13
|
+
*
|
|
14
|
+
* Discovery is zero-config: everything under the project root that looks like source, minus the
|
|
15
|
+
* directories below. A project that needs more can narrow it through oxlint `settings`:
|
|
16
|
+
*/
|
|
17
|
+
/** Directory names never worth crawling. Skipped at any depth. */
|
|
18
|
+
const IGNORED_DIRS = new Set([
|
|
19
|
+
'node_modules',
|
|
20
|
+
'dist',
|
|
21
|
+
'build',
|
|
22
|
+
'coverage',
|
|
23
|
+
'.git',
|
|
24
|
+
'.yarn',
|
|
25
|
+
'.expo',
|
|
26
|
+
'.next',
|
|
27
|
+
'.turbo',
|
|
28
|
+
'android',
|
|
29
|
+
'ios',
|
|
30
|
+
]);
|
|
31
|
+
const SOURCE_FILE = /\.([mc]?[tj]s|[tj]sx)$/;
|
|
32
|
+
/** Extensions tried when resolving an import specifier, in order. */
|
|
33
|
+
const RESOLVE_EXTENSIONS = ['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs'];
|
|
34
|
+
/** Mirrors `knip --production`: nothing a test reaches counts as keeping production code alive. */
|
|
35
|
+
const TEST_FILE = /(\.(spec|test)\.[mc]?[tj]sx?|\.mock\.[mc]?[tj]sx?)$/;
|
|
36
|
+
const TEST_DIRS = ['/src/test/', '/__tests__/', '/__mocks__/'];
|
|
37
|
+
/**
|
|
38
|
+
* Settings key. Deliberately the package name rather than the rule prefix: the prefix changes when
|
|
39
|
+
* a project aliases the plugin (`{ name: 'local', specifier: … }`), and the settings key should not
|
|
40
|
+
* move with it.
|
|
41
|
+
*/
|
|
42
|
+
const SETTINGS_KEY = '@jeremybarbet/oxlint-plugin';
|
|
43
|
+
export const readProjectSettings = (context) => context.settings?.[SETTINGS_KEY] ?? {};
|
|
44
|
+
const addTo = (map, key, value) => {
|
|
45
|
+
let set = map.get(key);
|
|
46
|
+
if (!set) {
|
|
47
|
+
map.set(key, (set = new Set()));
|
|
48
|
+
}
|
|
49
|
+
set.add(value);
|
|
50
|
+
};
|
|
51
|
+
const collectFiles = (dir, ignore, out) => {
|
|
52
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
53
|
+
const path = join(dir, entry.name);
|
|
54
|
+
if (ignore.some(fragment => path.includes(fragment))) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (entry.isDirectory()) {
|
|
58
|
+
// Dotfile directories are build/tool state far more often than source.
|
|
59
|
+
if (IGNORED_DIRS.has(entry.name) || entry.name.startsWith('.')) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
collectFiles(path, ignore, out);
|
|
63
|
+
}
|
|
64
|
+
else if (SOURCE_FILE.test(entry.name)) {
|
|
65
|
+
out.push(path);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Read the `paths` aliases straight from `tsconfig.json` rather than hardcoding `@/*`, so the
|
|
71
|
+
* index cannot drift from what the compiler actually resolves.
|
|
72
|
+
*/
|
|
73
|
+
const readAliases = (root) => {
|
|
74
|
+
try {
|
|
75
|
+
const raw = readFileSync(join(root, 'tsconfig.json'), 'utf8');
|
|
76
|
+
const paths = JSON.parse(raw).compilerOptions
|
|
77
|
+
?.paths;
|
|
78
|
+
return Object.entries(paths ?? {})
|
|
79
|
+
.filter(([alias]) => alias.endsWith('/*'))
|
|
80
|
+
.map(([alias, [target]]) => [alias.slice(0, -1), join(root, target.replace(/^\.\//, '').slice(0, -1))]);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* `"moduleResolution": "nodenext"` means a relative import may spell an explicit `.js` extension
|
|
88
|
+
* that has to land on the `.ts` source — the same rewrite `jest.config.js` does in its
|
|
89
|
+
* `moduleNameMapper`. Everything else is ordinary extension/index probing.
|
|
90
|
+
*/
|
|
91
|
+
const resolveSpecifier = (specifier, fromFile, root, aliases) => {
|
|
92
|
+
let base = null;
|
|
93
|
+
if (specifier.startsWith('.')) {
|
|
94
|
+
base = resolve(dirname(fromFile), specifier);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
for (const [prefix, target] of aliases) {
|
|
98
|
+
if (specifier.startsWith(prefix)) {
|
|
99
|
+
base = join(target, specifier.slice(prefix.length));
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (base === null) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
// A `.js`/`.mjs` specifier under nodenext usually points at a `.ts`/`.mts` source.
|
|
108
|
+
const rewritten = base.replace(/\.([mc]?)js$/, '.$1ts');
|
|
109
|
+
const candidates = rewritten === base
|
|
110
|
+
? [...RESOLVE_EXTENSIONS.map(extension => `${base}${extension}`), base]
|
|
111
|
+
: [rewritten, `${rewritten}x`, base];
|
|
112
|
+
for (const candidate of candidates) {
|
|
113
|
+
if (existsSync(candidate) && statSync(candidate).isFile()) {
|
|
114
|
+
return candidate;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
for (const extension of RESOLVE_EXTENSIONS) {
|
|
118
|
+
const candidate = join(base, `index${extension}`);
|
|
119
|
+
if (existsSync(candidate)) {
|
|
120
|
+
return candidate;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
};
|
|
125
|
+
/** Node types whose `id` names the thing being declared rather than referencing it. */
|
|
126
|
+
const DECLARES_ITS_ID = new Set([
|
|
127
|
+
'VariableDeclarator',
|
|
128
|
+
'FunctionDeclaration',
|
|
129
|
+
'FunctionExpression',
|
|
130
|
+
'ClassDeclaration',
|
|
131
|
+
'ClassExpression',
|
|
132
|
+
'TSInterfaceDeclaration',
|
|
133
|
+
'TSTypeAliasDeclaration',
|
|
134
|
+
'TSEnumDeclaration',
|
|
135
|
+
'TSModuleDeclaration',
|
|
136
|
+
]);
|
|
137
|
+
/**
|
|
138
|
+
* Single walk collecting both name sets.
|
|
139
|
+
*
|
|
140
|
+
* A member is "used" wherever its name appears in a position that could read it. Object literal
|
|
141
|
+
* keys and computed string accesses count, because a name reached that way is indistinguishable
|
|
142
|
+
* from a direct read without type information — the index deliberately over-counts usage so the
|
|
143
|
+
* rules under-report rather than point at live code.
|
|
144
|
+
*
|
|
145
|
+
* `localReferences` is the narrower question of whether an export is consumed inside its own file,
|
|
146
|
+
* so it skips the two places a name appears without being read: its own declaration `id`, and the
|
|
147
|
+
* specifiers of an `export { … }` clause.
|
|
148
|
+
*/
|
|
149
|
+
const collectNames = (node, members, locals) => {
|
|
150
|
+
if (node === null || typeof node !== 'object') {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (Array.isArray(node)) {
|
|
154
|
+
for (const child of node) {
|
|
155
|
+
collectNames(child, members, locals);
|
|
156
|
+
}
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const record = node;
|
|
160
|
+
const type = record.type;
|
|
161
|
+
if (type === 'MemberExpression') {
|
|
162
|
+
if (!record.computed && record.property?.type === 'Identifier') {
|
|
163
|
+
members.add(record.property.name);
|
|
164
|
+
}
|
|
165
|
+
else if (record.computed && record.property?.type === 'Literal' && typeof record.property.value === 'string') {
|
|
166
|
+
members.add(record.property.value);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else if (type === 'Property' && !record.computed && record.key?.type === 'Identifier') {
|
|
170
|
+
members.add(record.key.name);
|
|
171
|
+
}
|
|
172
|
+
else if (type === 'Literal' && typeof record.value === 'string') {
|
|
173
|
+
members.add(record.value);
|
|
174
|
+
}
|
|
175
|
+
else if (type === 'Identifier') {
|
|
176
|
+
locals.add(record.name);
|
|
177
|
+
}
|
|
178
|
+
const skipId = DECLARES_ITS_ID.has(type);
|
|
179
|
+
for (const key in record) {
|
|
180
|
+
if (key === 'type' || key === 'start' || key === 'end' || key === 'range' || key === 'loc') {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (skipId && key === 'id') {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
// An `export { x }` / `export { x as y }` clause names `x` without consuming it.
|
|
187
|
+
if ((type === 'ExportSpecifier' || type === 'ImportSpecifier') && (key === 'local' || key === 'exported')) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
collectNames(record[key], members, locals);
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
const indexFile = (path, root, aliases) => {
|
|
194
|
+
const code = readFileSync(path, 'utf8');
|
|
195
|
+
const parsed = parseSync(path, code, { sourceType: 'module' });
|
|
196
|
+
const file = {
|
|
197
|
+
path,
|
|
198
|
+
isTest: TEST_FILE.test(path) || TEST_DIRS.some(dir => path.includes(dir)),
|
|
199
|
+
exports: new Map(),
|
|
200
|
+
imports: new Map(),
|
|
201
|
+
memberNames: new Set(),
|
|
202
|
+
localReferences: new Set(),
|
|
203
|
+
intentionalExports: new Set(),
|
|
204
|
+
};
|
|
205
|
+
const comments = [...parsed.comments].sort((a, b) => a.end - b.end);
|
|
206
|
+
/** A JSDoc block counts as attached only if nothing but whitespace separates it from the export. */
|
|
207
|
+
const leadingTag = (start) => {
|
|
208
|
+
for (let i = comments.length - 1; i >= 0; i -= 1) {
|
|
209
|
+
const comment = comments[i];
|
|
210
|
+
if (comment.end > start) {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
return code.slice(comment.end, start).trim() === '' ? comment.value : null;
|
|
214
|
+
}
|
|
215
|
+
return null;
|
|
216
|
+
};
|
|
217
|
+
const record = (specifier, name) => {
|
|
218
|
+
const target = resolveSpecifier(specifier, path, root, aliases);
|
|
219
|
+
if (target !== null) {
|
|
220
|
+
addTo(file.imports, target, name);
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
// `kind` distinguishes `import x`, `import * as x` and `import { x }`; only the last carries a name.
|
|
224
|
+
const importedName = (kind, name) => {
|
|
225
|
+
if (kind === 'NamespaceObject') {
|
|
226
|
+
return '*';
|
|
227
|
+
}
|
|
228
|
+
return kind === 'Default' ? 'default' : (name ?? '*');
|
|
229
|
+
};
|
|
230
|
+
for (const statement of parsed.module.staticImports) {
|
|
231
|
+
for (const entry of statement.entries) {
|
|
232
|
+
record(statement.moduleRequest.value, importedName(entry.importName.kind, entry.importName.name));
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
for (const statement of parsed.module.staticExports) {
|
|
236
|
+
const tag = leadingTag(statement.start);
|
|
237
|
+
const isIntentional = tag !== null && /@(public|beta)\b/.test(tag);
|
|
238
|
+
for (const entry of statement.entries) {
|
|
239
|
+
// `export … from './x'` both re-exports and consumes, so it counts as a use of the source.
|
|
240
|
+
if (entry.moduleRequest) {
|
|
241
|
+
record(entry.moduleRequest.value, importedName(entry.importName.kind, entry.importName.name));
|
|
242
|
+
}
|
|
243
|
+
const exported = entry.exportName.kind === 'Name' && entry.exportName.name
|
|
244
|
+
? entry.exportName.name
|
|
245
|
+
: entry.exportName.kind === 'Default'
|
|
246
|
+
? 'default'
|
|
247
|
+
: null;
|
|
248
|
+
if (exported === null) {
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
file.exports.set(exported, entry.exportName.start ?? entry.start);
|
|
252
|
+
if (isIntentional) {
|
|
253
|
+
file.intentionalExports.add(exported);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
for (const dynamic of parsed.module.dynamicImports) {
|
|
258
|
+
const specifier = code.slice(dynamic.moduleRequest.start, dynamic.moduleRequest.end).replace(/^['"`]|['"`]$/g, '');
|
|
259
|
+
// The imported names are unknowable statically, so treat it as a namespace use.
|
|
260
|
+
record(specifier, '*');
|
|
261
|
+
}
|
|
262
|
+
collectNames(parsed.program, file.memberNames, file.localReferences);
|
|
263
|
+
return file;
|
|
264
|
+
};
|
|
265
|
+
/** Keyed by root: the module is shared now, and one process can lint more than one project. */
|
|
266
|
+
const cache = new Map();
|
|
267
|
+
/**
|
|
268
|
+
* Built once per root and memoised. Oxlint may run JS plugins on more than one worker, in which
|
|
269
|
+
* case this runs once per worker — still cheap enough not to warrant an on-disk cache.
|
|
270
|
+
*/
|
|
271
|
+
export const getProjectIndex = (cwd, settings = {}) => {
|
|
272
|
+
const hit = cache.get(cwd);
|
|
273
|
+
if (hit) {
|
|
274
|
+
return hit;
|
|
275
|
+
}
|
|
276
|
+
const root = cwd;
|
|
277
|
+
const aliases = readAliases(root);
|
|
278
|
+
const ignore = settings.ignore ?? [];
|
|
279
|
+
const paths = [];
|
|
280
|
+
// No `roots` configured means the whole project, minus `IGNORED_DIRS`.
|
|
281
|
+
for (const dir of settings.roots ?? ['.']) {
|
|
282
|
+
const full = resolve(root, dir);
|
|
283
|
+
if (existsSync(full)) {
|
|
284
|
+
collectFiles(full, ignore, paths);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
const files = new Map();
|
|
288
|
+
const importedFrom = new Map();
|
|
289
|
+
const memberUsage = new Map();
|
|
290
|
+
for (const path of paths) {
|
|
291
|
+
const file = indexFile(path, root, aliases);
|
|
292
|
+
files.set(path, file);
|
|
293
|
+
for (const name of file.memberNames) {
|
|
294
|
+
addTo(memberUsage, name, path);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
for (const file of files.values()) {
|
|
298
|
+
for (const [target, names] of file.imports) {
|
|
299
|
+
if (target === file.path) {
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
for (const name of names) {
|
|
303
|
+
addTo(importedFrom, target, name);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
const index = { root, files, importedFrom, memberUsage };
|
|
308
|
+
cache.set(cwd, index);
|
|
309
|
+
return index;
|
|
310
|
+
};
|
|
311
|
+
//# sourceMappingURL=project-index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-index.mjs","sourceRoot":"","sources":["../src/project-index.mts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {} from "../types.mjs";
|
|
2
|
+
const rule = {
|
|
3
|
+
meta: {
|
|
4
|
+
type: 'suggestion',
|
|
5
|
+
docs: {
|
|
6
|
+
description: 'Disallow `export *` re-exports',
|
|
7
|
+
},
|
|
8
|
+
messages: {
|
|
9
|
+
noExportAll: 'Prefer explicit exports over `export *` to avoid name clashes, and improve tree-shakeability.',
|
|
10
|
+
},
|
|
11
|
+
schema: [],
|
|
12
|
+
},
|
|
13
|
+
create(context) {
|
|
14
|
+
return {
|
|
15
|
+
ExportAllDeclaration(node) {
|
|
16
|
+
context.report({ node, messageId: 'noExportAll' });
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
export default rule;
|
|
22
|
+
//# sourceMappingURL=no-export-all.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-export-all.mjs","sourceRoot":"","sources":["../../src/rules/no-export-all.mts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,MAAM,cAAc,CAAC;AAElE,MAAM,IAAI,GAAS;IACjB,IAAI,EAAE;QACJ,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE;YACJ,WAAW,EAAE,gCAAgC;SAC9C;QACD,QAAQ,EAAE;YACR,WAAW,EAAE,+FAA+F;SAC7G;QACD,MAAM,EAAE,EAAE;KACX;IACD,MAAM,CAAC,OAAgB;QACrB,OAAO;YACL,oBAAoB,CAAC,IAAU;gBAC7B,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC,CAAC;YACrD,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,eAAe,IAAI,CAAC"}
|