@craft-ng/dev-tools 0.1.3 → 0.1.5
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/package.json +1 -1
- package/src/eslint-rules/brand-angular-deps-match.cjs +33 -7
- package/src/eslint-rules/no-direct-angular-class-export.cjs +35 -6
- package/src/scripts/angular-brand-codemod.d.ts +17 -2
- package/src/scripts/angular-brand-codemod.js +454 -44
- package/src/scripts/angular-brand-codemod.js.map +1 -1
- package/src/scripts/angular-brand-codemod.spec.ts +228 -0
- package/src/scripts/angular-brand-codemod.ts +777 -63
|
@@ -2,6 +2,7 @@ import { __awaiter } from "tslib";
|
|
|
2
2
|
import { Node, Project, QuoteKind, SyntaxKind, ts, } from 'ts-morph';
|
|
3
3
|
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
4
4
|
import { basename, extname, isAbsolute, join, resolve } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
5
6
|
const SUPPORTED_DECORATORS = {
|
|
6
7
|
Component: 'component',
|
|
7
8
|
Directive: 'directive',
|
|
@@ -63,18 +64,17 @@ export function transformSourceFile(sourceFile, options = {}) {
|
|
|
63
64
|
if (analysis.skipped || !classDeclaration || !angularKind || !className) {
|
|
64
65
|
return result;
|
|
65
66
|
}
|
|
66
|
-
const exportSafety = getExportRewriteSafety(sourceFile, classDeclaration, className);
|
|
67
|
-
if (!exportSafety.safe) {
|
|
68
|
-
return skip(result, exportSafety.warnings);
|
|
69
|
-
}
|
|
70
67
|
const importSafety = getHelperImportSafety(sourceFile, normalizedOptions.helperImportPath);
|
|
71
68
|
if (!importSafety.safe) {
|
|
72
69
|
return skip(result, importSafety.warnings);
|
|
73
70
|
}
|
|
74
|
-
|
|
71
|
+
const beforeText = sourceFile.getFullText();
|
|
72
|
+
migrateLegacyBrandExport(sourceFile, classDeclaration, className);
|
|
75
73
|
ensureHelperImports(sourceFile, normalizedOptions.helperImportPath);
|
|
76
|
-
|
|
77
|
-
result.
|
|
74
|
+
ensureGeneratedDependencyTypeImports(sourceFile, result.generatedDependencyGroups);
|
|
75
|
+
writeGeneratedDepsTypeAlias(sourceFile, className, result.generatedDependencyGroups);
|
|
76
|
+
sourceFile.organizeImports();
|
|
77
|
+
result.changed = sourceFile.getFullText() !== beforeText;
|
|
78
78
|
return result;
|
|
79
79
|
}
|
|
80
80
|
export function analyzeSourceFileDependencies(sourceFile, options = {}) {
|
|
@@ -84,6 +84,7 @@ export function analyzeSourceFileDependencies(sourceFile, options = {}) {
|
|
|
84
84
|
warnings: [],
|
|
85
85
|
dependencies: [],
|
|
86
86
|
dependencyGroups: emptyDependencyGroups(),
|
|
87
|
+
generatedDependencyGroups: emptyGeneratedDependencyGroups(),
|
|
87
88
|
};
|
|
88
89
|
const angularClass = findAngularDecoratedClass(sourceFile);
|
|
89
90
|
result.warnings.push(...angularClass.warnings);
|
|
@@ -113,15 +114,20 @@ export function analyzeSourceFileDependencies(sourceFile, options = {}) {
|
|
|
113
114
|
const metadataDeps = angularKind === 'component' || angularKind === 'directive'
|
|
114
115
|
? extractDecoratorMetadataDepGroups(classDeclaration, angularKind, normalizedOptions)
|
|
115
116
|
: emptyMetadataDependencyGroups();
|
|
117
|
+
const providedDeps = angularKind === 'component' || angularKind === 'directive'
|
|
118
|
+
? extractProvidedDependencies(sourceFile, classDeclaration, angularKind, normalizedOptions)
|
|
119
|
+
: { entries: [], warnings: [] };
|
|
116
120
|
const constructorDeps = extractConstructorDeps(classDeclaration);
|
|
117
121
|
const injectCallDeps = extractInjectCallDeps(classDeclaration);
|
|
118
|
-
result.warnings.push(...metadataDeps.warnings, ...constructorDeps.warnings, ...injectCallDeps.warnings);
|
|
122
|
+
result.warnings.push(...metadataDeps.warnings, ...providedDeps.warnings, ...constructorDeps.warnings, ...injectCallDeps.warnings);
|
|
119
123
|
result.dependencies = mergeDeps(constructorDeps.dependencies, injectCallDeps.dependencies, metadataDeps.imports, metadataDeps.hostDirectives, metadataDeps.providers, metadataDeps.viewProviders);
|
|
120
124
|
result.dependencyGroups = {
|
|
121
125
|
injected: mergeDeps(constructorDeps.dependencies, injectCallDeps.dependencies),
|
|
122
126
|
importDeps: mergeDeps(metadataDeps.imports, metadataDeps.hostDirectives),
|
|
123
127
|
providers: mergeDeps(metadataDeps.providers, metadataDeps.viewProviders),
|
|
124
128
|
};
|
|
129
|
+
result.generatedTypeName = getGeneratedDepsTypeName(className);
|
|
130
|
+
result.generatedDependencyGroups = createGeneratedDependencyGroups(sourceFile, result.dependencyGroups.importDeps, result.dependencyGroups.injected, providedDeps.entries);
|
|
125
131
|
return result;
|
|
126
132
|
}
|
|
127
133
|
export function findAngularDecoratedClass(sourceFile) {
|
|
@@ -265,8 +271,199 @@ function emptyDependencyGroups() {
|
|
|
265
271
|
providers: [],
|
|
266
272
|
};
|
|
267
273
|
}
|
|
268
|
-
function
|
|
269
|
-
return
|
|
274
|
+
function emptyGeneratedDependencyGroups() {
|
|
275
|
+
return {
|
|
276
|
+
deps: [],
|
|
277
|
+
provided: [],
|
|
278
|
+
missingProvider: [],
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
function getGeneratedDepsTypeName(className) {
|
|
282
|
+
return `GenDeps_${className}`;
|
|
283
|
+
}
|
|
284
|
+
function createGeneratedDependencyGroups(sourceFile, importDependencies, injectedDependencies, providedEntries) {
|
|
285
|
+
const deps = mergeGeneratedDependencyEntries(importDependencies.map((dependency) => createGeneratedDependencyEntry(sourceFile, dependency, 'import')), injectedDependencies.map((dependency) => createGeneratedDependencyEntry(sourceFile, dependency, 'inject')));
|
|
286
|
+
const provided = mergeGeneratedDependencyEntries(providedEntries);
|
|
287
|
+
const providedKeys = new Set(provided.map((entry) => entry.key));
|
|
288
|
+
const missingProvider = mergeGeneratedDependencyEntries(injectedDependencies
|
|
289
|
+
.filter((dependency) => {
|
|
290
|
+
const dependencyKey = createGeneratedDependencyKey(dependency);
|
|
291
|
+
return (!providedKeys.has(dependencyKey) &&
|
|
292
|
+
!isProvidedInInjectableTree(sourceFile, dependency));
|
|
293
|
+
})
|
|
294
|
+
.map((dependency) => createGeneratedDependencyEntry(sourceFile, dependency, 'inject')));
|
|
295
|
+
return {
|
|
296
|
+
deps,
|
|
297
|
+
provided,
|
|
298
|
+
missingProvider,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function mergeGeneratedDependencyEntries(...groups) {
|
|
302
|
+
const entries = new Map();
|
|
303
|
+
for (const group of groups) {
|
|
304
|
+
for (const entry of group) {
|
|
305
|
+
if (!entries.has(entry.key)) {
|
|
306
|
+
entries.set(entry.key, entry);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return [...entries.values()];
|
|
311
|
+
}
|
|
312
|
+
function createGeneratedDependencyEntry(sourceFile, dependencyText, context) {
|
|
313
|
+
if (context === 'import') {
|
|
314
|
+
const declarableResolution = resolveAngularDeclarableDependency(sourceFile, dependencyText);
|
|
315
|
+
if (declarableResolution) {
|
|
316
|
+
const generatedTypeName = getGeneratedDepsTypeName(declarableResolution.className);
|
|
317
|
+
return {
|
|
318
|
+
key: generatedTypeName,
|
|
319
|
+
typeText: generatedTypeName,
|
|
320
|
+
typeImport: declarableResolution.moduleSpecifier
|
|
321
|
+
? {
|
|
322
|
+
moduleSpecifier: declarableResolution.moduleSpecifier,
|
|
323
|
+
name: generatedTypeName,
|
|
324
|
+
}
|
|
325
|
+
: undefined,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return {
|
|
330
|
+
key: createGeneratedDependencyKey(dependencyText),
|
|
331
|
+
typeText: createGeneratedDependencyTypeText(sourceFile, dependencyText),
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
function createGeneratedDependencyKey(dependencyText) {
|
|
335
|
+
var _a;
|
|
336
|
+
const lastSegment = (_a = dependencyText.split('.').pop()) !== null && _a !== void 0 ? _a : dependencyText;
|
|
337
|
+
const helperMatch = /^(inject|provide)([A-Z].*)$/.exec(lastSegment);
|
|
338
|
+
if (helperMatch) {
|
|
339
|
+
return helperMatch[2];
|
|
340
|
+
}
|
|
341
|
+
const toYieldMatch = /^(.*)ToYield$/.exec(lastSegment);
|
|
342
|
+
if (toYieldMatch === null || toYieldMatch === void 0 ? void 0 : toYieldMatch[1]) {
|
|
343
|
+
return toYieldMatch[1];
|
|
344
|
+
}
|
|
345
|
+
return lastSegment;
|
|
346
|
+
}
|
|
347
|
+
function createGeneratedDependencyTypeText(sourceFile, dependencyText) {
|
|
348
|
+
if (isHelperLikeDependency(dependencyText)) {
|
|
349
|
+
return `ReturnType<typeof ${dependencyText}>`;
|
|
350
|
+
}
|
|
351
|
+
const resolution = resolveDependencyReference(sourceFile, dependencyText);
|
|
352
|
+
if (resolution.kind === 'class' || resolution.kind === 'enum') {
|
|
353
|
+
return dependencyText;
|
|
354
|
+
}
|
|
355
|
+
if (resolution.kind === 'unknown') {
|
|
356
|
+
return dependencyText;
|
|
357
|
+
}
|
|
358
|
+
return `typeof ${dependencyText}`;
|
|
359
|
+
}
|
|
360
|
+
function isHelperLikeDependency(dependencyText) {
|
|
361
|
+
var _a;
|
|
362
|
+
const lastSegment = (_a = dependencyText.split('.').pop()) !== null && _a !== void 0 ? _a : dependencyText;
|
|
363
|
+
return (/^(inject|provide)[A-Z].*/.test(lastSegment) || /ToYield$/.test(lastSegment));
|
|
364
|
+
}
|
|
365
|
+
function resolveAngularDeclarableDependency(sourceFile, dependencyText) {
|
|
366
|
+
const resolution = resolveDependencyReference(sourceFile, dependencyText);
|
|
367
|
+
if (!resolution.classDeclaration) {
|
|
368
|
+
return undefined;
|
|
369
|
+
}
|
|
370
|
+
const angularKind = getAngularKind(resolution.classDeclaration);
|
|
371
|
+
if (angularKind !== 'component' &&
|
|
372
|
+
angularKind !== 'directive' &&
|
|
373
|
+
angularKind !== 'pipe') {
|
|
374
|
+
return undefined;
|
|
375
|
+
}
|
|
376
|
+
const className = resolution.classDeclaration.getName();
|
|
377
|
+
if (!className) {
|
|
378
|
+
return undefined;
|
|
379
|
+
}
|
|
380
|
+
const declarationSourceFile = resolution.classDeclaration.getSourceFile();
|
|
381
|
+
if (declarationSourceFile === sourceFile ||
|
|
382
|
+
declarationSourceFile.isDeclarationFile()) {
|
|
383
|
+
return undefined;
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
className,
|
|
387
|
+
moduleSpecifier: resolution.moduleSpecifier,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
function ensureGeneratedDependencyTypeImports(sourceFile, generatedDependencyGroups) {
|
|
391
|
+
var _a;
|
|
392
|
+
const importsToAdd = new Map();
|
|
393
|
+
for (const entry of generatedDependencyGroups.deps) {
|
|
394
|
+
if (!entry.typeImport) {
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
const importNames = (_a = importsToAdd.get(entry.typeImport.moduleSpecifier)) !== null && _a !== void 0 ? _a : new Set();
|
|
398
|
+
importNames.add(entry.typeImport.name);
|
|
399
|
+
importsToAdd.set(entry.typeImport.moduleSpecifier, importNames);
|
|
400
|
+
}
|
|
401
|
+
for (const [moduleSpecifier, importNames] of importsToAdd) {
|
|
402
|
+
const existingImport = sourceFile
|
|
403
|
+
.getImportDeclarations()
|
|
404
|
+
.find((importDeclaration) => importDeclaration.getModuleSpecifierValue() === moduleSpecifier);
|
|
405
|
+
if (existingImport) {
|
|
406
|
+
existingImport.addNamedImports([...importNames].map((name) => ({ name, isTypeOnly: true })));
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
sourceFile.addImportDeclaration({
|
|
410
|
+
moduleSpecifier,
|
|
411
|
+
namedImports: [...importNames].map((name) => ({
|
|
412
|
+
name,
|
|
413
|
+
isTypeOnly: true,
|
|
414
|
+
})),
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
function formatGeneratedDependencyType(generatedDependencyGroups) {
|
|
419
|
+
return [
|
|
420
|
+
'{',
|
|
421
|
+
` deps: ${formatGeneratedDependencyObject(generatedDependencyGroups.deps)};`,
|
|
422
|
+
` provided: ${formatGeneratedDependencyObject(generatedDependencyGroups.provided)};`,
|
|
423
|
+
` missingProvider: ${formatGeneratedDependencyObject(generatedDependencyGroups.missingProvider)};`,
|
|
424
|
+
'}',
|
|
425
|
+
].join('\n');
|
|
426
|
+
}
|
|
427
|
+
function formatGeneratedDependencyObject(entries) {
|
|
428
|
+
if (entries.length === 0) {
|
|
429
|
+
return '{}';
|
|
430
|
+
}
|
|
431
|
+
return [
|
|
432
|
+
'{',
|
|
433
|
+
...entries.map((entry) => ` ${formatObjectKey(entry.key)}: ${entry.typeText};`),
|
|
434
|
+
' }',
|
|
435
|
+
].join('\n');
|
|
436
|
+
}
|
|
437
|
+
function formatObjectKey(key) {
|
|
438
|
+
return /^[$A-Z_][0-9A-Z_$]*$/i.test(key) ? key : `'${key}'`;
|
|
439
|
+
}
|
|
440
|
+
export function writeGeneratedDepsTypeAlias(sourceFile, className, generatedDependencyGroups) {
|
|
441
|
+
const generatedTypeName = getGeneratedDepsTypeName(className);
|
|
442
|
+
const generatedType = `GetDeps<${formatGeneratedDependencyType(generatedDependencyGroups)}>`;
|
|
443
|
+
const existingTypeAlias = sourceFile.getTypeAlias(generatedTypeName);
|
|
444
|
+
if (existingTypeAlias) {
|
|
445
|
+
existingTypeAlias.setIsExported(true);
|
|
446
|
+
existingTypeAlias.setType(generatedType);
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
sourceFile.addTypeAlias({
|
|
450
|
+
isExported: true,
|
|
451
|
+
name: generatedTypeName,
|
|
452
|
+
type: generatedType,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
export function migrateLegacyBrandExport(sourceFile, classDeclaration, className) {
|
|
456
|
+
const existing = readExistingDependencyGroups(sourceFile, className);
|
|
457
|
+
if (!existing.found || !existing.exportAssignmentNode) {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
const exportAssignment = existing.exportAssignmentNode.asKind(SyntaxKind.ExportAssignment);
|
|
461
|
+
exportAssignment === null || exportAssignment === void 0 ? void 0 : exportAssignment.remove();
|
|
462
|
+
if (!classDeclaration.isDefaultExport() &&
|
|
463
|
+
!classDeclaration.isExported() &&
|
|
464
|
+
exportAssignment) {
|
|
465
|
+
classDeclaration.setIsDefaultExport(true);
|
|
466
|
+
}
|
|
270
467
|
}
|
|
271
468
|
function invalidExistingDependencyGroups(node, warnings) {
|
|
272
469
|
return {
|
|
@@ -327,7 +524,7 @@ function getInjectionHelperDependency(expression) {
|
|
|
327
524
|
return undefined;
|
|
328
525
|
}
|
|
329
526
|
export function ensureHelperImports(sourceFile, helperImportPath) {
|
|
330
|
-
const missingImports = ['
|
|
527
|
+
const missingImports = ['GetDeps'].filter((importName) => !hasLocalNamedImport(sourceFile, helperImportPath, importName));
|
|
331
528
|
if (missingImports.length === 0) {
|
|
332
529
|
return;
|
|
333
530
|
}
|
|
@@ -335,35 +532,15 @@ export function ensureHelperImports(sourceFile, helperImportPath) {
|
|
|
335
532
|
.getImportDeclarations()
|
|
336
533
|
.find((importDeclaration) => importDeclaration.getModuleSpecifierValue() === helperImportPath);
|
|
337
534
|
if (existingImport) {
|
|
338
|
-
existingImport.addNamedImports(missingImports);
|
|
535
|
+
existingImport.addNamedImports(missingImports.map((name) => ({ name, isTypeOnly: true })));
|
|
339
536
|
return;
|
|
340
537
|
}
|
|
341
538
|
sourceFile.addImportDeclaration({
|
|
342
539
|
moduleSpecifier: helperImportPath,
|
|
343
|
-
namedImports: missingImports
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
if (classDeclaration.isDefaultExport()) {
|
|
348
|
-
classDeclaration.setIsDefaultExport(false);
|
|
349
|
-
}
|
|
350
|
-
if (classDeclaration.isExported()) {
|
|
351
|
-
classDeclaration.setIsExported(false);
|
|
352
|
-
}
|
|
353
|
-
for (const exportAssignment of sourceFile.getExportAssignments()) {
|
|
354
|
-
if (isDefaultIdentifierExport(exportAssignment, className) ||
|
|
355
|
-
isDefaultBrandedExport(exportAssignment, className)) {
|
|
356
|
-
exportAssignment.remove();
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
for (const exportDeclaration of sourceFile.getExportDeclarations()) {
|
|
360
|
-
removeClassFromExportDeclaration(exportDeclaration, className);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
export function writeDefaultBrandExport(sourceFile, className, dependencyGroups) {
|
|
364
|
-
sourceFile.addExportAssignment({
|
|
365
|
-
expression: `brandAngularSymbol(${className}, ${createDepsExpression(dependencyGroups)})`,
|
|
366
|
-
isExportEquals: false,
|
|
540
|
+
namedImports: missingImports.map((name) => ({
|
|
541
|
+
name,
|
|
542
|
+
isTypeOnly: true,
|
|
543
|
+
})),
|
|
367
544
|
});
|
|
368
545
|
}
|
|
369
546
|
export function readExistingDependencyGroups(sourceFile, className) {
|
|
@@ -594,6 +771,108 @@ function emptyMetadataDependencyGroups() {
|
|
|
594
771
|
warnings: [],
|
|
595
772
|
};
|
|
596
773
|
}
|
|
774
|
+
function extractProvidedDependencies(sourceFile, classDeclaration, angularKind, options) {
|
|
775
|
+
const metadata = getDecoratorMetadataObject(classDeclaration);
|
|
776
|
+
const warnings = [];
|
|
777
|
+
const entries = [];
|
|
778
|
+
if (!metadata) {
|
|
779
|
+
return { entries, warnings };
|
|
780
|
+
}
|
|
781
|
+
if (options.includeProviders) {
|
|
782
|
+
entries.push(...extractProvidedDependencyArrayProperty(sourceFile, metadata, 'providers', warnings));
|
|
783
|
+
}
|
|
784
|
+
if (options.includeViewProviders && angularKind === 'component') {
|
|
785
|
+
entries.push(...extractProvidedDependencyArrayProperty(sourceFile, metadata, 'viewProviders', warnings));
|
|
786
|
+
}
|
|
787
|
+
return {
|
|
788
|
+
entries: mergeGeneratedDependencyEntries(entries),
|
|
789
|
+
warnings,
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
function extractProvidedDependencyArrayProperty(sourceFile, metadata, propertyName, warnings) {
|
|
793
|
+
const property = getObjectPropertyAssignment(metadata, propertyName);
|
|
794
|
+
if (!property) {
|
|
795
|
+
return [];
|
|
796
|
+
}
|
|
797
|
+
const initializer = property.getInitializer();
|
|
798
|
+
if (!Node.isArrayLiteralExpression(initializer)) {
|
|
799
|
+
warnings.push(`Skipped metadata property "${propertyName}" because it is not a static array.`);
|
|
800
|
+
return [];
|
|
801
|
+
}
|
|
802
|
+
return extractProvidedDependencyEntriesFromArray(sourceFile, initializer, propertyName, warnings);
|
|
803
|
+
}
|
|
804
|
+
function extractProvidedDependencyEntriesFromArray(sourceFile, arrayLiteral, context, warnings) {
|
|
805
|
+
const entries = [];
|
|
806
|
+
for (const element of arrayLiteral.getElements()) {
|
|
807
|
+
if (Node.isArrayLiteralExpression(element)) {
|
|
808
|
+
entries.push(...extractProvidedDependencyEntriesFromArray(sourceFile, element, context, warnings));
|
|
809
|
+
continue;
|
|
810
|
+
}
|
|
811
|
+
if (Node.isSpreadElement(element)) {
|
|
812
|
+
warnings.push(`Skipped spread element in "${context}" metadata providers.`);
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
if (Node.isCallExpression(element)) {
|
|
816
|
+
const providerFactory = getStaticExpressionText(element.getExpression());
|
|
817
|
+
if (!providerFactory) {
|
|
818
|
+
warnings.push(`Skipped complex provider factory "${element.getText()}" in "${context}".`);
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
entries.push({
|
|
822
|
+
key: createGeneratedDependencyKey(providerFactory),
|
|
823
|
+
typeText: `ReturnType<typeof ${providerFactory}>`,
|
|
824
|
+
});
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
827
|
+
const staticExpression = getStaticExpressionText(element);
|
|
828
|
+
if (staticExpression) {
|
|
829
|
+
entries.push({
|
|
830
|
+
key: createGeneratedDependencyKey(staticExpression),
|
|
831
|
+
typeText: createGeneratedDependencyTypeText(sourceFile, staticExpression),
|
|
832
|
+
});
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
if (Node.isObjectLiteralExpression(element)) {
|
|
836
|
+
const providerEntry = extractProvidedDependencyEntryFromObjectLiteral(sourceFile, element, context, warnings);
|
|
837
|
+
if (providerEntry) {
|
|
838
|
+
entries.push(providerEntry);
|
|
839
|
+
}
|
|
840
|
+
continue;
|
|
841
|
+
}
|
|
842
|
+
warnings.push(`Skipped complex expression "${element.getText()}" in "${context}" metadata providers.`);
|
|
843
|
+
}
|
|
844
|
+
return entries;
|
|
845
|
+
}
|
|
846
|
+
function extractProvidedDependencyEntryFromObjectLiteral(sourceFile, objectLiteral, context, warnings) {
|
|
847
|
+
var _a, _b, _c, _d, _e;
|
|
848
|
+
const provideProperty = getObjectPropertyAssignment(objectLiteral, 'provide');
|
|
849
|
+
const provideText = getStaticExpressionText(provideProperty === null || provideProperty === void 0 ? void 0 : provideProperty.getInitializer());
|
|
850
|
+
if (!provideText) {
|
|
851
|
+
warnings.push(`Skipped provider object in "${context}" because "provide" is not static.`);
|
|
852
|
+
return undefined;
|
|
853
|
+
}
|
|
854
|
+
const useClass = getStaticExpressionText((_a = getObjectPropertyAssignment(objectLiteral, 'useClass')) === null || _a === void 0 ? void 0 : _a.getInitializer());
|
|
855
|
+
const useExisting = getStaticExpressionText((_b = getObjectPropertyAssignment(objectLiteral, 'useExisting')) === null || _b === void 0 ? void 0 : _b.getInitializer());
|
|
856
|
+
const useValue = getStaticExpressionText((_c = getObjectPropertyAssignment(objectLiteral, 'useValue')) === null || _c === void 0 ? void 0 : _c.getInitializer());
|
|
857
|
+
const useFactory = getStaticExpressionText((_d = getObjectPropertyAssignment(objectLiteral, 'useFactory')) === null || _d === void 0 ? void 0 : _d.getInitializer());
|
|
858
|
+
if (useFactory) {
|
|
859
|
+
return {
|
|
860
|
+
key: createGeneratedDependencyKey(provideText),
|
|
861
|
+
typeText: `ReturnType<typeof ${useFactory}>`,
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
if (useValue) {
|
|
865
|
+
return {
|
|
866
|
+
key: createGeneratedDependencyKey(provideText),
|
|
867
|
+
typeText: `typeof ${useValue}`,
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
const valueText = (_e = useClass !== null && useClass !== void 0 ? useClass : useExisting) !== null && _e !== void 0 ? _e : provideText;
|
|
871
|
+
return {
|
|
872
|
+
key: createGeneratedDependencyKey(provideText),
|
|
873
|
+
typeText: createGeneratedDependencyTypeText(sourceFile, valueText),
|
|
874
|
+
};
|
|
875
|
+
}
|
|
597
876
|
function extractMetadataArrayProperty(metadata, propertyName, context, warnings) {
|
|
598
877
|
const property = getObjectPropertyAssignment(metadata, propertyName);
|
|
599
878
|
if (!property) {
|
|
@@ -700,6 +979,134 @@ function getStaticPropertyName(nameNode) {
|
|
|
700
979
|
}
|
|
701
980
|
return undefined;
|
|
702
981
|
}
|
|
982
|
+
function resolveDependencyReference(sourceFile, dependencyText) {
|
|
983
|
+
const [rootIdentifier] = dependencyText.split('.');
|
|
984
|
+
if (!rootIdentifier) {
|
|
985
|
+
return { kind: 'unknown' };
|
|
986
|
+
}
|
|
987
|
+
const importResolution = resolveImportedDependencyReference(sourceFile, dependencyText, rootIdentifier);
|
|
988
|
+
if (importResolution) {
|
|
989
|
+
return importResolution;
|
|
990
|
+
}
|
|
991
|
+
const localClass = sourceFile.getClass(rootIdentifier);
|
|
992
|
+
if (localClass) {
|
|
993
|
+
return { kind: 'class', classDeclaration: localClass };
|
|
994
|
+
}
|
|
995
|
+
if (sourceFile.getEnum(rootIdentifier)) {
|
|
996
|
+
return { kind: 'enum' };
|
|
997
|
+
}
|
|
998
|
+
if (sourceFile.getFunction(rootIdentifier)) {
|
|
999
|
+
return { kind: 'function' };
|
|
1000
|
+
}
|
|
1001
|
+
if (sourceFile
|
|
1002
|
+
.getVariableDeclarations()
|
|
1003
|
+
.some((declaration) => declaration.getName() === rootIdentifier)) {
|
|
1004
|
+
return { kind: 'variable' };
|
|
1005
|
+
}
|
|
1006
|
+
return { kind: 'unknown' };
|
|
1007
|
+
}
|
|
1008
|
+
function resolveImportedDependencyReference(sourceFile, dependencyText, rootIdentifier) {
|
|
1009
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1010
|
+
for (const importDeclaration of sourceFile.getImportDeclarations()) {
|
|
1011
|
+
const moduleSpecifier = importDeclaration.getModuleSpecifierValue();
|
|
1012
|
+
if (((_a = importDeclaration.getDefaultImport()) === null || _a === void 0 ? void 0 : _a.getText()) === rootIdentifier) {
|
|
1013
|
+
const declaration = getDefaultImportClassDeclaration(importDeclaration);
|
|
1014
|
+
if (declaration) {
|
|
1015
|
+
return {
|
|
1016
|
+
kind: 'class',
|
|
1017
|
+
classDeclaration: declaration,
|
|
1018
|
+
moduleSpecifier,
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
return { kind: 'unknown', moduleSpecifier };
|
|
1022
|
+
}
|
|
1023
|
+
if (((_b = importDeclaration.getNamespaceImport()) === null || _b === void 0 ? void 0 : _b.getText()) === rootIdentifier) {
|
|
1024
|
+
const declaration = getNamespaceImportClassDeclaration(importDeclaration, dependencyText);
|
|
1025
|
+
if (declaration) {
|
|
1026
|
+
return {
|
|
1027
|
+
kind: 'class',
|
|
1028
|
+
classDeclaration: declaration,
|
|
1029
|
+
moduleSpecifier,
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
return { kind: 'namespace', moduleSpecifier };
|
|
1033
|
+
}
|
|
1034
|
+
const namedImport = importDeclaration
|
|
1035
|
+
.getNamedImports()
|
|
1036
|
+
.find((specifier) => {
|
|
1037
|
+
var _a, _b;
|
|
1038
|
+
const localName = (_b = (_a = specifier.getAliasNode()) === null || _a === void 0 ? void 0 : _a.getText()) !== null && _b !== void 0 ? _b : specifier.getNameNode().getText();
|
|
1039
|
+
return localName === rootIdentifier;
|
|
1040
|
+
});
|
|
1041
|
+
if (!namedImport) {
|
|
1042
|
+
continue;
|
|
1043
|
+
}
|
|
1044
|
+
const localIdentifier = (_c = namedImport.getAliasNode()) !== null && _c !== void 0 ? _c : namedImport.getNameNode();
|
|
1045
|
+
const symbol = (_d = localIdentifier.getSymbol()) !== null && _d !== void 0 ? _d : localIdentifier.getType().getSymbol();
|
|
1046
|
+
const aliasedSymbol = (_e = symbol === null || symbol === void 0 ? void 0 : symbol.getAliasedSymbol()) !== null && _e !== void 0 ? _e : symbol;
|
|
1047
|
+
const declarations = (_f = aliasedSymbol === null || aliasedSymbol === void 0 ? void 0 : aliasedSymbol.getDeclarations()) !== null && _f !== void 0 ? _f : [];
|
|
1048
|
+
const classDeclaration = declarations.find((declaration) => Node.isClassDeclaration(declaration));
|
|
1049
|
+
if (classDeclaration && Node.isClassDeclaration(classDeclaration)) {
|
|
1050
|
+
return {
|
|
1051
|
+
kind: 'class',
|
|
1052
|
+
classDeclaration,
|
|
1053
|
+
moduleSpecifier,
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
if (declarations.some((declaration) => Node.isEnumDeclaration(declaration))) {
|
|
1057
|
+
return { kind: 'enum', moduleSpecifier };
|
|
1058
|
+
}
|
|
1059
|
+
if (declarations.some((declaration) => Node.isFunctionDeclaration(declaration))) {
|
|
1060
|
+
return { kind: 'function', moduleSpecifier };
|
|
1061
|
+
}
|
|
1062
|
+
if (declarations.some((declaration) => Node.isVariableDeclaration(declaration))) {
|
|
1063
|
+
return { kind: 'variable', moduleSpecifier };
|
|
1064
|
+
}
|
|
1065
|
+
return { kind: 'unknown', moduleSpecifier };
|
|
1066
|
+
}
|
|
1067
|
+
return undefined;
|
|
1068
|
+
}
|
|
1069
|
+
function getDefaultImportClassDeclaration(importDeclaration) {
|
|
1070
|
+
var _a;
|
|
1071
|
+
const moduleSourceFile = importDeclaration.getModuleSpecifierSourceFile();
|
|
1072
|
+
if (!moduleSourceFile) {
|
|
1073
|
+
return undefined;
|
|
1074
|
+
}
|
|
1075
|
+
const defaultExportSymbol = moduleSourceFile.getDefaultExportSymbol();
|
|
1076
|
+
const declarations = (_a = defaultExportSymbol === null || defaultExportSymbol === void 0 ? void 0 : defaultExportSymbol.getDeclarations()) !== null && _a !== void 0 ? _a : [];
|
|
1077
|
+
return declarations.find((declaration) => Node.isClassDeclaration(declaration));
|
|
1078
|
+
}
|
|
1079
|
+
function getNamespaceImportClassDeclaration(importDeclaration, dependencyText) {
|
|
1080
|
+
var _a;
|
|
1081
|
+
const moduleSourceFile = importDeclaration.getModuleSpecifierSourceFile();
|
|
1082
|
+
if (!moduleSourceFile) {
|
|
1083
|
+
return undefined;
|
|
1084
|
+
}
|
|
1085
|
+
const leafIdentifier = dependencyText.split('.').pop();
|
|
1086
|
+
if (!leafIdentifier) {
|
|
1087
|
+
return undefined;
|
|
1088
|
+
}
|
|
1089
|
+
const exportedDeclarations = (_a = moduleSourceFile.getExportedDeclarations().get(leafIdentifier)) !== null && _a !== void 0 ? _a : [];
|
|
1090
|
+
return exportedDeclarations.find((declaration) => Node.isClassDeclaration(declaration));
|
|
1091
|
+
}
|
|
1092
|
+
function isProvidedInInjectableTree(sourceFile, dependencyText) {
|
|
1093
|
+
const resolution = resolveDependencyReference(sourceFile, dependencyText);
|
|
1094
|
+
const classDeclaration = resolution.classDeclaration;
|
|
1095
|
+
if (!classDeclaration || getAngularKind(classDeclaration) !== 'injectable') {
|
|
1096
|
+
return false;
|
|
1097
|
+
}
|
|
1098
|
+
const metadata = getDecoratorMetadataObject(classDeclaration);
|
|
1099
|
+
const providedInProperty = metadata
|
|
1100
|
+
? getObjectPropertyAssignment(metadata, 'providedIn')
|
|
1101
|
+
: undefined;
|
|
1102
|
+
const initializer = providedInProperty === null || providedInProperty === void 0 ? void 0 : providedInProperty.getInitializer();
|
|
1103
|
+
if (!initializer) {
|
|
1104
|
+
return false;
|
|
1105
|
+
}
|
|
1106
|
+
return (!Node.isNullLiteral(initializer) &&
|
|
1107
|
+
!Node.isFalseLiteral(initializer) &&
|
|
1108
|
+
!(Node.isIdentifier(initializer) && initializer.getText() === 'undefined'));
|
|
1109
|
+
}
|
|
703
1110
|
function getDependencyTextFromTypeNode(typeNode) {
|
|
704
1111
|
if (isPrimitiveTypeNode(typeNode)) {
|
|
705
1112
|
return undefined;
|
|
@@ -861,7 +1268,7 @@ function getExportRewriteSafety(sourceFile, classDeclaration, className) {
|
|
|
861
1268
|
: { safe: false, warnings };
|
|
862
1269
|
}
|
|
863
1270
|
function getHelperImportSafety(sourceFile, helperImportPath) {
|
|
864
|
-
const warnings = ['
|
|
1271
|
+
const warnings = ['GetDeps']
|
|
865
1272
|
.filter((helperName) => hasConflictingTopLevelBinding(sourceFile, helperImportPath, helperName))
|
|
866
1273
|
.map((helperName) => `Skipped file because "${helperName}" is already bound to a different top-level symbol.`);
|
|
867
1274
|
return warnings.length === 0
|
|
@@ -1044,9 +1451,9 @@ function logFileResult(report, log) {
|
|
|
1044
1451
|
`file=${report.filePath}`,
|
|
1045
1452
|
report.angularKind ? `kind=${report.angularKind}` : undefined,
|
|
1046
1453
|
report.className ? `class=${report.className}` : undefined,
|
|
1047
|
-
`
|
|
1048
|
-
`
|
|
1049
|
-
`
|
|
1454
|
+
`deps=[${report.generatedDependencyGroups.deps.map((entry) => entry.key).join(', ')}]`,
|
|
1455
|
+
`provided=[${report.generatedDependencyGroups.provided.map((entry) => entry.key).join(', ')}]`,
|
|
1456
|
+
`missingProvider=[${report.generatedDependencyGroups.missingProvider.map((entry) => entry.key).join(', ')}]`,
|
|
1050
1457
|
`status=${status}`,
|
|
1051
1458
|
].filter(Boolean);
|
|
1052
1459
|
log(details.join(' '));
|
|
@@ -1106,16 +1513,19 @@ function printHelpAndExit() {
|
|
|
1106
1513
|
Options:
|
|
1107
1514
|
--root <dir> Project root. Defaults to cwd.
|
|
1108
1515
|
--tsconfig <path> tsconfig path. Defaults to <root>/tsconfig.json.
|
|
1109
|
-
--helper-import <path> Import path for
|
|
1516
|
+
--helper-import <path> Import path for GetDeps.
|
|
1110
1517
|
--transform-only-standalone-declarables Only transform standalone components/directives.
|
|
1111
|
-
--no-providers Do not include metadata providers in
|
|
1112
|
-
--no-view-providers Do not include component viewProviders in
|
|
1518
|
+
--no-providers Do not include metadata providers in generated types.
|
|
1519
|
+
--no-view-providers Do not include component viewProviders in generated types.
|
|
1113
1520
|
--dry-run Print results without writing files.
|
|
1114
1521
|
--help Show this help.
|
|
1115
1522
|
`);
|
|
1116
1523
|
process.exit(0);
|
|
1117
1524
|
}
|
|
1118
|
-
if (require.main === module)
|
|
1525
|
+
// Check if this module is being run directly (ESM equivalent of require.main === module)
|
|
1526
|
+
if (import.meta.url === `file://${process.argv[1]}` ||
|
|
1527
|
+
(import.meta.url.startsWith('file:') &&
|
|
1528
|
+
process.argv[1] === fileURLToPath(import.meta.url))) {
|
|
1119
1529
|
runAngularBrandCodemod(parseCliArgs(process.argv.slice(2))).catch((error) => {
|
|
1120
1530
|
console.error(error);
|
|
1121
1531
|
process.exitCode = 1;
|