@nx/devkit 23.0.0-canary.20260504-e8aa612 → 23.0.0-pr.33655.05d9542
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.
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEVKIT_INTERNAL_SYMBOLS = void 0;
|
|
4
|
+
exports.default = updateDevkitDeepImports;
|
|
5
|
+
exports.rewriteDevkitDeepImports = rewriteDevkitDeepImports;
|
|
6
|
+
const devkit_exports_1 = require("nx/src/devkit-exports");
|
|
7
|
+
const format_files_1 = require("../../generators/format-files");
|
|
8
|
+
const visit_not_ignored_files_1 = require("../../generators/visit-not-ignored-files");
|
|
9
|
+
const package_json_1 = require("../../utils/package-json");
|
|
10
|
+
const string_change_1 = require("../../utils/string-change");
|
|
11
|
+
const versions_1 = require("../../utils/versions");
|
|
12
|
+
const TS_EXTENSIONS = ['.ts', '.tsx', '.cts', '.mts'];
|
|
13
|
+
const DEEP_IMPORT_PREFIX = '@nx/devkit/src/';
|
|
14
|
+
const PUBLIC_SPECIFIER = '@nx/devkit';
|
|
15
|
+
const INTERNAL_SPECIFIER = '@nx/devkit/internal';
|
|
16
|
+
// Names re-exported from `@nx/devkit/internal` (see packages/devkit/internal.ts
|
|
17
|
+
// at the time this migration was authored). Anything imported from a
|
|
18
|
+
// `@nx/devkit/src/...` path whose name is NOT in this set is assumed to be
|
|
19
|
+
// part of the stable public `@nx/devkit` API.
|
|
20
|
+
exports.DEVKIT_INTERNAL_SYMBOLS = new Set([
|
|
21
|
+
'signalToCode',
|
|
22
|
+
'createProjectRootMappingsFromProjectConfigurations',
|
|
23
|
+
'PluginCache',
|
|
24
|
+
'safeWriteFileCache',
|
|
25
|
+
'determineArtifactNameAndDirectoryOptions',
|
|
26
|
+
'getRelativeCwd',
|
|
27
|
+
'FileExtensionType',
|
|
28
|
+
'getE2EWebServerInfo',
|
|
29
|
+
'E2EWebServerDetails',
|
|
30
|
+
'forEachExecutorOptions',
|
|
31
|
+
'AggregatedLog',
|
|
32
|
+
'migrateProjectExecutorsToPlugin',
|
|
33
|
+
'migrateProjectExecutorsToPluginV1',
|
|
34
|
+
'NoTargetsToMigrateError',
|
|
35
|
+
'InferredTargetConfiguration',
|
|
36
|
+
'processTargetOutputs',
|
|
37
|
+
'deleteMatchingProperties',
|
|
38
|
+
'toProjectRelativePath',
|
|
39
|
+
'determineProjectNameAndRootOptions',
|
|
40
|
+
'ensureRootProjectName',
|
|
41
|
+
'resolveImportPath',
|
|
42
|
+
'promptWhenInteractive',
|
|
43
|
+
'addBuildTargetDefaults',
|
|
44
|
+
'addE2eCiTargetDefaults',
|
|
45
|
+
'addPlugin',
|
|
46
|
+
'createAsyncIterable',
|
|
47
|
+
'combineAsyncIterables',
|
|
48
|
+
'mapAsyncIterable',
|
|
49
|
+
'calculateHashForCreateNodes',
|
|
50
|
+
'calculateHashesForCreateNodes',
|
|
51
|
+
'getCatalogManager',
|
|
52
|
+
'loadConfigFile',
|
|
53
|
+
'clearRequireCache',
|
|
54
|
+
'findPluginForConfigFile',
|
|
55
|
+
'getNamedInputs',
|
|
56
|
+
'logShowProjectCommand',
|
|
57
|
+
'eachValueFrom',
|
|
58
|
+
'checkAndCleanWithSemver',
|
|
59
|
+
'camelize',
|
|
60
|
+
'capitalize',
|
|
61
|
+
'classify',
|
|
62
|
+
'dasherize',
|
|
63
|
+
]);
|
|
64
|
+
const FALLBACK_RE = /(['"])@nx\/devkit\/src\/[^'"\n]+?\1/g;
|
|
65
|
+
let ts;
|
|
66
|
+
async function updateDevkitDeepImports(tree) {
|
|
67
|
+
let touchedCount = 0;
|
|
68
|
+
(0, visit_not_ignored_files_1.visitNotIgnoredFiles)(tree, '.', (filePath) => {
|
|
69
|
+
if (!TS_EXTENSIONS.some((ext) => filePath.endsWith(ext))) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const original = tree.read(filePath, 'utf-8');
|
|
73
|
+
if (!original || !original.includes(DEEP_IMPORT_PREFIX)) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const updated = rewriteDevkitDeepImports(original);
|
|
77
|
+
if (updated !== original) {
|
|
78
|
+
tree.write(filePath, updated);
|
|
79
|
+
touchedCount += 1;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
if (touchedCount > 0) {
|
|
83
|
+
devkit_exports_1.logger.info(`Rewrote @nx/devkit deep imports in ${touchedCount} file(s).`);
|
|
84
|
+
}
|
|
85
|
+
await (0, format_files_1.formatFiles)(tree);
|
|
86
|
+
}
|
|
87
|
+
function rewriteDevkitDeepImports(source) {
|
|
88
|
+
ts ??= (0, package_json_1.ensurePackage)('typescript', versions_1.typescriptVersion);
|
|
89
|
+
const sourceFile = ts.createSourceFile('tmp.ts', source, ts.ScriptTarget.Latest,
|
|
90
|
+
/* setParentNodes */ true, ts.ScriptKind.TSX);
|
|
91
|
+
const changes = [];
|
|
92
|
+
for (const stmt of sourceFile.statements) {
|
|
93
|
+
if (!ts.isImportDeclaration(stmt))
|
|
94
|
+
continue;
|
|
95
|
+
if (!ts.isStringLiteral(stmt.moduleSpecifier))
|
|
96
|
+
continue;
|
|
97
|
+
if (!stmt.moduleSpecifier.text.startsWith(DEEP_IMPORT_PREFIX))
|
|
98
|
+
continue;
|
|
99
|
+
const replacement = buildReplacement(stmt, sourceFile);
|
|
100
|
+
changes.push({
|
|
101
|
+
type: string_change_1.ChangeType.Delete,
|
|
102
|
+
start: stmt.getStart(sourceFile),
|
|
103
|
+
length: stmt.getEnd() - stmt.getStart(sourceFile),
|
|
104
|
+
}, {
|
|
105
|
+
type: string_change_1.ChangeType.Insert,
|
|
106
|
+
index: stmt.getStart(sourceFile),
|
|
107
|
+
text: replacement,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
let updated = changes.length > 0 ? (0, string_change_1.applyChangesToString)(source, changes) : source;
|
|
111
|
+
// Fallback: any remaining `@nx/devkit/src/...` specifiers (default imports,
|
|
112
|
+
// namespace imports, side-effect imports, dynamic `import(...)`, `require(...)`
|
|
113
|
+
// calls, etc.) get rewritten to `/internal`. We can't bucket by symbol for
|
|
114
|
+
// those forms, so `/internal` is the safe default since it re-exports every
|
|
115
|
+
// symbol that was deep-importable.
|
|
116
|
+
updated = updated.replace(FALLBACK_RE, (_match, quote) => `${quote}${INTERNAL_SPECIFIER}${quote}`);
|
|
117
|
+
// Final pass: collapse any duplicate `@nx/devkit` and `@nx/devkit/internal`
|
|
118
|
+
// named-only imports into a single declaration. This handles both the
|
|
119
|
+
// imports we just emitted AND any that the user already had, so we never
|
|
120
|
+
// leave the file with two `from '@nx/devkit'` lines.
|
|
121
|
+
updated = collapseDevkitImports(updated);
|
|
122
|
+
return updated;
|
|
123
|
+
}
|
|
124
|
+
function collapseDevkitImports(source) {
|
|
125
|
+
const sourceFile = ts.createSourceFile('tmp.ts', source, ts.ScriptTarget.Latest,
|
|
126
|
+
/* setParentNodes */ true, ts.ScriptKind.TSX);
|
|
127
|
+
const groups = new Map();
|
|
128
|
+
for (const stmt of sourceFile.statements) {
|
|
129
|
+
if (!ts.isImportDeclaration(stmt))
|
|
130
|
+
continue;
|
|
131
|
+
if (!ts.isStringLiteral(stmt.moduleSpecifier))
|
|
132
|
+
continue;
|
|
133
|
+
const specifier = stmt.moduleSpecifier.text;
|
|
134
|
+
if (specifier !== PUBLIC_SPECIFIER && specifier !== INTERNAL_SPECIFIER) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const importClause = stmt.importClause;
|
|
138
|
+
if (!importClause)
|
|
139
|
+
continue; // skip side-effect imports
|
|
140
|
+
if (importClause.name)
|
|
141
|
+
continue; // skip default imports
|
|
142
|
+
const namedBindings = importClause.namedBindings;
|
|
143
|
+
if (!namedBindings || !ts.isNamedImports(namedBindings))
|
|
144
|
+
continue;
|
|
145
|
+
const typeOnly = !!importClause.isTypeOnly;
|
|
146
|
+
const key = `${specifier}\x00${typeOnly ? 'type' : 'value'}`;
|
|
147
|
+
if (!groups.has(key)) {
|
|
148
|
+
groups.set(key, { decls: [], specifier, typeOnly });
|
|
149
|
+
}
|
|
150
|
+
groups.get(key).decls.push(stmt);
|
|
151
|
+
}
|
|
152
|
+
const changes = [];
|
|
153
|
+
for (const { decls, specifier, typeOnly } of groups.values()) {
|
|
154
|
+
if (decls.length < 2)
|
|
155
|
+
continue;
|
|
156
|
+
const seen = new Set();
|
|
157
|
+
const merged = [];
|
|
158
|
+
for (const decl of decls) {
|
|
159
|
+
const named = decl.importClause.namedBindings;
|
|
160
|
+
for (const el of named.elements) {
|
|
161
|
+
const text = renderSpecifierFromNode(el, typeOnly);
|
|
162
|
+
if (!seen.has(text)) {
|
|
163
|
+
seen.add(text);
|
|
164
|
+
merged.push(text);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Replace the first declaration with the merged one in place.
|
|
169
|
+
const first = decls[0];
|
|
170
|
+
changes.push({
|
|
171
|
+
type: string_change_1.ChangeType.Delete,
|
|
172
|
+
start: first.getStart(sourceFile),
|
|
173
|
+
length: first.getEnd() - first.getStart(sourceFile),
|
|
174
|
+
}, {
|
|
175
|
+
type: string_change_1.ChangeType.Insert,
|
|
176
|
+
index: first.getStart(sourceFile),
|
|
177
|
+
text: renderImport(merged, specifier, typeOnly),
|
|
178
|
+
});
|
|
179
|
+
// Delete every other declaration in this group (and consume one trailing
|
|
180
|
+
// newline so we don't leave behind a blank line that prettier has to clean
|
|
181
|
+
// up later).
|
|
182
|
+
for (let i = 1; i < decls.length; i++) {
|
|
183
|
+
const decl = decls[i];
|
|
184
|
+
const start = decl.getStart(sourceFile);
|
|
185
|
+
let end = decl.getEnd();
|
|
186
|
+
if (source[end] === '\n') {
|
|
187
|
+
end += 1;
|
|
188
|
+
}
|
|
189
|
+
else if (source[end] === '\r' && source[end + 1] === '\n') {
|
|
190
|
+
end += 2;
|
|
191
|
+
}
|
|
192
|
+
changes.push({
|
|
193
|
+
type: string_change_1.ChangeType.Delete,
|
|
194
|
+
start,
|
|
195
|
+
length: end - start,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return changes.length > 0 ? (0, string_change_1.applyChangesToString)(source, changes) : source;
|
|
200
|
+
}
|
|
201
|
+
function renderSpecifierFromNode(el, parentIsTypeOnly) {
|
|
202
|
+
const aliasText = el.propertyName ? ` as ${el.name.text}` : '';
|
|
203
|
+
const typePrefix = !parentIsTypeOnly && el.isTypeOnly ? 'type ' : '';
|
|
204
|
+
return `${typePrefix}${(el.propertyName ?? el.name).text}${aliasText}`;
|
|
205
|
+
}
|
|
206
|
+
function buildReplacement(decl, sourceFile) {
|
|
207
|
+
const importClause = decl.importClause;
|
|
208
|
+
// `import '@nx/devkit/src/...';` (side-effect) — no clause to bucket.
|
|
209
|
+
if (!importClause) {
|
|
210
|
+
return `import '${INTERNAL_SPECIFIER}';`;
|
|
211
|
+
}
|
|
212
|
+
const namedBindings = importClause.namedBindings;
|
|
213
|
+
const isNamedImport = namedBindings && ts.isNamedImports(namedBindings) && !importClause.name;
|
|
214
|
+
// Default / namespace / mixed-default-and-named — can't bucket reliably.
|
|
215
|
+
// Preserve the import shape, swap the specifier.
|
|
216
|
+
if (!isNamedImport) {
|
|
217
|
+
const before = source(decl, sourceFile).slice(0, decl.moduleSpecifier.getStart(sourceFile) - decl.getStart(sourceFile));
|
|
218
|
+
const after = source(decl, sourceFile).slice(decl.moduleSpecifier.getEnd() - decl.getStart(sourceFile));
|
|
219
|
+
return `${before}'${INTERNAL_SPECIFIER}'${after}`;
|
|
220
|
+
}
|
|
221
|
+
const isTypeOnlyImport = importClause.isTypeOnly;
|
|
222
|
+
const elements = namedBindings.elements;
|
|
223
|
+
const publik = [];
|
|
224
|
+
const internal = [];
|
|
225
|
+
for (const el of elements) {
|
|
226
|
+
bucketSpecifier(el, isTypeOnlyImport, publik, internal);
|
|
227
|
+
}
|
|
228
|
+
const lines = [];
|
|
229
|
+
if (publik.length > 0) {
|
|
230
|
+
lines.push(renderImport(publik, PUBLIC_SPECIFIER, isTypeOnlyImport));
|
|
231
|
+
}
|
|
232
|
+
if (internal.length > 0) {
|
|
233
|
+
lines.push(renderImport(internal, INTERNAL_SPECIFIER, isTypeOnlyImport));
|
|
234
|
+
}
|
|
235
|
+
if (lines.length === 0) {
|
|
236
|
+
// Defensive: empty `import {} from '...'` — point at /internal.
|
|
237
|
+
lines.push(`import {} from '${INTERNAL_SPECIFIER}';`);
|
|
238
|
+
}
|
|
239
|
+
return lines.join('\n');
|
|
240
|
+
}
|
|
241
|
+
function bucketSpecifier(el, parentIsTypeOnly, publik, internal) {
|
|
242
|
+
const lookupName = (el.propertyName ?? el.name).text;
|
|
243
|
+
const elementIsTypeOnly = el.isTypeOnly;
|
|
244
|
+
const aliasText = el.propertyName ? ` as ${el.name.text}` : '';
|
|
245
|
+
// Inline `type` is illegal when the parent import is already `import type`.
|
|
246
|
+
const typePrefix = !parentIsTypeOnly && elementIsTypeOnly ? 'type ' : '';
|
|
247
|
+
const text = `${typePrefix}${(el.propertyName ?? el.name).text}${aliasText}`;
|
|
248
|
+
if (exports.DEVKIT_INTERNAL_SYMBOLS.has(lookupName)) {
|
|
249
|
+
internal.push(text);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
publik.push(text);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function renderImport(specifiers, from, typeOnly) {
|
|
256
|
+
const prefix = typeOnly ? 'import type' : 'import';
|
|
257
|
+
return `${prefix} { ${specifiers.join(', ')} } from '${from}';`;
|
|
258
|
+
}
|
|
259
|
+
function source(decl, sourceFile) {
|
|
260
|
+
return sourceFile.text.slice(decl.getStart(sourceFile), decl.getEnd());
|
|
261
|
+
}
|
package/migrations.json
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generators": {
|
|
2
|
+
"generators": {
|
|
3
|
+
"update-devkit-deep-imports": {
|
|
4
|
+
"version": "23.0.0-beta.6",
|
|
5
|
+
"description": "Rewrite imports from `@nx/devkit/src/...` to `@nx/devkit` (for public symbols) or `@nx/devkit/internal` (for the rest), since deep imports are no longer reachable through the package's `exports` map.",
|
|
6
|
+
"implementation": "./dist/src/migrations/update-23-0-0/update-deep-imports"
|
|
7
|
+
}
|
|
8
|
+
},
|
|
3
9
|
"packageJsonUpdates": {},
|
|
4
10
|
"version": "0.1"
|
|
5
11
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nx/devkit",
|
|
3
|
-
"version": "23.0.0-
|
|
3
|
+
"version": "23.0.0-pr.33655.05d9542",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"files": [
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"jest": "^30.0.2",
|
|
63
|
-
"nx": "23.0.0-
|
|
63
|
+
"nx": "23.0.0-pr.33655.05d9542"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
66
|
"nx": ">= 22 <= 24 || ^23.0.0-0"
|