@nx-devkit/knip 0.0.0 → 0.0.1
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 +62 -1
- package/dist/generators/init/generator.d.mts +10 -0
- package/dist/generators/init/generator.d.mts.map +1 -0
- package/dist/generators/init/generator.mjs +283 -0
- package/dist/generators/init/generator.mjs.map +1 -0
- package/dist/plugin.d.mts +22 -0
- package/dist/plugin.d.mts.map +1 -0
- package/dist/plugin.mjs +90 -0
- package/dist/plugin.mjs.map +1 -0
- package/generators.json +10 -0
- package/package.json +48 -12
- package/src/generators/init/schema.json +15 -0
package/README.md
CHANGED
|
@@ -1,3 +1,64 @@
|
|
|
1
1
|
# @nx-devkit/knip
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Standalone Nx plugin: any `knip` config file — or a `package.json` carrying a `"knip"` key — becomes a cached `knip` target (plus `knip:fix`) on the owning project. Dead exports, unused files and unused dependencies, detected per project and cached by Nx. No `project.json` needed.
|
|
4
|
+
|
|
5
|
+
Part of [nx-devkit](https://github.com/nx-devkit/nx.ts).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bun add -D @nx-devkit/knip knip
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Depends on `@nx/devkit` `^22 || ^23`. Requires `knip` `^5 || ^6` as a peer.
|
|
14
|
+
|
|
15
|
+
## Register
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
nx add @nx-devkit/knip # runs the init generator — registers the plugin in nx.json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or manually:
|
|
22
|
+
|
|
23
|
+
```jsonc
|
|
24
|
+
// nx.json
|
|
25
|
+
{ "plugins": ["@nx-devkit/knip"] }
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## What it infers
|
|
29
|
+
|
|
30
|
+
<!-- target table consistent with src/plugin.ts createNodesV2 -->
|
|
31
|
+
|
|
32
|
+
| Trigger | Target | Command | Cacheable | Inputs |
|
|
33
|
+
|---|---|---|---|---|
|
|
34
|
+
| `knip.{json,jsonc,ts}`, `knip.config.{ts,js,mjs,cjs}`, or `package.json` with a `"knip"` key | `knip` | `npx knip` (cwd = `{projectRoot}`) | yes | `<root>/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,json,jsonc}` + the config files |
|
|
35
|
+
| either of the above | `knip:fix` | `npx knip --fix` | no | — |
|
|
36
|
+
|
|
37
|
+
Knip auto-discovers its config from the working directory, so the plugin never passes `--config` — the target simply runs `knip` in the directory that owns the config. When several config forms live in one directory they merge into a single target pair (`knip.json` wins, matching knip's own discovery order). A `package.json` *without* a `"knip"` key infers nothing — it is only scanned as a possible config host.
|
|
38
|
+
|
|
39
|
+
## Options
|
|
40
|
+
|
|
41
|
+
```jsonc
|
|
42
|
+
// nx.json
|
|
43
|
+
{
|
|
44
|
+
"plugins": [
|
|
45
|
+
{
|
|
46
|
+
"plugin": "@nx-devkit/knip",
|
|
47
|
+
"options": {
|
|
48
|
+
"targetName": "knip", // rename the target
|
|
49
|
+
"fixTarget": false // do not infer `knip:fix` (default: true)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Inspect
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npx nx show project my-app # see inferred targets
|
|
60
|
+
npx nx run my-app:knip # analyze
|
|
61
|
+
npx nx run my-app:knip:fix # auto-remove unused exports/deps
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
CI tip: run `npx nx affected -t knip` to analyze only the projects touched by a change.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { GeneratorCallback, Tree } from "@nx/devkit";
|
|
2
|
+
|
|
3
|
+
//#region src/generators/init/generator.d.ts
|
|
4
|
+
interface NxKnipInitOptions {
|
|
5
|
+
pluginPath?: string;
|
|
6
|
+
}
|
|
7
|
+
declare function initGenerator(tree: Tree, options?: NxKnipInitOptions): Promise<GeneratorCallback>;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { NxKnipInitOptions, initGenerator as default, initGenerator };
|
|
10
|
+
//# sourceMappingURL=generator.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.d.mts","names":[],"sources":["../../../src/generators/init/generator.ts"],"mappings":";;;UAGiB,iBAAA;EACf,UAAU;AAAA;AAAA,iBAKU,aAAA,CACpB,IAAA,EAAM,IAAA,EACN,OAAA,GAAS,iBAAA,GACR,OAAA,CAAQ,iBAAA"}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { formatFiles } from "@nx/devkit";
|
|
3
|
+
//#region \0rolldown/runtime.js
|
|
4
|
+
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
5
|
+
var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region ../internal/src/jsonc.ts
|
|
8
|
+
var import_main = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
9
|
+
(function(factory) {
|
|
10
|
+
if (typeof module === "object" && typeof module.exports === "object") {
|
|
11
|
+
var v = factory(__require, exports);
|
|
12
|
+
if (v !== void 0) module.exports = v;
|
|
13
|
+
} else if (typeof define === "function" && define.amd) define([
|
|
14
|
+
"require",
|
|
15
|
+
"exports",
|
|
16
|
+
"./impl/format",
|
|
17
|
+
"./impl/edit",
|
|
18
|
+
"./impl/scanner",
|
|
19
|
+
"./impl/parser"
|
|
20
|
+
], factory);
|
|
21
|
+
})(function(require, exports$1) {
|
|
22
|
+
"use strict";
|
|
23
|
+
Object.defineProperty(exports$1, "__esModule", { value: true });
|
|
24
|
+
exports$1.applyEdits = exports$1.modify = exports$1.format = exports$1.printParseErrorCode = exports$1.ParseErrorCode = exports$1.stripComments = exports$1.visit = exports$1.getNodeValue = exports$1.getNodePath = exports$1.findNodeAtOffset = exports$1.findNodeAtLocation = exports$1.parseTree = exports$1.parse = exports$1.getLocation = exports$1.SyntaxKind = exports$1.ScanError = exports$1.createScanner = void 0;
|
|
25
|
+
const formatter = require("./impl/format");
|
|
26
|
+
const edit = require("./impl/edit");
|
|
27
|
+
const scanner = require("./impl/scanner");
|
|
28
|
+
const parser = require("./impl/parser");
|
|
29
|
+
/**
|
|
30
|
+
* Creates a JSON scanner on the given text.
|
|
31
|
+
* If ignoreTrivia is set, whitespaces or comments are ignored.
|
|
32
|
+
*/
|
|
33
|
+
exports$1.createScanner = scanner.createScanner;
|
|
34
|
+
var ScanError;
|
|
35
|
+
(function(ScanError) {
|
|
36
|
+
ScanError[ScanError["None"] = 0] = "None";
|
|
37
|
+
ScanError[ScanError["UnexpectedEndOfComment"] = 1] = "UnexpectedEndOfComment";
|
|
38
|
+
ScanError[ScanError["UnexpectedEndOfString"] = 2] = "UnexpectedEndOfString";
|
|
39
|
+
ScanError[ScanError["UnexpectedEndOfNumber"] = 3] = "UnexpectedEndOfNumber";
|
|
40
|
+
ScanError[ScanError["InvalidUnicode"] = 4] = "InvalidUnicode";
|
|
41
|
+
ScanError[ScanError["InvalidEscapeCharacter"] = 5] = "InvalidEscapeCharacter";
|
|
42
|
+
ScanError[ScanError["InvalidCharacter"] = 6] = "InvalidCharacter";
|
|
43
|
+
})(ScanError || (exports$1.ScanError = ScanError = {}));
|
|
44
|
+
var SyntaxKind;
|
|
45
|
+
(function(SyntaxKind) {
|
|
46
|
+
SyntaxKind[SyntaxKind["OpenBraceToken"] = 1] = "OpenBraceToken";
|
|
47
|
+
SyntaxKind[SyntaxKind["CloseBraceToken"] = 2] = "CloseBraceToken";
|
|
48
|
+
SyntaxKind[SyntaxKind["OpenBracketToken"] = 3] = "OpenBracketToken";
|
|
49
|
+
SyntaxKind[SyntaxKind["CloseBracketToken"] = 4] = "CloseBracketToken";
|
|
50
|
+
SyntaxKind[SyntaxKind["CommaToken"] = 5] = "CommaToken";
|
|
51
|
+
SyntaxKind[SyntaxKind["ColonToken"] = 6] = "ColonToken";
|
|
52
|
+
SyntaxKind[SyntaxKind["NullKeyword"] = 7] = "NullKeyword";
|
|
53
|
+
SyntaxKind[SyntaxKind["TrueKeyword"] = 8] = "TrueKeyword";
|
|
54
|
+
SyntaxKind[SyntaxKind["FalseKeyword"] = 9] = "FalseKeyword";
|
|
55
|
+
SyntaxKind[SyntaxKind["StringLiteral"] = 10] = "StringLiteral";
|
|
56
|
+
SyntaxKind[SyntaxKind["NumericLiteral"] = 11] = "NumericLiteral";
|
|
57
|
+
SyntaxKind[SyntaxKind["LineCommentTrivia"] = 12] = "LineCommentTrivia";
|
|
58
|
+
SyntaxKind[SyntaxKind["BlockCommentTrivia"] = 13] = "BlockCommentTrivia";
|
|
59
|
+
SyntaxKind[SyntaxKind["LineBreakTrivia"] = 14] = "LineBreakTrivia";
|
|
60
|
+
SyntaxKind[SyntaxKind["Trivia"] = 15] = "Trivia";
|
|
61
|
+
SyntaxKind[SyntaxKind["Unknown"] = 16] = "Unknown";
|
|
62
|
+
SyntaxKind[SyntaxKind["EOF"] = 17] = "EOF";
|
|
63
|
+
})(SyntaxKind || (exports$1.SyntaxKind = SyntaxKind = {}));
|
|
64
|
+
/**
|
|
65
|
+
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
|
|
66
|
+
*/
|
|
67
|
+
exports$1.getLocation = parser.getLocation;
|
|
68
|
+
/**
|
|
69
|
+
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
|
|
70
|
+
* Therefore, always check the errors list to find out if the input was valid.
|
|
71
|
+
*/
|
|
72
|
+
exports$1.parse = parser.parse;
|
|
73
|
+
/**
|
|
74
|
+
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
|
|
75
|
+
*/
|
|
76
|
+
exports$1.parseTree = parser.parseTree;
|
|
77
|
+
/**
|
|
78
|
+
* Finds the node at the given path in a JSON DOM.
|
|
79
|
+
*/
|
|
80
|
+
exports$1.findNodeAtLocation = parser.findNodeAtLocation;
|
|
81
|
+
/**
|
|
82
|
+
* Finds the innermost node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
|
|
83
|
+
*/
|
|
84
|
+
exports$1.findNodeAtOffset = parser.findNodeAtOffset;
|
|
85
|
+
/**
|
|
86
|
+
* Gets the JSON path of the given JSON DOM node
|
|
87
|
+
*/
|
|
88
|
+
exports$1.getNodePath = parser.getNodePath;
|
|
89
|
+
/**
|
|
90
|
+
* Evaluates the JavaScript object of the given JSON DOM node
|
|
91
|
+
*/
|
|
92
|
+
exports$1.getNodeValue = parser.getNodeValue;
|
|
93
|
+
/**
|
|
94
|
+
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
|
|
95
|
+
*/
|
|
96
|
+
exports$1.visit = parser.visit;
|
|
97
|
+
/**
|
|
98
|
+
* Takes JSON with JavaScript-style comments and remove
|
|
99
|
+
* them. Optionally replaces every none-newline character
|
|
100
|
+
* of comments with a replaceCharacter
|
|
101
|
+
*/
|
|
102
|
+
exports$1.stripComments = parser.stripComments;
|
|
103
|
+
var ParseErrorCode;
|
|
104
|
+
(function(ParseErrorCode) {
|
|
105
|
+
ParseErrorCode[ParseErrorCode["InvalidSymbol"] = 1] = "InvalidSymbol";
|
|
106
|
+
ParseErrorCode[ParseErrorCode["InvalidNumberFormat"] = 2] = "InvalidNumberFormat";
|
|
107
|
+
ParseErrorCode[ParseErrorCode["PropertyNameExpected"] = 3] = "PropertyNameExpected";
|
|
108
|
+
ParseErrorCode[ParseErrorCode["ValueExpected"] = 4] = "ValueExpected";
|
|
109
|
+
ParseErrorCode[ParseErrorCode["ColonExpected"] = 5] = "ColonExpected";
|
|
110
|
+
ParseErrorCode[ParseErrorCode["CommaExpected"] = 6] = "CommaExpected";
|
|
111
|
+
ParseErrorCode[ParseErrorCode["CloseBraceExpected"] = 7] = "CloseBraceExpected";
|
|
112
|
+
ParseErrorCode[ParseErrorCode["CloseBracketExpected"] = 8] = "CloseBracketExpected";
|
|
113
|
+
ParseErrorCode[ParseErrorCode["EndOfFileExpected"] = 9] = "EndOfFileExpected";
|
|
114
|
+
ParseErrorCode[ParseErrorCode["InvalidCommentToken"] = 10] = "InvalidCommentToken";
|
|
115
|
+
ParseErrorCode[ParseErrorCode["UnexpectedEndOfComment"] = 11] = "UnexpectedEndOfComment";
|
|
116
|
+
ParseErrorCode[ParseErrorCode["UnexpectedEndOfString"] = 12] = "UnexpectedEndOfString";
|
|
117
|
+
ParseErrorCode[ParseErrorCode["UnexpectedEndOfNumber"] = 13] = "UnexpectedEndOfNumber";
|
|
118
|
+
ParseErrorCode[ParseErrorCode["InvalidUnicode"] = 14] = "InvalidUnicode";
|
|
119
|
+
ParseErrorCode[ParseErrorCode["InvalidEscapeCharacter"] = 15] = "InvalidEscapeCharacter";
|
|
120
|
+
ParseErrorCode[ParseErrorCode["InvalidCharacter"] = 16] = "InvalidCharacter";
|
|
121
|
+
})(ParseErrorCode || (exports$1.ParseErrorCode = ParseErrorCode = {}));
|
|
122
|
+
function printParseErrorCode(code) {
|
|
123
|
+
switch (code) {
|
|
124
|
+
case 1: return "InvalidSymbol";
|
|
125
|
+
case 2: return "InvalidNumberFormat";
|
|
126
|
+
case 3: return "PropertyNameExpected";
|
|
127
|
+
case 4: return "ValueExpected";
|
|
128
|
+
case 5: return "ColonExpected";
|
|
129
|
+
case 6: return "CommaExpected";
|
|
130
|
+
case 7: return "CloseBraceExpected";
|
|
131
|
+
case 8: return "CloseBracketExpected";
|
|
132
|
+
case 9: return "EndOfFileExpected";
|
|
133
|
+
case 10: return "InvalidCommentToken";
|
|
134
|
+
case 11: return "UnexpectedEndOfComment";
|
|
135
|
+
case 12: return "UnexpectedEndOfString";
|
|
136
|
+
case 13: return "UnexpectedEndOfNumber";
|
|
137
|
+
case 14: return "InvalidUnicode";
|
|
138
|
+
case 15: return "InvalidEscapeCharacter";
|
|
139
|
+
case 16: return "InvalidCharacter";
|
|
140
|
+
}
|
|
141
|
+
return "<unknown ParseErrorCode>";
|
|
142
|
+
}
|
|
143
|
+
exports$1.printParseErrorCode = printParseErrorCode;
|
|
144
|
+
/**
|
|
145
|
+
* Computes the edit operations needed to format a JSON document.
|
|
146
|
+
*
|
|
147
|
+
* @param documentText The input text
|
|
148
|
+
* @param range The range to format or `undefined` to format the full content
|
|
149
|
+
* @param options The formatting options
|
|
150
|
+
* @returns The edit operations describing the formatting changes to the original document following the format described in {@linkcode EditResult}.
|
|
151
|
+
* To apply the edit operations to the input, use {@linkcode applyEdits}.
|
|
152
|
+
*/
|
|
153
|
+
function format(documentText, range, options) {
|
|
154
|
+
return formatter.format(documentText, range, options);
|
|
155
|
+
}
|
|
156
|
+
exports$1.format = format;
|
|
157
|
+
/**
|
|
158
|
+
* Computes the edit operations needed to modify a value in the JSON document.
|
|
159
|
+
*
|
|
160
|
+
* @param documentText The input text
|
|
161
|
+
* @param path The path of the value to change. The path represents either to the document root, a property or an array item.
|
|
162
|
+
* If the path points to an non-existing property or item, it will be created.
|
|
163
|
+
* @param value The new value for the specified property or item. If the value is undefined,
|
|
164
|
+
* the property or item will be removed.
|
|
165
|
+
* @param options Options
|
|
166
|
+
* @returns The edit operations describing the changes to the original document, following the format described in {@linkcode EditResult}.
|
|
167
|
+
* To apply the edit operations to the input, use {@linkcode applyEdits}.
|
|
168
|
+
*/
|
|
169
|
+
function modify(text, path, value, options) {
|
|
170
|
+
return edit.setProperty(text, path, value, options);
|
|
171
|
+
}
|
|
172
|
+
exports$1.modify = modify;
|
|
173
|
+
/**
|
|
174
|
+
* Applies edits to an input string.
|
|
175
|
+
* @param text The input text
|
|
176
|
+
* @param edits Edit operations following the format described in {@linkcode EditResult}.
|
|
177
|
+
* @returns The text with the applied edits.
|
|
178
|
+
* @throws An error if the edit operations are not well-formed as described in {@linkcode EditResult}.
|
|
179
|
+
*/
|
|
180
|
+
function applyEdits(text, edits) {
|
|
181
|
+
let sortedEdits = edits.slice(0).sort((a, b) => {
|
|
182
|
+
const diff = a.offset - b.offset;
|
|
183
|
+
if (diff === 0) return a.length - b.length;
|
|
184
|
+
return diff;
|
|
185
|
+
});
|
|
186
|
+
let lastModifiedOffset = text.length;
|
|
187
|
+
for (let i = sortedEdits.length - 1; i >= 0; i--) {
|
|
188
|
+
let e = sortedEdits[i];
|
|
189
|
+
if (e.offset + e.length <= lastModifiedOffset) text = edit.applyEdit(text, e);
|
|
190
|
+
else throw new Error("Overlapping edit");
|
|
191
|
+
lastModifiedOffset = e.offset;
|
|
192
|
+
}
|
|
193
|
+
return text;
|
|
194
|
+
}
|
|
195
|
+
exports$1.applyEdits = applyEdits;
|
|
196
|
+
});
|
|
197
|
+
})))();
|
|
198
|
+
function parseJsonObject(text, path) {
|
|
199
|
+
const errors = [];
|
|
200
|
+
const data = (0, import_main.parse)(text, errors, { allowTrailingComma: true });
|
|
201
|
+
if (errors.length > 0) {
|
|
202
|
+
const details = errors.map((e) => `${(0, import_main.printParseErrorCode)(e.error)} at offset ${e.offset}`).join("; ");
|
|
203
|
+
throw new Error(`Cannot parse ${path}: ${details}`);
|
|
204
|
+
}
|
|
205
|
+
if (typeof data !== "object" || data === null || Array.isArray(data)) throw new Error(`Cannot parse ${path}: expected a JSON object`);
|
|
206
|
+
return data;
|
|
207
|
+
}
|
|
208
|
+
function detectIndent(text) {
|
|
209
|
+
const firstProp = (0, import_main.parseTree)(text)?.children?.[0]?.children?.[0];
|
|
210
|
+
const lineStart = firstProp ? text.lastIndexOf("\n", firstProp.offset) + 1 : -1;
|
|
211
|
+
const leading = firstProp ? text.slice(lineStart, firstProp.offset) : "";
|
|
212
|
+
const indent = /^[ \t]+$/.test(leading) ? leading : " ";
|
|
213
|
+
return {
|
|
214
|
+
eol: text.includes("\r\n") ? "\r\n" : "\n",
|
|
215
|
+
insertSpaces: !indent.startsWith(" "),
|
|
216
|
+
tabSize: indent.startsWith(" ") ? 1 : indent.length
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
//#endregion
|
|
220
|
+
//#region ../internal/src/init-generator.ts
|
|
221
|
+
function readJson(tree, path) {
|
|
222
|
+
if (!tree.exists(path)) return null;
|
|
223
|
+
const text = tree.read(path, "utf8");
|
|
224
|
+
if (text == null) return null;
|
|
225
|
+
return parseJsonObject(text, path);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Registers `plugin` in nx.json as `{ plugin, options: {} }`, preserving
|
|
229
|
+
* JSONC comments and the file's own indentation. Dedupes string, tuple,
|
|
230
|
+
* and object plugin forms; creates nx.json when missing.
|
|
231
|
+
*/
|
|
232
|
+
function registerPlugin(tree, pluginPath) {
|
|
233
|
+
const text = tree.exists("nx.json") ? tree.read("nx.json", "utf8") : null;
|
|
234
|
+
const nxJson = text == null ? {} : parseJsonObject(text, "nx.json");
|
|
235
|
+
const plugins = Array.isArray(nxJson.plugins) ? nxJson.plugins : [];
|
|
236
|
+
if (plugins.some((entry) => typeof entry === "string" && entry === pluginPath || Array.isArray(entry) && entry[0] === pluginPath || typeof entry === "object" && entry !== null && !Array.isArray(entry) && entry.plugin === pluginPath)) return;
|
|
237
|
+
const entry = {
|
|
238
|
+
options: {},
|
|
239
|
+
plugin: pluginPath
|
|
240
|
+
};
|
|
241
|
+
if (text == null) {
|
|
242
|
+
tree.write("nx.json", `${JSON.stringify({ plugins: [entry] }, null, 2)}\n`);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const formattingOptions = detectIndent(text);
|
|
246
|
+
const updated = (0, import_main.applyEdits)(text, (0, import_main.modify)(text, Array.isArray(nxJson.plugins) ? ["plugins", plugins.length] : ["plugins"], Array.isArray(nxJson.plugins) ? entry : [entry], {
|
|
247
|
+
formattingOptions,
|
|
248
|
+
isArrayInsertion: true
|
|
249
|
+
}));
|
|
250
|
+
tree.write("nx.json", updated);
|
|
251
|
+
}
|
|
252
|
+
/** Nx names the root project from project.json, then nx.json, then package.json. */
|
|
253
|
+
function resolveRootProjectName(tree) {
|
|
254
|
+
for (const path of [
|
|
255
|
+
"project.json",
|
|
256
|
+
"nx.json",
|
|
257
|
+
"package.json"
|
|
258
|
+
]) {
|
|
259
|
+
const name = readJson(tree, path)?.name;
|
|
260
|
+
if (typeof name === "string" && name) return name;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
//#endregion
|
|
264
|
+
//#region src/generators/init/generator.ts
|
|
265
|
+
const DEFAULT_PLUGIN_PATH = "@nx-devkit/knip";
|
|
266
|
+
async function initGenerator(tree, options = {}) {
|
|
267
|
+
const pluginPath = options.pluginPath || DEFAULT_PLUGIN_PATH;
|
|
268
|
+
const projectName = resolveRootProjectName(tree) ?? "{root-project}";
|
|
269
|
+
registerPlugin(tree, pluginPath);
|
|
270
|
+
const checklist = [
|
|
271
|
+
`1. The plugin is registered (${pluginPath}) — projects containing a knip config now have knip targets.`,
|
|
272
|
+
"2. Inspect inferred targets:",
|
|
273
|
+
" bunx nx show projects",
|
|
274
|
+
`3. Run a target: bunx nx run ${projectName}:knip`
|
|
275
|
+
];
|
|
276
|
+
for (const line of checklist) console.log(line);
|
|
277
|
+
await formatFiles(tree);
|
|
278
|
+
return () => {};
|
|
279
|
+
}
|
|
280
|
+
//#endregion
|
|
281
|
+
export { initGenerator as default, initGenerator };
|
|
282
|
+
|
|
283
|
+
//# sourceMappingURL=generator.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.mjs","names":["exports"],"sources":["../../../../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/umd/main.js","../../../../internal/src/jsonc.ts","../../../../internal/src/init-generator.ts","../../../src/generators/init/generator.ts"],"sourcesContent":["(function (factory) {\n if (typeof module === \"object\" && typeof module.exports === \"object\") {\n var v = factory(require, exports);\n if (v !== undefined) module.exports = v;\n }\n else if (typeof define === \"function\" && define.amd) {\n define([\"require\", \"exports\", \"./impl/format\", \"./impl/edit\", \"./impl/scanner\", \"./impl/parser\"], factory);\n }\n})(function (require, exports) {\n /*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n 'use strict';\n Object.defineProperty(exports, \"__esModule\", { value: true });\n exports.applyEdits = exports.modify = exports.format = exports.printParseErrorCode = exports.ParseErrorCode = exports.stripComments = exports.visit = exports.getNodeValue = exports.getNodePath = exports.findNodeAtOffset = exports.findNodeAtLocation = exports.parseTree = exports.parse = exports.getLocation = exports.SyntaxKind = exports.ScanError = exports.createScanner = void 0;\n const formatter = require(\"./impl/format\");\n const edit = require(\"./impl/edit\");\n const scanner = require(\"./impl/scanner\");\n const parser = require(\"./impl/parser\");\n /**\n * Creates a JSON scanner on the given text.\n * If ignoreTrivia is set, whitespaces or comments are ignored.\n */\n exports.createScanner = scanner.createScanner;\n var ScanError;\n (function (ScanError) {\n ScanError[ScanError[\"None\"] = 0] = \"None\";\n ScanError[ScanError[\"UnexpectedEndOfComment\"] = 1] = \"UnexpectedEndOfComment\";\n ScanError[ScanError[\"UnexpectedEndOfString\"] = 2] = \"UnexpectedEndOfString\";\n ScanError[ScanError[\"UnexpectedEndOfNumber\"] = 3] = \"UnexpectedEndOfNumber\";\n ScanError[ScanError[\"InvalidUnicode\"] = 4] = \"InvalidUnicode\";\n ScanError[ScanError[\"InvalidEscapeCharacter\"] = 5] = \"InvalidEscapeCharacter\";\n ScanError[ScanError[\"InvalidCharacter\"] = 6] = \"InvalidCharacter\";\n })(ScanError || (exports.ScanError = ScanError = {}));\n var SyntaxKind;\n (function (SyntaxKind) {\n SyntaxKind[SyntaxKind[\"OpenBraceToken\"] = 1] = \"OpenBraceToken\";\n SyntaxKind[SyntaxKind[\"CloseBraceToken\"] = 2] = \"CloseBraceToken\";\n SyntaxKind[SyntaxKind[\"OpenBracketToken\"] = 3] = \"OpenBracketToken\";\n SyntaxKind[SyntaxKind[\"CloseBracketToken\"] = 4] = \"CloseBracketToken\";\n SyntaxKind[SyntaxKind[\"CommaToken\"] = 5] = \"CommaToken\";\n SyntaxKind[SyntaxKind[\"ColonToken\"] = 6] = \"ColonToken\";\n SyntaxKind[SyntaxKind[\"NullKeyword\"] = 7] = \"NullKeyword\";\n SyntaxKind[SyntaxKind[\"TrueKeyword\"] = 8] = \"TrueKeyword\";\n SyntaxKind[SyntaxKind[\"FalseKeyword\"] = 9] = \"FalseKeyword\";\n SyntaxKind[SyntaxKind[\"StringLiteral\"] = 10] = \"StringLiteral\";\n SyntaxKind[SyntaxKind[\"NumericLiteral\"] = 11] = \"NumericLiteral\";\n SyntaxKind[SyntaxKind[\"LineCommentTrivia\"] = 12] = \"LineCommentTrivia\";\n SyntaxKind[SyntaxKind[\"BlockCommentTrivia\"] = 13] = \"BlockCommentTrivia\";\n SyntaxKind[SyntaxKind[\"LineBreakTrivia\"] = 14] = \"LineBreakTrivia\";\n SyntaxKind[SyntaxKind[\"Trivia\"] = 15] = \"Trivia\";\n SyntaxKind[SyntaxKind[\"Unknown\"] = 16] = \"Unknown\";\n SyntaxKind[SyntaxKind[\"EOF\"] = 17] = \"EOF\";\n })(SyntaxKind || (exports.SyntaxKind = SyntaxKind = {}));\n /**\n * For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.\n */\n exports.getLocation = parser.getLocation;\n /**\n * Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.\n * Therefore, always check the errors list to find out if the input was valid.\n */\n exports.parse = parser.parse;\n /**\n * Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.\n */\n exports.parseTree = parser.parseTree;\n /**\n * Finds the node at the given path in a JSON DOM.\n */\n exports.findNodeAtLocation = parser.findNodeAtLocation;\n /**\n * Finds the innermost node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.\n */\n exports.findNodeAtOffset = parser.findNodeAtOffset;\n /**\n * Gets the JSON path of the given JSON DOM node\n */\n exports.getNodePath = parser.getNodePath;\n /**\n * Evaluates the JavaScript object of the given JSON DOM node\n */\n exports.getNodeValue = parser.getNodeValue;\n /**\n * Parses the given text and invokes the visitor functions for each object, array and literal reached.\n */\n exports.visit = parser.visit;\n /**\n * Takes JSON with JavaScript-style comments and remove\n * them. Optionally replaces every none-newline character\n * of comments with a replaceCharacter\n */\n exports.stripComments = parser.stripComments;\n var ParseErrorCode;\n (function (ParseErrorCode) {\n ParseErrorCode[ParseErrorCode[\"InvalidSymbol\"] = 1] = \"InvalidSymbol\";\n ParseErrorCode[ParseErrorCode[\"InvalidNumberFormat\"] = 2] = \"InvalidNumberFormat\";\n ParseErrorCode[ParseErrorCode[\"PropertyNameExpected\"] = 3] = \"PropertyNameExpected\";\n ParseErrorCode[ParseErrorCode[\"ValueExpected\"] = 4] = \"ValueExpected\";\n ParseErrorCode[ParseErrorCode[\"ColonExpected\"] = 5] = \"ColonExpected\";\n ParseErrorCode[ParseErrorCode[\"CommaExpected\"] = 6] = \"CommaExpected\";\n ParseErrorCode[ParseErrorCode[\"CloseBraceExpected\"] = 7] = \"CloseBraceExpected\";\n ParseErrorCode[ParseErrorCode[\"CloseBracketExpected\"] = 8] = \"CloseBracketExpected\";\n ParseErrorCode[ParseErrorCode[\"EndOfFileExpected\"] = 9] = \"EndOfFileExpected\";\n ParseErrorCode[ParseErrorCode[\"InvalidCommentToken\"] = 10] = \"InvalidCommentToken\";\n ParseErrorCode[ParseErrorCode[\"UnexpectedEndOfComment\"] = 11] = \"UnexpectedEndOfComment\";\n ParseErrorCode[ParseErrorCode[\"UnexpectedEndOfString\"] = 12] = \"UnexpectedEndOfString\";\n ParseErrorCode[ParseErrorCode[\"UnexpectedEndOfNumber\"] = 13] = \"UnexpectedEndOfNumber\";\n ParseErrorCode[ParseErrorCode[\"InvalidUnicode\"] = 14] = \"InvalidUnicode\";\n ParseErrorCode[ParseErrorCode[\"InvalidEscapeCharacter\"] = 15] = \"InvalidEscapeCharacter\";\n ParseErrorCode[ParseErrorCode[\"InvalidCharacter\"] = 16] = \"InvalidCharacter\";\n })(ParseErrorCode || (exports.ParseErrorCode = ParseErrorCode = {}));\n function printParseErrorCode(code) {\n switch (code) {\n case 1 /* ParseErrorCode.InvalidSymbol */: return 'InvalidSymbol';\n case 2 /* ParseErrorCode.InvalidNumberFormat */: return 'InvalidNumberFormat';\n case 3 /* ParseErrorCode.PropertyNameExpected */: return 'PropertyNameExpected';\n case 4 /* ParseErrorCode.ValueExpected */: return 'ValueExpected';\n case 5 /* ParseErrorCode.ColonExpected */: return 'ColonExpected';\n case 6 /* ParseErrorCode.CommaExpected */: return 'CommaExpected';\n case 7 /* ParseErrorCode.CloseBraceExpected */: return 'CloseBraceExpected';\n case 8 /* ParseErrorCode.CloseBracketExpected */: return 'CloseBracketExpected';\n case 9 /* ParseErrorCode.EndOfFileExpected */: return 'EndOfFileExpected';\n case 10 /* ParseErrorCode.InvalidCommentToken */: return 'InvalidCommentToken';\n case 11 /* ParseErrorCode.UnexpectedEndOfComment */: return 'UnexpectedEndOfComment';\n case 12 /* ParseErrorCode.UnexpectedEndOfString */: return 'UnexpectedEndOfString';\n case 13 /* ParseErrorCode.UnexpectedEndOfNumber */: return 'UnexpectedEndOfNumber';\n case 14 /* ParseErrorCode.InvalidUnicode */: return 'InvalidUnicode';\n case 15 /* ParseErrorCode.InvalidEscapeCharacter */: return 'InvalidEscapeCharacter';\n case 16 /* ParseErrorCode.InvalidCharacter */: return 'InvalidCharacter';\n }\n return '<unknown ParseErrorCode>';\n }\n exports.printParseErrorCode = printParseErrorCode;\n /**\n * Computes the edit operations needed to format a JSON document.\n *\n * @param documentText The input text\n * @param range The range to format or `undefined` to format the full content\n * @param options The formatting options\n * @returns The edit operations describing the formatting changes to the original document following the format described in {@linkcode EditResult}.\n * To apply the edit operations to the input, use {@linkcode applyEdits}.\n */\n function format(documentText, range, options) {\n return formatter.format(documentText, range, options);\n }\n exports.format = format;\n /**\n * Computes the edit operations needed to modify a value in the JSON document.\n *\n * @param documentText The input text\n * @param path The path of the value to change. The path represents either to the document root, a property or an array item.\n * If the path points to an non-existing property or item, it will be created.\n * @param value The new value for the specified property or item. If the value is undefined,\n * the property or item will be removed.\n * @param options Options\n * @returns The edit operations describing the changes to the original document, following the format described in {@linkcode EditResult}.\n * To apply the edit operations to the input, use {@linkcode applyEdits}.\n */\n function modify(text, path, value, options) {\n return edit.setProperty(text, path, value, options);\n }\n exports.modify = modify;\n /**\n * Applies edits to an input string.\n * @param text The input text\n * @param edits Edit operations following the format described in {@linkcode EditResult}.\n * @returns The text with the applied edits.\n * @throws An error if the edit operations are not well-formed as described in {@linkcode EditResult}.\n */\n function applyEdits(text, edits) {\n let sortedEdits = edits.slice(0).sort((a, b) => {\n const diff = a.offset - b.offset;\n if (diff === 0) {\n return a.length - b.length;\n }\n return diff;\n });\n let lastModifiedOffset = text.length;\n for (let i = sortedEdits.length - 1; i >= 0; i--) {\n let e = sortedEdits[i];\n if (e.offset + e.length <= lastModifiedOffset) {\n text = edit.applyEdit(text, e);\n }\n else {\n throw new Error('Overlapping edit');\n }\n lastModifiedOffset = e.offset;\n }\n return text;\n }\n exports.applyEdits = applyEdits;\n});\n","import { parse, type ParseError, parseTree, printParseErrorCode } from 'jsonc-parser'\n\nexport function parseJsonObject(text: string, path: string): Record<string, unknown> {\n const errors: ParseError[] = []\n const data = parse(text, errors, { allowTrailingComma: true }) as unknown\n if (errors.length > 0) {\n const details = errors\n .map((e) => `${printParseErrorCode(e.error)} at offset ${e.offset}`)\n .join('; ')\n throw new Error(`Cannot parse ${path}: ${details}`)\n }\n if (typeof data !== 'object' || data === null || Array.isArray(data)) {\n throw new Error(`Cannot parse ${path}: expected a JSON object`)\n }\n return data as Record<string, unknown>\n}\n\nexport function detectIndent(text: string): {\n eol: string\n insertSpaces: boolean\n tabSize: number\n} {\n // Anchor on the first real property of the root object so quoted lines\n // Inside comments cannot skew the detected indentation.\n const root = parseTree(text)\n const firstProp = root?.children?.[0]?.children?.[0]\n const lineStart = firstProp ? text.lastIndexOf('\\n', firstProp.offset) + 1 : -1\n const leading = firstProp ? text.slice(lineStart, firstProp.offset) : ''\n const indent = /^[ \\t]+$/.test(leading) ? leading : ' '\n return {\n eol: text.includes('\\r\\n') ? '\\r\\n' : '\\n',\n insertSpaces: !indent.startsWith('\\t'),\n tabSize: indent.startsWith('\\t') ? 1 : indent.length,\n }\n}\n","import type { Tree } from '@nx/devkit'\nimport { applyEdits, modify } from 'jsonc-parser'\nimport { detectIndent, parseJsonObject } from './jsonc.ts'\n\nexport function readJson(tree: Tree, path: string): Record<string, unknown> | null {\n if (!tree.exists(path)) {\n return null\n }\n const text = tree.read(path, 'utf8')\n if (text == null) {\n return null\n }\n return parseJsonObject(text, path)\n}\n\n/**\n * Registers `plugin` in nx.json as `{ plugin, options: {} }`, preserving\n * JSONC comments and the file's own indentation. Dedupes string, tuple,\n * and object plugin forms; creates nx.json when missing.\n */\nexport function registerPlugin(tree: Tree, pluginPath: string): void {\n const text = tree.exists('nx.json') ? tree.read('nx.json', 'utf8') : null\n const nxJson = text == null ? {} : parseJsonObject(text, 'nx.json')\n const plugins = Array.isArray(nxJson.plugins) ? (nxJson.plugins as unknown[]) : []\n const alreadyRegistered = plugins.some(\n (entry) =>\n (typeof entry === 'string' && entry === pluginPath) ||\n (Array.isArray(entry) && entry[0] === pluginPath) ||\n (typeof entry === 'object' &&\n entry !== null &&\n !Array.isArray(entry) &&\n (entry as { plugin?: string }).plugin === pluginPath),\n )\n if (alreadyRegistered) {\n return\n }\n\n const entry = { options: {}, plugin: pluginPath }\n if (text == null) {\n tree.write('nx.json', `${JSON.stringify({ plugins: [entry] }, null, 2)}\\n`)\n return\n }\n const formattingOptions = detectIndent(text)\n const updated = applyEdits(\n text,\n modify(\n text,\n Array.isArray(nxJson.plugins) ? ['plugins', plugins.length] : ['plugins'],\n Array.isArray(nxJson.plugins) ? entry : [entry],\n { formattingOptions, isArrayInsertion: true },\n ),\n )\n tree.write('nx.json', updated)\n}\n\n/** Nx names the root project from project.json, then nx.json, then package.json. */\nexport function resolveRootProjectName(tree: Tree): string | undefined {\n for (const path of ['project.json', 'nx.json', 'package.json']) {\n const name = readJson(tree, path)?.name\n if (typeof name === 'string' && name) {\n return name\n }\n }\n return undefined\n}\n","import { formatFiles, type GeneratorCallback, type Tree } from '@nx/devkit'\nimport { registerPlugin, resolveRootProjectName } from '@nx-devkit/internal'\n\nexport interface NxKnipInitOptions {\n pluginPath?: string\n}\n\nconst DEFAULT_PLUGIN_PATH = '@nx-devkit/knip'\n\nexport async function initGenerator(\n tree: Tree,\n options: NxKnipInitOptions = {},\n): Promise<GeneratorCallback> {\n const pluginPath = options.pluginPath || DEFAULT_PLUGIN_PATH\n const projectName = resolveRootProjectName(tree) ?? '{root-project}'\n\n registerPlugin(tree, pluginPath)\n\n const checklist = [\n `1. The plugin is registered (${pluginPath}) — projects containing a knip config now have knip targets.`,\n '2. Inspect inferred targets:',\n ' bunx nx show projects',\n `3. Run a target: bunx nx run ${projectName}:knip`,\n ]\n for (const line of checklist) {\n console.log(line)\n }\n await formatFiles(tree)\n\n return () => {\n /* No-op */\n }\n}\n\nexport default initGenerator\n"],"x_google_ignoreList":[0],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { CreateNodesV2 } from "@nx/devkit";
|
|
2
|
+
|
|
3
|
+
//#region src/plugin.d.ts
|
|
4
|
+
interface NxKnipPluginOptions {
|
|
5
|
+
/** Name of the knip target. Default: "knip". */
|
|
6
|
+
targetName?: string;
|
|
7
|
+
/** Also infer a `<targetName>:fix` target (`knip --fix`). Default: true. */
|
|
8
|
+
fixTarget?: boolean;
|
|
9
|
+
}
|
|
10
|
+
declare function configRank(fileName: string): number;
|
|
11
|
+
declare function isKnipConfig(fileName: string): boolean;
|
|
12
|
+
declare function hasKnipKey(absPath: string): boolean;
|
|
13
|
+
declare const createNodesV2: CreateNodesV2<NxKnipPluginOptions>;
|
|
14
|
+
declare const __testing: {
|
|
15
|
+
KNIP_GLOB: string;
|
|
16
|
+
configRank: typeof configRank;
|
|
17
|
+
hasKnipKey: typeof hasKnipKey;
|
|
18
|
+
isKnipConfig: typeof isKnipConfig;
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
export { NxKnipPluginOptions, __testing, createNodesV2, createNodesV2 as default };
|
|
22
|
+
//# sourceMappingURL=plugin.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.mts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;UAIiB,mBAAA;;EAEf,UAAA;EAFkC;EAIlC,SAAS;AAAA;AAAA,iBAoBF,UAAA,CAAW,QAAgB;AAAA,iBAK3B,YAAA,CAAa,QAAgB;AAAA,iBAI7B,UAAA,CAAW,OAAe;AAAA,cAgBtB,aAAA,EAAe,aAAa,CAAC,mBAAA;AAAA,cAyD7B,SAAA"}
|
package/dist/plugin.mjs
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, relative, resolve } from "node:path";
|
|
3
|
+
//#region src/plugin.ts
|
|
4
|
+
const KNIP_GLOB = "**/{knip.json,knip.jsonc,knip.ts,knip.config.ts,knip.config.js,knip.config.mjs,knip.config.cjs,package.json}";
|
|
5
|
+
const PRECEDENCE = [
|
|
6
|
+
"knip.json",
|
|
7
|
+
"knip.jsonc",
|
|
8
|
+
"knip.ts",
|
|
9
|
+
"knip.config.ts",
|
|
10
|
+
"knip.config.js",
|
|
11
|
+
"knip.config.mjs",
|
|
12
|
+
"knip.config.cjs",
|
|
13
|
+
"package.json"
|
|
14
|
+
];
|
|
15
|
+
function configRank(fileName) {
|
|
16
|
+
const idx = PRECEDENCE.indexOf(fileName);
|
|
17
|
+
return idx === -1 ? PRECEDENCE.length : idx;
|
|
18
|
+
}
|
|
19
|
+
function isKnipConfig(fileName) {
|
|
20
|
+
return configRank(fileName) < PRECEDENCE.length - 1;
|
|
21
|
+
}
|
|
22
|
+
function hasKnipKey(absPath) {
|
|
23
|
+
try {
|
|
24
|
+
const pkg = JSON.parse(readFileSync(absPath, "utf8"));
|
|
25
|
+
return typeof pkg === "object" && pkg !== null && "knip" in pkg;
|
|
26
|
+
} catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function shouldSkipDir(projectRoot, workspaceRoot) {
|
|
31
|
+
const rel = relative(workspaceRoot, resolve(workspaceRoot, projectRoot || "."));
|
|
32
|
+
if (rel === "") return false;
|
|
33
|
+
const segments = rel.split(/[\\/]/);
|
|
34
|
+
return segments.includes("..") || segments.includes("node_modules");
|
|
35
|
+
}
|
|
36
|
+
const createNodesV2 = [KNIP_GLOB, (configFiles, options = {}, context) => {
|
|
37
|
+
const workspaceRoot = context.workspaceRoot;
|
|
38
|
+
const targetName = options.targetName ?? "knip";
|
|
39
|
+
const fixTarget = options.fixTarget ?? true;
|
|
40
|
+
const byDir = /* @__PURE__ */ new Map();
|
|
41
|
+
for (const configFile of [...configFiles].sort()) {
|
|
42
|
+
const dir = dirname(configFile).replace(/\\/g, "/");
|
|
43
|
+
const key = dir === "." ? "" : dir;
|
|
44
|
+
const list = byDir.get(key) ?? [];
|
|
45
|
+
list.push(configFile);
|
|
46
|
+
byDir.set(key, list);
|
|
47
|
+
}
|
|
48
|
+
const results = [];
|
|
49
|
+
for (const [dir, files] of [...byDir.entries()].sort(([a], [b]) => a.localeCompare(b))) {
|
|
50
|
+
if (shouldSkipDir(dir, workspaceRoot)) continue;
|
|
51
|
+
const sorted = files.toSorted((a, b) => configRank(a.split("/").pop() ?? "") - configRank(b.split("/").pop() ?? ""));
|
|
52
|
+
const winner = sorted.at(0);
|
|
53
|
+
if (winner === void 0) continue;
|
|
54
|
+
if ((winner.split("/").pop() ?? "") === "package.json" && !hasKnipKey(resolve(workspaceRoot, winner))) continue;
|
|
55
|
+
const prefix = dir ? "{projectRoot}" : "{workspaceRoot}";
|
|
56
|
+
const configInputs = sorted.map((f) => `${prefix}/${dir ? f.slice(dir.length + 1) : f}`);
|
|
57
|
+
const targets = { [targetName]: {
|
|
58
|
+
executor: "nx:run-commands",
|
|
59
|
+
cache: true,
|
|
60
|
+
inputs: [`${prefix}/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,json,jsonc}`, ...configInputs],
|
|
61
|
+
options: {
|
|
62
|
+
command: "npx knip",
|
|
63
|
+
cwd: "{projectRoot}"
|
|
64
|
+
}
|
|
65
|
+
} };
|
|
66
|
+
if (fixTarget) targets[`${targetName}:fix`] = {
|
|
67
|
+
executor: "nx:run-commands",
|
|
68
|
+
cache: false,
|
|
69
|
+
options: {
|
|
70
|
+
command: "npx knip --fix",
|
|
71
|
+
cwd: "{projectRoot}"
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
results.push([winner, { projects: { [dir]: {
|
|
75
|
+
root: dir,
|
|
76
|
+
targets
|
|
77
|
+
} } }]);
|
|
78
|
+
}
|
|
79
|
+
return results;
|
|
80
|
+
}];
|
|
81
|
+
const __testing = {
|
|
82
|
+
KNIP_GLOB,
|
|
83
|
+
configRank,
|
|
84
|
+
hasKnipKey,
|
|
85
|
+
isKnipConfig
|
|
86
|
+
};
|
|
87
|
+
//#endregion
|
|
88
|
+
export { __testing, createNodesV2, createNodesV2 as default };
|
|
89
|
+
|
|
90
|
+
//# sourceMappingURL=plugin.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.mjs","names":[],"sources":["../src/plugin.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { dirname, relative, resolve } from 'node:path'\nimport type { CreateNodesV2, ProjectConfiguration, TargetConfiguration } from '@nx/devkit'\n\nexport interface NxKnipPluginOptions {\n /** Name of the knip target. Default: \"knip\". */\n targetName?: string\n /** Also infer a `<targetName>:fix` target (`knip --fix`). Default: true. */\n fixTarget?: boolean\n}\n\n// Standalone knip configs plus package.json carrying a \"knip\" key — knip auto-discovers all of them when run from the owning directory.\nconst KNIP_GLOB =\n '**/{knip.json,knip.jsonc,knip.ts,knip.config.ts,knip.config.js,knip.config.mjs,knip.config.cjs,package.json}'\n\n// Deterministic precedence when several config forms live in one directory —\n// Knip.json wins over everything else (matches knip's own discovery order).\nconst PRECEDENCE = [\n 'knip.json',\n 'knip.jsonc',\n 'knip.ts',\n 'knip.config.ts',\n 'knip.config.js',\n 'knip.config.mjs',\n 'knip.config.cjs',\n 'package.json',\n]\n\nfunction configRank(fileName: string): number {\n const idx = PRECEDENCE.indexOf(fileName)\n return idx === -1 ? PRECEDENCE.length : idx\n}\n\nfunction isKnipConfig(fileName: string): boolean {\n return configRank(fileName) < PRECEDENCE.length - 1\n}\n\nfunction hasKnipKey(absPath: string): boolean {\n try {\n const pkg = JSON.parse(readFileSync(absPath, 'utf8'))\n return typeof pkg === 'object' && pkg !== null && 'knip' in pkg\n } catch {\n return false\n }\n}\n\nfunction shouldSkipDir(projectRoot: string, workspaceRoot: string): boolean {\n const rel = relative(workspaceRoot, resolve(workspaceRoot, projectRoot || '.'))\n if (rel === '') return false\n const segments = rel.split(/[\\\\/]/)\n return segments.includes('..') || segments.includes('node_modules')\n}\n\nexport const createNodesV2: CreateNodesV2<NxKnipPluginOptions> = [\n KNIP_GLOB,\n (configFiles, options = {}, context) => {\n const workspaceRoot = context.workspaceRoot\n const targetName = options.targetName ?? 'knip'\n const fixTarget = options.fixTarget ?? true\n\n // One target pair per owning directory — the highest-precedence config wins.\n const byDir = new Map<string, string[]>()\n for (const configFile of [...configFiles].sort()) {\n const dir = dirname(configFile).replace(/\\\\/g, '/')\n const key = dir === '.' ? '' : dir\n const list = byDir.get(key) ?? []\n list.push(configFile)\n byDir.set(key, list)\n }\n\n const results: Array<readonly [string, { projects: Record<string, ProjectConfiguration> }]> = []\n for (const [dir, files] of [...byDir.entries()].sort(([a], [b]) => a.localeCompare(b))) {\n if (shouldSkipDir(dir, workspaceRoot)) continue\n\n const sorted = files.toSorted(\n (a, b) => configRank(a.split('/').pop() ?? '') - configRank(b.split('/').pop() ?? ''),\n )\n const winner = sorted.at(0)\n if (winner === undefined) continue\n const winnerName = winner.split('/').pop() ?? ''\n if (winnerName === 'package.json' && !hasKnipKey(resolve(workspaceRoot, winner))) continue\n\n const prefix = dir ? '{projectRoot}' : '{workspaceRoot}'\n // Every knip-bearing file in the dir must bust the cache — paths are relative to the project root (\"\" for the root project).\n const configInputs = sorted.map((f) => `${prefix}/${dir ? f.slice(dir.length + 1) : f}`)\n\n const targets: Record<string, TargetConfiguration> = {\n [targetName]: {\n executor: 'nx:run-commands',\n cache: true,\n inputs: [`${prefix}/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,json,jsonc}`, ...configInputs],\n options: { command: 'npx knip', cwd: '{projectRoot}' },\n },\n }\n if (fixTarget) {\n targets[`${targetName}:fix`] = {\n executor: 'nx:run-commands',\n cache: false,\n options: { command: 'npx knip --fix', cwd: '{projectRoot}' },\n }\n }\n\n results.push([winner, { projects: { [dir]: { root: dir, targets } } }])\n }\n return results\n },\n]\n\nexport default createNodesV2\n\nexport const __testing = { KNIP_GLOB, configRank, hasKnipKey, isKnipConfig }\n"],"mappings":";;;AAYA,MAAM,YACJ;AAIF,MAAM,aAAa;CACjB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAS,WAAW,UAA0B;CAC5C,MAAM,MAAM,WAAW,QAAQ,QAAQ;CACvC,OAAO,QAAQ,KAAK,WAAW,SAAS;AAC1C;AAEA,SAAS,aAAa,UAA2B;CAC/C,OAAO,WAAW,QAAQ,IAAI,WAAW,SAAS;AACpD;AAEA,SAAS,WAAW,SAA0B;CAC5C,IAAI;EACF,MAAM,MAAM,KAAK,MAAM,aAAa,SAAS,MAAM,CAAC;EACpD,OAAO,OAAO,QAAQ,YAAY,QAAQ,QAAQ,UAAU;CAC9D,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,cAAc,aAAqB,eAAgC;CAC1E,MAAM,MAAM,SAAS,eAAe,QAAQ,eAAe,eAAe,GAAG,CAAC;CAC9E,IAAI,QAAQ,IAAI,OAAO;CACvB,MAAM,WAAW,IAAI,MAAM,OAAO;CAClC,OAAO,SAAS,SAAS,IAAI,KAAK,SAAS,SAAS,cAAc;AACpE;AAEA,MAAa,gBAAoD,CAC/D,YACC,aAAa,UAAU,CAAC,GAAG,YAAY;CACtC,MAAM,gBAAgB,QAAQ;CAC9B,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,YAAY,QAAQ,aAAa;CAGvC,MAAM,wBAAQ,IAAI,IAAsB;CACxC,KAAK,MAAM,cAAc,CAAC,GAAG,WAAW,CAAC,CAAC,KAAK,GAAG;EAChD,MAAM,MAAM,QAAQ,UAAU,CAAC,CAAC,QAAQ,OAAO,GAAG;EAClD,MAAM,MAAM,QAAQ,MAAM,KAAK;EAC/B,MAAM,OAAO,MAAM,IAAI,GAAG,KAAK,CAAC;EAChC,KAAK,KAAK,UAAU;EACpB,MAAM,IAAI,KAAK,IAAI;CACrB;CAEA,MAAM,UAAwF,CAAC;CAC/F,KAAK,MAAM,CAAC,KAAK,UAAU,CAAC,GAAG,MAAM,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,GAAG;EACtF,IAAI,cAAc,KAAK,aAAa,GAAG;EAEvC,MAAM,SAAS,MAAM,UAClB,GAAG,MAAM,WAAW,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK,EAAE,IAAI,WAAW,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK,EAAE,CACtF;EACA,MAAM,SAAS,OAAO,GAAG,CAAC;EAC1B,IAAI,WAAW,KAAA,GAAW;EAE1B,KADmB,OAAO,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK,QAC3B,kBAAkB,CAAC,WAAW,QAAQ,eAAe,MAAM,CAAC,GAAG;EAElF,MAAM,SAAS,MAAM,kBAAkB;EAEvC,MAAM,eAAe,OAAO,KAAK,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,MAAM,IAAI,SAAS,CAAC,IAAI,GAAG;EAEvF,MAAM,UAA+C,GAClD,aAAa;GACZ,UAAU;GACV,OAAO;GACP,QAAQ,CAAC,GAAG,OAAO,mDAAmD,GAAG,YAAY;GACrF,SAAS;IAAE,SAAS;IAAY,KAAK;GAAgB;EACvD,EACF;EACA,IAAI,WACF,QAAQ,GAAG,WAAW,SAAS;GAC7B,UAAU;GACV,OAAO;GACP,SAAS;IAAE,SAAS;IAAkB,KAAK;GAAgB;EAC7D;EAGF,QAAQ,KAAK,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM;GAAE,MAAM;GAAK;EAAQ,EAAE,EAAE,CAAC,CAAC;CACxE;CACA,OAAO;AACT,CACF;AAIA,MAAa,YAAY;CAAE;CAAW;CAAY;CAAY;AAAa"}
|
package/generators.json
ADDED
package/package.json
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
|
|
4
|
-
},
|
|
5
|
-
"description": "Placeholder for @nx-devkit/knip published by @nx-devkit/prepare-for-release.",
|
|
6
|
-
"homepage": "https://github.com/nx-devkit/nx.ts/tree/main/packages/knip",
|
|
2
|
+
"name": "@nx-devkit/knip",
|
|
3
|
+
"version": "0.0.1",
|
|
7
4
|
"license": "MIT",
|
|
5
|
+
"description": "Nx plugin: knip config files infer a cached knip target — dead exports/files/dependencies detection without project.json.",
|
|
8
6
|
"keywords": [
|
|
9
7
|
"nx",
|
|
10
8
|
"nx-plugin",
|
|
@@ -15,16 +13,54 @@
|
|
|
15
13
|
"monorepo",
|
|
16
14
|
"zero-config"
|
|
17
15
|
],
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
"private": false,
|
|
17
|
+
"type": "module",
|
|
18
|
+
"main": "./dist/plugin.mjs",
|
|
19
|
+
"module": "./dist/plugin.mjs",
|
|
20
|
+
"types": "./dist/plugin.d.mts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/plugin.d.mts",
|
|
24
|
+
"default": "./dist/plugin.mjs"
|
|
25
|
+
},
|
|
26
|
+
"./package.json": "./package.json",
|
|
27
|
+
"./generators/init": {
|
|
28
|
+
"types": "./dist/generators/init/generator.d.mts",
|
|
29
|
+
"default": "./dist/generators/init/generator.mjs"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"generators.json",
|
|
35
|
+
"src/generators/init/schema.json",
|
|
36
|
+
"README.md"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsdown",
|
|
40
|
+
"test": "vitest run"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"knip": "^5.0.0 || ^6.0.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@nx-devkit/internal": "0.0.0",
|
|
47
|
+
"@types/node": "^26.0.0",
|
|
48
|
+
"knip": "^6.0.0",
|
|
49
|
+
"tsdown": "^0.22.3",
|
|
50
|
+
"typescript": "^6.0.3",
|
|
51
|
+
"vitest": "^4.1.11"
|
|
22
52
|
},
|
|
23
53
|
"repository": {
|
|
24
54
|
"type": "git",
|
|
25
55
|
"url": "https://github.com/nx-devkit/nx.ts",
|
|
26
56
|
"directory": "packages/knip"
|
|
27
57
|
},
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
}
|
|
58
|
+
"bugs": {
|
|
59
|
+
"url": "https://github.com/nx-devkit/nx.ts/issues"
|
|
60
|
+
},
|
|
61
|
+
"homepage": "https://github.com/nx-devkit/nx.ts/tree/main/packages/knip",
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"@nx/devkit": "^22.0.0 || ^23.0.0"
|
|
64
|
+
},
|
|
65
|
+
"generators": "./generators.json"
|
|
66
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"title": "Init generator",
|
|
5
|
+
"description": "Registers @nx-devkit/knip in nx.json plugins.",
|
|
6
|
+
"properties": {
|
|
7
|
+
"pluginPath": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"default": "@nx-devkit/knip",
|
|
10
|
+
"minLength": 1,
|
|
11
|
+
"description": "Plugin specifier to register (e.g. a local path during development)."
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"additionalProperties": false
|
|
15
|
+
}
|