@craft-ng/dev-tools 0.1.4 → 0.1.6
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 +25 -3
- package/src/scripts/angular-brand-codemod.js +744 -45
- package/src/scripts/angular-brand-codemod.js.map +1 -1
- package/src/scripts/angular-brand-codemod.spec.ts +303 -0
- package/src/scripts/angular-brand-codemod.ts +1260 -77
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
} from 'ts-morph';
|
|
18
18
|
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
19
19
|
import { basename, extname, isAbsolute, join, resolve } from 'node:path';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
20
21
|
|
|
21
22
|
export type AngularKind = 'component' | 'directive' | 'pipe' | 'injectable';
|
|
22
23
|
|
|
@@ -28,6 +29,8 @@ export type TransformResult = {
|
|
|
28
29
|
className?: string;
|
|
29
30
|
dependencies: string[];
|
|
30
31
|
dependencyGroups: DependencyGroups;
|
|
32
|
+
generatedTypeName?: string;
|
|
33
|
+
generatedDependencyGroups: GeneratedDependencyGroups;
|
|
31
34
|
};
|
|
32
35
|
|
|
33
36
|
export type AngularBrandCodemodOptions = {
|
|
@@ -56,6 +59,21 @@ export type DependencyGroups = {
|
|
|
56
59
|
providers: string[];
|
|
57
60
|
};
|
|
58
61
|
|
|
62
|
+
export type GeneratedDependencyEntry = {
|
|
63
|
+
key: string;
|
|
64
|
+
typeText: string;
|
|
65
|
+
typeImport?: {
|
|
66
|
+
moduleSpecifier: string;
|
|
67
|
+
name: string;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export type GeneratedDependencyGroups = {
|
|
72
|
+
deps: GeneratedDependencyEntry[];
|
|
73
|
+
provided: GeneratedDependencyEntry[];
|
|
74
|
+
missingProvider: GeneratedDependencyEntry[];
|
|
75
|
+
};
|
|
76
|
+
|
|
59
77
|
export type DependencyAnalysisResult = Omit<TransformResult, 'changed'> & {
|
|
60
78
|
classDeclaration?: ClassDeclaration;
|
|
61
79
|
};
|
|
@@ -77,6 +95,44 @@ type MetadataDependencyGroups = {
|
|
|
77
95
|
warnings: string[];
|
|
78
96
|
};
|
|
79
97
|
|
|
98
|
+
type ProvidedDependencyExtractionResult = {
|
|
99
|
+
entries: GeneratedDependencyEntry[];
|
|
100
|
+
warnings: string[];
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
type InjectedDependencyDescriptor = {
|
|
104
|
+
dependencyText: string;
|
|
105
|
+
entry: GeneratedDependencyEntry;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
type InjectCallDependencyExtractionResult = DependencyExtractionResult & {
|
|
109
|
+
generatedDependencies: InjectedDependencyDescriptor[];
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
type DependencyReferenceResolution = {
|
|
113
|
+
kind: 'class' | 'enum' | 'function' | 'variable' | 'namespace' | 'unknown';
|
|
114
|
+
classDeclaration?: ClassDeclaration;
|
|
115
|
+
moduleSpecifier?: string;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
type TrackedHelperResolution = {
|
|
119
|
+
serviceName: string;
|
|
120
|
+
scope: string;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
type HelperExposureBinding = {
|
|
124
|
+
localName: string;
|
|
125
|
+
sourceKey: string;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
type HelperExposureTracking = {
|
|
129
|
+
usedProperties: string[];
|
|
130
|
+
exposedProperties: Array<{
|
|
131
|
+
exposedKey: string;
|
|
132
|
+
sourceKey: string;
|
|
133
|
+
}>;
|
|
134
|
+
};
|
|
135
|
+
|
|
80
136
|
type NormalizedOptions = Required<AngularBrandCodemodOptions>;
|
|
81
137
|
|
|
82
138
|
type InjectDecoratorTokenResult =
|
|
@@ -170,15 +226,6 @@ export function transformSourceFile(
|
|
|
170
226
|
return result;
|
|
171
227
|
}
|
|
172
228
|
|
|
173
|
-
const exportSafety = getExportRewriteSafety(
|
|
174
|
-
sourceFile,
|
|
175
|
-
classDeclaration,
|
|
176
|
-
className,
|
|
177
|
-
);
|
|
178
|
-
if (!exportSafety.safe) {
|
|
179
|
-
return skip(result, exportSafety.warnings);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
229
|
const importSafety = getHelperImportSafety(
|
|
183
230
|
sourceFile,
|
|
184
231
|
normalizedOptions.helperImportPath,
|
|
@@ -187,11 +234,22 @@ export function transformSourceFile(
|
|
|
187
234
|
return skip(result, importSafety.warnings);
|
|
188
235
|
}
|
|
189
236
|
|
|
190
|
-
|
|
237
|
+
const beforeText = sourceFile.getFullText();
|
|
238
|
+
|
|
239
|
+
migrateLegacyBrandExport(sourceFile, classDeclaration, className);
|
|
191
240
|
ensureHelperImports(sourceFile, normalizedOptions.helperImportPath);
|
|
192
|
-
|
|
241
|
+
ensureGeneratedDependencyTypeImports(
|
|
242
|
+
sourceFile,
|
|
243
|
+
result.generatedDependencyGroups,
|
|
244
|
+
);
|
|
245
|
+
writeGeneratedDepsTypeAlias(
|
|
246
|
+
sourceFile,
|
|
247
|
+
className,
|
|
248
|
+
result.generatedDependencyGroups,
|
|
249
|
+
);
|
|
250
|
+
sourceFile.organizeImports();
|
|
193
251
|
|
|
194
|
-
result.changed =
|
|
252
|
+
result.changed = sourceFile.getFullText() !== beforeText;
|
|
195
253
|
return result;
|
|
196
254
|
}
|
|
197
255
|
|
|
@@ -205,6 +263,7 @@ export function analyzeSourceFileDependencies(
|
|
|
205
263
|
warnings: [],
|
|
206
264
|
dependencies: [],
|
|
207
265
|
dependencyGroups: emptyDependencyGroups(),
|
|
266
|
+
generatedDependencyGroups: emptyGeneratedDependencyGroups(),
|
|
208
267
|
};
|
|
209
268
|
|
|
210
269
|
const angularClass = findAngularDecoratedClass(sourceFile);
|
|
@@ -250,11 +309,21 @@ export function analyzeSourceFileDependencies(
|
|
|
250
309
|
normalizedOptions,
|
|
251
310
|
)
|
|
252
311
|
: emptyMetadataDependencyGroups();
|
|
312
|
+
const providedDeps =
|
|
313
|
+
angularKind === 'component' || angularKind === 'directive'
|
|
314
|
+
? extractProvidedDependencies(
|
|
315
|
+
sourceFile,
|
|
316
|
+
classDeclaration,
|
|
317
|
+
angularKind,
|
|
318
|
+
normalizedOptions,
|
|
319
|
+
)
|
|
320
|
+
: { entries: [], warnings: [] };
|
|
253
321
|
const constructorDeps = extractConstructorDeps(classDeclaration);
|
|
254
322
|
const injectCallDeps = extractInjectCallDeps(classDeclaration);
|
|
255
323
|
|
|
256
324
|
result.warnings.push(
|
|
257
325
|
...metadataDeps.warnings,
|
|
326
|
+
...providedDeps.warnings,
|
|
258
327
|
...constructorDeps.warnings,
|
|
259
328
|
...injectCallDeps.warnings,
|
|
260
329
|
);
|
|
@@ -274,6 +343,19 @@ export function analyzeSourceFileDependencies(
|
|
|
274
343
|
importDeps: mergeDeps(metadataDeps.imports, metadataDeps.hostDirectives),
|
|
275
344
|
providers: mergeDeps(metadataDeps.providers, metadataDeps.viewProviders),
|
|
276
345
|
};
|
|
346
|
+
result.generatedTypeName = getGeneratedDepsTypeName(className);
|
|
347
|
+
result.generatedDependencyGroups = createGeneratedDependencyGroups(
|
|
348
|
+
sourceFile,
|
|
349
|
+
result.dependencyGroups.importDeps,
|
|
350
|
+
[
|
|
351
|
+
...constructorDeps.dependencies.map((dependencyText) => ({
|
|
352
|
+
dependencyText,
|
|
353
|
+
entry: createGeneratedDependencyEntry(sourceFile, dependencyText, 'inject'),
|
|
354
|
+
})),
|
|
355
|
+
...injectCallDeps.generatedDependencies,
|
|
356
|
+
],
|
|
357
|
+
providedDeps.entries,
|
|
358
|
+
);
|
|
277
359
|
|
|
278
360
|
return result;
|
|
279
361
|
}
|
|
@@ -436,9 +518,11 @@ export function extractConstructorDeps(
|
|
|
436
518
|
|
|
437
519
|
export function extractInjectCallDeps(
|
|
438
520
|
classDeclaration: ClassDeclaration,
|
|
439
|
-
):
|
|
521
|
+
): InjectCallDependencyExtractionResult {
|
|
440
522
|
const warnings: string[] = [];
|
|
441
523
|
const dependencies: string[] = [];
|
|
524
|
+
const generatedDependencies: InjectedDependencyDescriptor[] = [];
|
|
525
|
+
const sourceFile = classDeclaration.getSourceFile();
|
|
442
526
|
|
|
443
527
|
for (const callExpression of classDeclaration.getDescendantsOfKind(
|
|
444
528
|
SyntaxKind.CallExpression,
|
|
@@ -462,9 +546,25 @@ export function extractInjectCallDeps(
|
|
|
462
546
|
}
|
|
463
547
|
|
|
464
548
|
dependencies.push(dependency);
|
|
549
|
+
generatedDependencies.push({
|
|
550
|
+
dependencyText: dependency,
|
|
551
|
+
entry:
|
|
552
|
+
injectMethodName === 'inject'
|
|
553
|
+
? createGeneratedDependencyEntry(sourceFile, dependency, 'inject')
|
|
554
|
+
: createGeneratedInjectHelperDependencyEntry(
|
|
555
|
+
sourceFile,
|
|
556
|
+
callExpression,
|
|
557
|
+
expression,
|
|
558
|
+
dependency,
|
|
559
|
+
),
|
|
560
|
+
});
|
|
465
561
|
}
|
|
466
562
|
|
|
467
|
-
return {
|
|
563
|
+
return {
|
|
564
|
+
dependencies: mergeDeps(dependencies),
|
|
565
|
+
warnings,
|
|
566
|
+
generatedDependencies,
|
|
567
|
+
};
|
|
468
568
|
}
|
|
469
569
|
|
|
470
570
|
export function mergeDeps(...dependencyGroups: readonly string[][]): string[] {
|
|
@@ -493,8 +593,651 @@ function emptyDependencyGroups(): DependencyGroups {
|
|
|
493
593
|
};
|
|
494
594
|
}
|
|
495
595
|
|
|
496
|
-
function
|
|
497
|
-
return
|
|
596
|
+
function emptyGeneratedDependencyGroups(): GeneratedDependencyGroups {
|
|
597
|
+
return {
|
|
598
|
+
deps: [],
|
|
599
|
+
provided: [],
|
|
600
|
+
missingProvider: [],
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function getGeneratedDepsTypeName(className: string): string {
|
|
605
|
+
return `GenDeps_${className}`;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
function createGeneratedDependencyGroups(
|
|
609
|
+
sourceFile: SourceFile,
|
|
610
|
+
importDependencies: string[],
|
|
611
|
+
injectedDependencies: InjectedDependencyDescriptor[],
|
|
612
|
+
providedEntries: GeneratedDependencyEntry[],
|
|
613
|
+
): GeneratedDependencyGroups {
|
|
614
|
+
const deps = mergeGeneratedDependencyEntries(
|
|
615
|
+
importDependencies.map((dependency) =>
|
|
616
|
+
createGeneratedDependencyEntry(sourceFile, dependency, 'import'),
|
|
617
|
+
),
|
|
618
|
+
injectedDependencies.map((dependency) => dependency.entry),
|
|
619
|
+
);
|
|
620
|
+
const provided = mergeGeneratedDependencyEntries(providedEntries);
|
|
621
|
+
const providedKeys = new Set(provided.map((entry) => entry.key));
|
|
622
|
+
const injectedEntriesByKey = new Map(
|
|
623
|
+
injectedDependencies.map((dependency) => [dependency.entry.key, dependency.entry]),
|
|
624
|
+
);
|
|
625
|
+
const missingProvider = mergeGeneratedDependencyEntries(
|
|
626
|
+
injectedDependencies
|
|
627
|
+
.filter((dependency) => {
|
|
628
|
+
const dependencyKey = dependency.entry.key;
|
|
629
|
+
return (
|
|
630
|
+
!providedKeys.has(dependencyKey) &&
|
|
631
|
+
!isDependencyProvidedElsewhere(sourceFile, dependency.dependencyText)
|
|
632
|
+
);
|
|
633
|
+
})
|
|
634
|
+
.map((dependency) =>
|
|
635
|
+
injectedEntriesByKey.get(dependency.entry.key) ??
|
|
636
|
+
createGeneratedDependencyEntry(
|
|
637
|
+
sourceFile,
|
|
638
|
+
dependency.dependencyText,
|
|
639
|
+
'inject',
|
|
640
|
+
),
|
|
641
|
+
),
|
|
642
|
+
);
|
|
643
|
+
|
|
644
|
+
return {
|
|
645
|
+
deps,
|
|
646
|
+
provided,
|
|
647
|
+
missingProvider,
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
function mergeGeneratedDependencyEntries(
|
|
652
|
+
...groups: readonly GeneratedDependencyEntry[][]
|
|
653
|
+
): GeneratedDependencyEntry[] {
|
|
654
|
+
const entries = new Map<string, GeneratedDependencyEntry>();
|
|
655
|
+
|
|
656
|
+
for (const group of groups) {
|
|
657
|
+
for (const entry of group) {
|
|
658
|
+
if (!entries.has(entry.key)) {
|
|
659
|
+
entries.set(entry.key, entry);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
return [...entries.values()];
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function createGeneratedDependencyEntry(
|
|
668
|
+
sourceFile: SourceFile,
|
|
669
|
+
dependencyText: string,
|
|
670
|
+
context: 'import' | 'inject',
|
|
671
|
+
): GeneratedDependencyEntry {
|
|
672
|
+
if (context === 'import') {
|
|
673
|
+
const declarableResolution = resolveAngularDeclarableDependency(
|
|
674
|
+
sourceFile,
|
|
675
|
+
dependencyText,
|
|
676
|
+
);
|
|
677
|
+
if (declarableResolution) {
|
|
678
|
+
const generatedTypeName = getGeneratedDepsTypeName(
|
|
679
|
+
declarableResolution.className,
|
|
680
|
+
);
|
|
681
|
+
return {
|
|
682
|
+
key: generatedTypeName,
|
|
683
|
+
typeText: generatedTypeName,
|
|
684
|
+
typeImport: declarableResolution.moduleSpecifier
|
|
685
|
+
? {
|
|
686
|
+
moduleSpecifier: declarableResolution.moduleSpecifier,
|
|
687
|
+
name: generatedTypeName,
|
|
688
|
+
}
|
|
689
|
+
: undefined,
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
return {
|
|
695
|
+
key: createGeneratedDependencyKey(dependencyText),
|
|
696
|
+
typeText: createGeneratedDependencyTypeText(sourceFile, dependencyText),
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
function createGeneratedInjectHelperDependencyEntry(
|
|
701
|
+
sourceFile: SourceFile,
|
|
702
|
+
callExpression: import('ts-morph').CallExpression,
|
|
703
|
+
expression: Node,
|
|
704
|
+
dependencyText: string,
|
|
705
|
+
): GeneratedDependencyEntry {
|
|
706
|
+
const trackedHelper = resolveTrackedInjectHelper(expression);
|
|
707
|
+
if (!trackedHelper) {
|
|
708
|
+
return createGeneratedDependencyEntry(sourceFile, dependencyText, 'inject');
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
const exposureTracking = extractHelperExposureTracking(callExpression);
|
|
712
|
+
|
|
713
|
+
return {
|
|
714
|
+
key: trackedHelper.serviceName,
|
|
715
|
+
typeText: createTrackedInjectHelperTypeText(
|
|
716
|
+
dependencyText,
|
|
717
|
+
exposureTracking,
|
|
718
|
+
),
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function createTrackedInjectHelperTypeText(
|
|
723
|
+
dependencyText: string,
|
|
724
|
+
exposureTracking: HelperExposureTracking | undefined,
|
|
725
|
+
): string {
|
|
726
|
+
const baseType = `GetInjectedServiceDependencies<typeof ${dependencyText}>`;
|
|
727
|
+
if (!exposureTracking) {
|
|
728
|
+
return [baseType + ' & {', ' usesWholeService: true;', '}'].join('\n');
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
return [
|
|
732
|
+
`${baseType} & {`,
|
|
733
|
+
` derivedPropertiesUsed: ${formatHelperExposurePropertiesType(
|
|
734
|
+
dependencyText,
|
|
735
|
+
exposureTracking.usedProperties.map((sourceKey) => ({
|
|
736
|
+
propertyKey: sourceKey,
|
|
737
|
+
sourceKey,
|
|
738
|
+
})),
|
|
739
|
+
)};`,
|
|
740
|
+
` derivedPropertiesExposed: ${formatHelperExposurePropertiesType(
|
|
741
|
+
dependencyText,
|
|
742
|
+
exposureTracking.exposedProperties.map(
|
|
743
|
+
({ exposedKey, sourceKey }) => ({
|
|
744
|
+
propertyKey: exposedKey,
|
|
745
|
+
sourceKey,
|
|
746
|
+
}),
|
|
747
|
+
),
|
|
748
|
+
)};`,
|
|
749
|
+
'}',
|
|
750
|
+
].join('\n');
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
function formatHelperExposurePropertiesType(
|
|
754
|
+
dependencyText: string,
|
|
755
|
+
properties: Array<{ propertyKey: string; sourceKey: string }>,
|
|
756
|
+
): string {
|
|
757
|
+
if (properties.length === 0) {
|
|
758
|
+
return '{}';
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
return [
|
|
762
|
+
'{',
|
|
763
|
+
...properties.map(
|
|
764
|
+
({ propertyKey, sourceKey }) =>
|
|
765
|
+
` ${formatObjectKey(propertyKey)}: ${createTrackedHelperOutputTypeText(
|
|
766
|
+
dependencyText,
|
|
767
|
+
sourceKey,
|
|
768
|
+
)};`,
|
|
769
|
+
),
|
|
770
|
+
' }',
|
|
771
|
+
].join('\n');
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
function createTrackedHelperOutputTypeText(
|
|
775
|
+
dependencyText: string,
|
|
776
|
+
sourceKey: string,
|
|
777
|
+
): string {
|
|
778
|
+
if (sourceKey === '$self') {
|
|
779
|
+
return `GetServiceOutput<typeof ${dependencyText}>`;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
return `GetServiceOutput<typeof ${dependencyText}>[${JSON.stringify(sourceKey)}]`;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
function resolveTrackedInjectHelper(
|
|
786
|
+
expression: Node,
|
|
787
|
+
): TrackedHelperResolution | undefined {
|
|
788
|
+
const symbol = expression.getSymbol() ?? expression.getType().getSymbol();
|
|
789
|
+
const declarations =
|
|
790
|
+
symbol?.getAliasedSymbol()?.getDeclarations() ?? symbol?.getDeclarations() ?? [];
|
|
791
|
+
|
|
792
|
+
for (const declaration of declarations) {
|
|
793
|
+
const helper = resolveTrackedInjectHelperFromDeclaration(declaration);
|
|
794
|
+
if (helper) {
|
|
795
|
+
return helper;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
return undefined;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
function resolveTrackedInjectHelperFromDeclaration(
|
|
803
|
+
declaration: Node,
|
|
804
|
+
): TrackedHelperResolution | undefined {
|
|
805
|
+
if (!Node.isBindingElement(declaration)) {
|
|
806
|
+
return undefined;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
const objectBindingPattern = declaration.getFirstAncestorByKind(
|
|
810
|
+
SyntaxKind.ObjectBindingPattern,
|
|
811
|
+
);
|
|
812
|
+
const variableDeclaration =
|
|
813
|
+
objectBindingPattern?.getFirstAncestorByKind(SyntaxKind.VariableDeclaration);
|
|
814
|
+
const initializer = variableDeclaration?.getInitializer();
|
|
815
|
+
if (!Node.isCallExpression(initializer) || !isServiceFactoryCall(initializer)) {
|
|
816
|
+
return undefined;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
const [optionsArgument] = initializer.getArguments();
|
|
820
|
+
if (!Node.isObjectLiteralExpression(optionsArgument)) {
|
|
821
|
+
return undefined;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
const nameInitializer = getObjectPropertyAssignment(
|
|
825
|
+
optionsArgument,
|
|
826
|
+
'name',
|
|
827
|
+
)?.getInitializer();
|
|
828
|
+
const scopeInitializer = getObjectPropertyAssignment(
|
|
829
|
+
optionsArgument,
|
|
830
|
+
'scope',
|
|
831
|
+
)?.getInitializer();
|
|
832
|
+
|
|
833
|
+
if (
|
|
834
|
+
!Node.isStringLiteral(nameInitializer) ||
|
|
835
|
+
!Node.isStringLiteral(scopeInitializer)
|
|
836
|
+
) {
|
|
837
|
+
return undefined;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
return {
|
|
841
|
+
serviceName: nameInitializer.getLiteralText(),
|
|
842
|
+
scope: scopeInitializer.getLiteralText(),
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
function isServiceFactoryCall(
|
|
847
|
+
callExpression: import('ts-morph').CallExpression,
|
|
848
|
+
): boolean {
|
|
849
|
+
const expression = callExpression.getExpression();
|
|
850
|
+
if (Node.isIdentifier(expression)) {
|
|
851
|
+
return expression.getText() === 'craftService' || expression.getText() === 'toCraftService';
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
if (Node.isPropertyAccessExpression(expression)) {
|
|
855
|
+
const methodName = expression.getName();
|
|
856
|
+
return methodName === 'craftService' || methodName === 'toCraftService';
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
return false;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
function extractHelperExposureTracking(
|
|
863
|
+
callExpression: import('ts-morph').CallExpression,
|
|
864
|
+
): HelperExposureTracking | undefined {
|
|
865
|
+
const exposeArgument = [...callExpression.getArguments()]
|
|
866
|
+
.reverse()
|
|
867
|
+
.find(
|
|
868
|
+
(argument) =>
|
|
869
|
+
Node.isArrowFunction(argument) || Node.isFunctionExpression(argument),
|
|
870
|
+
);
|
|
871
|
+
|
|
872
|
+
if (
|
|
873
|
+
!exposeArgument ||
|
|
874
|
+
(!Node.isArrowFunction(exposeArgument) &&
|
|
875
|
+
!Node.isFunctionExpression(exposeArgument))
|
|
876
|
+
) {
|
|
877
|
+
return undefined;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
const parameter = exposeArgument.getParameters()[0];
|
|
881
|
+
const nameNode = parameter?.getNameNode();
|
|
882
|
+
if (!nameNode || !Node.isObjectBindingPattern(nameNode)) {
|
|
883
|
+
return undefined;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
const bindings = extractHelperExposureBindings(nameNode);
|
|
887
|
+
if (bindings.length === 0) {
|
|
888
|
+
return undefined;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
const returnObjectLiteral = getExposureReturnObjectLiteral(exposeArgument);
|
|
892
|
+
if (!returnObjectLiteral) {
|
|
893
|
+
return undefined;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
const exposedProperties = extractHelperExposedProperties(
|
|
897
|
+
returnObjectLiteral,
|
|
898
|
+
bindings,
|
|
899
|
+
);
|
|
900
|
+
const usedProperties = mergeDeps(
|
|
901
|
+
exposedProperties.map(({ sourceKey }) => sourceKey),
|
|
902
|
+
extractHelperYieldedProperties(exposeArgument, bindings),
|
|
903
|
+
);
|
|
904
|
+
|
|
905
|
+
return {
|
|
906
|
+
usedProperties,
|
|
907
|
+
exposedProperties,
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
function extractHelperExposureBindings(
|
|
912
|
+
bindingPattern: import('ts-morph').ObjectBindingPattern,
|
|
913
|
+
): HelperExposureBinding[] {
|
|
914
|
+
const bindings: HelperExposureBinding[] = [];
|
|
915
|
+
|
|
916
|
+
for (const element of bindingPattern.getElements()) {
|
|
917
|
+
const nameNode = element.getNameNode();
|
|
918
|
+
if (!Node.isIdentifier(nameNode)) {
|
|
919
|
+
continue;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
const propertyNameNode = element.getPropertyNameNode();
|
|
923
|
+
const sourceKey =
|
|
924
|
+
getStaticPropertyName(propertyNameNode ?? nameNode) ?? nameNode.getText();
|
|
925
|
+
|
|
926
|
+
bindings.push({
|
|
927
|
+
localName: nameNode.getText(),
|
|
928
|
+
sourceKey,
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
return bindings;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
function getExposureReturnObjectLiteral(
|
|
936
|
+
exposeArgument: import('ts-morph').ArrowFunction | import('ts-morph').FunctionExpression,
|
|
937
|
+
): ObjectLiteralExpression | undefined {
|
|
938
|
+
const body = exposeArgument.getBody();
|
|
939
|
+
if (Node.isObjectLiteralExpression(body)) {
|
|
940
|
+
return body;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
if (Node.isParenthesizedExpression(body)) {
|
|
944
|
+
const expression = body.getExpression();
|
|
945
|
+
return Node.isObjectLiteralExpression(expression) ? expression : undefined;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
if (!Node.isBlock(body)) {
|
|
949
|
+
return undefined;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
const returnStatement = [...body.getDescendantsOfKind(SyntaxKind.ReturnStatement)]
|
|
953
|
+
.reverse()
|
|
954
|
+
.find((statement) => Node.isObjectLiteralExpression(statement.getExpression()));
|
|
955
|
+
|
|
956
|
+
const expression = returnStatement?.getExpression();
|
|
957
|
+
return Node.isObjectLiteralExpression(expression) ? expression : undefined;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
function extractHelperExposedProperties(
|
|
961
|
+
objectLiteral: ObjectLiteralExpression,
|
|
962
|
+
bindings: HelperExposureBinding[],
|
|
963
|
+
): Array<{ exposedKey: string; sourceKey: string }> {
|
|
964
|
+
const bindingMap = new Map(
|
|
965
|
+
bindings.map((binding) => [binding.localName, binding.sourceKey]),
|
|
966
|
+
);
|
|
967
|
+
const exposedProperties: Array<{ exposedKey: string; sourceKey: string }> = [];
|
|
968
|
+
|
|
969
|
+
for (const property of objectLiteral.getProperties()) {
|
|
970
|
+
if (Node.isShorthandPropertyAssignment(property)) {
|
|
971
|
+
const sourceKey = bindingMap.get(property.getName());
|
|
972
|
+
if (sourceKey) {
|
|
973
|
+
exposedProperties.push({
|
|
974
|
+
exposedKey: property.getName(),
|
|
975
|
+
sourceKey,
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
continue;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
if (!Node.isPropertyAssignment(property)) {
|
|
982
|
+
continue;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
const exposedKey = getStaticPropertyName(property.getNameNode());
|
|
986
|
+
const initializer = property.getInitializer();
|
|
987
|
+
if (!exposedKey || !Node.isIdentifier(initializer)) {
|
|
988
|
+
continue;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
const sourceKey = bindingMap.get(initializer.getText());
|
|
992
|
+
if (!sourceKey) {
|
|
993
|
+
continue;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
exposedProperties.push({
|
|
997
|
+
exposedKey,
|
|
998
|
+
sourceKey,
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
return exposedProperties;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
function extractHelperYieldedProperties(
|
|
1006
|
+
exposeArgument: import('ts-morph').ArrowFunction | import('ts-morph').FunctionExpression,
|
|
1007
|
+
bindings: HelperExposureBinding[],
|
|
1008
|
+
): string[] {
|
|
1009
|
+
const bindingMap = new Map(
|
|
1010
|
+
bindings.map((binding) => [binding.localName, binding.sourceKey]),
|
|
1011
|
+
);
|
|
1012
|
+
const yieldedProperties: string[] = [];
|
|
1013
|
+
|
|
1014
|
+
for (const yieldExpression of exposeArgument.getDescendantsOfKind(
|
|
1015
|
+
SyntaxKind.YieldExpression,
|
|
1016
|
+
)) {
|
|
1017
|
+
const expression = yieldExpression.getExpression();
|
|
1018
|
+
if (!Node.isCallExpression(expression)) {
|
|
1019
|
+
continue;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
const target = expression.getExpression();
|
|
1023
|
+
if (!Node.isIdentifier(target)) {
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
const sourceKey = bindingMap.get(target.getText());
|
|
1028
|
+
if (sourceKey) {
|
|
1029
|
+
yieldedProperties.push(sourceKey);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
return yieldedProperties;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
function createGeneratedDependencyKey(dependencyText: string): string {
|
|
1037
|
+
const lastSegment = dependencyText.split('.').pop() ?? dependencyText;
|
|
1038
|
+
const helperMatch = /^(inject|provide)([A-Z].*)$/.exec(lastSegment);
|
|
1039
|
+
if (helperMatch) {
|
|
1040
|
+
return helperMatch[2];
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
const toYieldMatch = /^(.*)ToYield$/.exec(lastSegment);
|
|
1044
|
+
if (toYieldMatch?.[1]) {
|
|
1045
|
+
return toYieldMatch[1];
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
return lastSegment;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
function createGeneratedDependencyTypeText(
|
|
1052
|
+
sourceFile: SourceFile,
|
|
1053
|
+
dependencyText: string,
|
|
1054
|
+
): string {
|
|
1055
|
+
if (isHelperLikeDependency(dependencyText)) {
|
|
1056
|
+
return `ReturnType<typeof ${dependencyText}>`;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
const resolution = resolveDependencyReference(sourceFile, dependencyText);
|
|
1060
|
+
if (resolution.kind === 'class' || resolution.kind === 'enum') {
|
|
1061
|
+
return dependencyText;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
if (resolution.kind === 'unknown') {
|
|
1065
|
+
return dependencyText;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
return `typeof ${dependencyText}`;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
function isHelperLikeDependency(dependencyText: string): boolean {
|
|
1072
|
+
const lastSegment = dependencyText.split('.').pop() ?? dependencyText;
|
|
1073
|
+
return (
|
|
1074
|
+
/^(inject|provide)[A-Z].*/.test(lastSegment) || /ToYield$/.test(lastSegment)
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
function resolveAngularDeclarableDependency(
|
|
1079
|
+
sourceFile: SourceFile,
|
|
1080
|
+
dependencyText: string,
|
|
1081
|
+
): { className: string; moduleSpecifier?: string } | undefined {
|
|
1082
|
+
const resolution = resolveDependencyReference(sourceFile, dependencyText);
|
|
1083
|
+
if (!resolution.classDeclaration) {
|
|
1084
|
+
return undefined;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
const angularKind = getAngularKind(resolution.classDeclaration);
|
|
1088
|
+
if (
|
|
1089
|
+
angularKind !== 'component' &&
|
|
1090
|
+
angularKind !== 'directive' &&
|
|
1091
|
+
angularKind !== 'pipe'
|
|
1092
|
+
) {
|
|
1093
|
+
return undefined;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
const className = resolution.classDeclaration.getName();
|
|
1097
|
+
if (!className) {
|
|
1098
|
+
return undefined;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
const declarationSourceFile = resolution.classDeclaration.getSourceFile();
|
|
1102
|
+
if (
|
|
1103
|
+
declarationSourceFile === sourceFile ||
|
|
1104
|
+
declarationSourceFile.isDeclarationFile()
|
|
1105
|
+
) {
|
|
1106
|
+
return undefined;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
return {
|
|
1110
|
+
className,
|
|
1111
|
+
moduleSpecifier: resolution.moduleSpecifier,
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
function ensureGeneratedDependencyTypeImports(
|
|
1116
|
+
sourceFile: SourceFile,
|
|
1117
|
+
generatedDependencyGroups: GeneratedDependencyGroups,
|
|
1118
|
+
): void {
|
|
1119
|
+
const importsToAdd = new Map<string, Set<string>>();
|
|
1120
|
+
|
|
1121
|
+
for (const entry of generatedDependencyGroups.deps) {
|
|
1122
|
+
if (!entry.typeImport) {
|
|
1123
|
+
continue;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
const importNames =
|
|
1127
|
+
importsToAdd.get(entry.typeImport.moduleSpecifier) ?? new Set<string>();
|
|
1128
|
+
importNames.add(entry.typeImport.name);
|
|
1129
|
+
importsToAdd.set(entry.typeImport.moduleSpecifier, importNames);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
for (const [moduleSpecifier, importNames] of importsToAdd) {
|
|
1133
|
+
const existingImport = sourceFile
|
|
1134
|
+
.getImportDeclarations()
|
|
1135
|
+
.find(
|
|
1136
|
+
(importDeclaration) =>
|
|
1137
|
+
importDeclaration.getModuleSpecifierValue() === moduleSpecifier,
|
|
1138
|
+
);
|
|
1139
|
+
|
|
1140
|
+
if (existingImport) {
|
|
1141
|
+
existingImport.addNamedImports(
|
|
1142
|
+
[...importNames].map((name) => ({ name, isTypeOnly: true })),
|
|
1143
|
+
);
|
|
1144
|
+
continue;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
sourceFile.addImportDeclaration({
|
|
1148
|
+
moduleSpecifier,
|
|
1149
|
+
namedImports: [...importNames].map((name) => ({
|
|
1150
|
+
name,
|
|
1151
|
+
isTypeOnly: true,
|
|
1152
|
+
})),
|
|
1153
|
+
});
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
function formatGeneratedDependencyType(
|
|
1158
|
+
generatedDependencyGroups: GeneratedDependencyGroups,
|
|
1159
|
+
): string {
|
|
1160
|
+
return [
|
|
1161
|
+
'{',
|
|
1162
|
+
` deps: ${formatGeneratedDependencyObject(generatedDependencyGroups.deps)};`,
|
|
1163
|
+
` provided: ${formatGeneratedDependencyObject(generatedDependencyGroups.provided)};`,
|
|
1164
|
+
` missingProvider: ${formatGeneratedDependencyObject(generatedDependencyGroups.missingProvider)};`,
|
|
1165
|
+
'}',
|
|
1166
|
+
].join('\n');
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
function formatGeneratedDependencyObject(
|
|
1170
|
+
entries: GeneratedDependencyEntry[],
|
|
1171
|
+
): string {
|
|
1172
|
+
if (entries.length === 0) {
|
|
1173
|
+
return '{}';
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
return [
|
|
1177
|
+
'{',
|
|
1178
|
+
...entries.map((entry) => formatGeneratedDependencyEntry(entry)),
|
|
1179
|
+
' }',
|
|
1180
|
+
].join('\n');
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
function formatGeneratedDependencyEntry(entry: GeneratedDependencyEntry): string {
|
|
1184
|
+
const typeLines = entry.typeText.split('\n');
|
|
1185
|
+
const lines = [
|
|
1186
|
+
` ${formatObjectKey(entry.key)}: ${typeLines[0]}`,
|
|
1187
|
+
...typeLines.slice(1).map((line) => ` ${line}`),
|
|
1188
|
+
];
|
|
1189
|
+
lines[lines.length - 1] += ';';
|
|
1190
|
+
return lines.join('\n');
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
function formatObjectKey(key: string): string {
|
|
1194
|
+
return /^[$A-Z_][0-9A-Z_$]*$/i.test(key) ? key : `'${key}'`;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
export function writeGeneratedDepsTypeAlias(
|
|
1198
|
+
sourceFile: SourceFile,
|
|
1199
|
+
className: string,
|
|
1200
|
+
generatedDependencyGroups: GeneratedDependencyGroups,
|
|
1201
|
+
): void {
|
|
1202
|
+
const generatedTypeName = getGeneratedDepsTypeName(className);
|
|
1203
|
+
const generatedType = `GetDeps<${formatGeneratedDependencyType(generatedDependencyGroups)}>`;
|
|
1204
|
+
const existingTypeAlias = sourceFile.getTypeAlias(generatedTypeName);
|
|
1205
|
+
|
|
1206
|
+
if (existingTypeAlias) {
|
|
1207
|
+
existingTypeAlias.setIsExported(true);
|
|
1208
|
+
existingTypeAlias.setType(generatedType);
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
sourceFile.addTypeAlias({
|
|
1213
|
+
isExported: true,
|
|
1214
|
+
name: generatedTypeName,
|
|
1215
|
+
type: generatedType,
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
export function migrateLegacyBrandExport(
|
|
1220
|
+
sourceFile: SourceFile,
|
|
1221
|
+
classDeclaration: ClassDeclaration,
|
|
1222
|
+
className: string,
|
|
1223
|
+
): void {
|
|
1224
|
+
const existing = readExistingDependencyGroups(sourceFile, className);
|
|
1225
|
+
if (!existing.found || !existing.exportAssignmentNode) {
|
|
1226
|
+
return;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
const exportAssignment = existing.exportAssignmentNode.asKind(
|
|
1230
|
+
SyntaxKind.ExportAssignment,
|
|
1231
|
+
);
|
|
1232
|
+
exportAssignment?.remove();
|
|
1233
|
+
|
|
1234
|
+
if (
|
|
1235
|
+
!classDeclaration.isDefaultExport() &&
|
|
1236
|
+
!classDeclaration.isExported() &&
|
|
1237
|
+
exportAssignment
|
|
1238
|
+
) {
|
|
1239
|
+
classDeclaration.setIsDefaultExport(true);
|
|
1240
|
+
}
|
|
498
1241
|
}
|
|
499
1242
|
|
|
500
1243
|
function invalidExistingDependencyGroups(
|
|
@@ -585,68 +1328,39 @@ export function ensureHelperImports(
|
|
|
585
1328
|
sourceFile: SourceFile,
|
|
586
1329
|
helperImportPath: string,
|
|
587
1330
|
): void {
|
|
588
|
-
const missingImports = [
|
|
1331
|
+
const missingImports = [
|
|
1332
|
+
'GetDeps',
|
|
1333
|
+
'GetInjectedServiceDependencies',
|
|
1334
|
+
'GetServiceOutput',
|
|
1335
|
+
].filter(
|
|
589
1336
|
(importName) =>
|
|
590
1337
|
!hasLocalNamedImport(sourceFile, helperImportPath, importName),
|
|
591
1338
|
);
|
|
592
1339
|
|
|
593
|
-
if (missingImports.length === 0) {
|
|
594
|
-
return;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
const existingImport = sourceFile
|
|
598
|
-
.getImportDeclarations()
|
|
599
|
-
.find(
|
|
600
|
-
(importDeclaration) =>
|
|
601
|
-
importDeclaration.getModuleSpecifierValue() === helperImportPath,
|
|
602
|
-
);
|
|
603
|
-
|
|
604
|
-
if (existingImport) {
|
|
605
|
-
existingImport.addNamedImports(missingImports);
|
|
606
|
-
return;
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
sourceFile.addImportDeclaration({
|
|
610
|
-
moduleSpecifier: helperImportPath,
|
|
611
|
-
namedImports: missingImports,
|
|
612
|
-
});
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
export function removeConflictingExports(
|
|
616
|
-
sourceFile: SourceFile,
|
|
617
|
-
classDeclaration: ClassDeclaration,
|
|
618
|
-
className: string,
|
|
619
|
-
): void {
|
|
620
|
-
if (classDeclaration.isDefaultExport()) {
|
|
621
|
-
classDeclaration.setIsDefaultExport(false);
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
if (classDeclaration.isExported()) {
|
|
625
|
-
classDeclaration.setIsExported(false);
|
|
1340
|
+
if (missingImports.length === 0) {
|
|
1341
|
+
return;
|
|
626
1342
|
}
|
|
627
1343
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
}
|
|
635
|
-
}
|
|
1344
|
+
const existingImport = sourceFile
|
|
1345
|
+
.getImportDeclarations()
|
|
1346
|
+
.find(
|
|
1347
|
+
(importDeclaration) =>
|
|
1348
|
+
importDeclaration.getModuleSpecifierValue() === helperImportPath,
|
|
1349
|
+
);
|
|
636
1350
|
|
|
637
|
-
|
|
638
|
-
|
|
1351
|
+
if (existingImport) {
|
|
1352
|
+
existingImport.addNamedImports(
|
|
1353
|
+
missingImports.map((name) => ({ name, isTypeOnly: true })),
|
|
1354
|
+
);
|
|
1355
|
+
return;
|
|
639
1356
|
}
|
|
640
|
-
}
|
|
641
1357
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
expression: `brandAngularSymbol(${className}, ${createDepsExpression(dependencyGroups)})`,
|
|
649
|
-
isExportEquals: false,
|
|
1358
|
+
sourceFile.addImportDeclaration({
|
|
1359
|
+
moduleSpecifier: helperImportPath,
|
|
1360
|
+
namedImports: missingImports.map((name) => ({
|
|
1361
|
+
name,
|
|
1362
|
+
isTypeOnly: true,
|
|
1363
|
+
})),
|
|
650
1364
|
});
|
|
651
1365
|
}
|
|
652
1366
|
|
|
@@ -996,6 +1710,204 @@ function emptyMetadataDependencyGroups(): MetadataDependencyGroups {
|
|
|
996
1710
|
};
|
|
997
1711
|
}
|
|
998
1712
|
|
|
1713
|
+
function extractProvidedDependencies(
|
|
1714
|
+
sourceFile: SourceFile,
|
|
1715
|
+
classDeclaration: ClassDeclaration,
|
|
1716
|
+
angularKind: AngularKind,
|
|
1717
|
+
options: NormalizedOptions,
|
|
1718
|
+
): ProvidedDependencyExtractionResult {
|
|
1719
|
+
const metadata = getDecoratorMetadataObject(classDeclaration);
|
|
1720
|
+
const warnings: string[] = [];
|
|
1721
|
+
const entries: GeneratedDependencyEntry[] = [];
|
|
1722
|
+
|
|
1723
|
+
if (!metadata) {
|
|
1724
|
+
return { entries, warnings };
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
if (options.includeProviders) {
|
|
1728
|
+
entries.push(
|
|
1729
|
+
...extractProvidedDependencyArrayProperty(
|
|
1730
|
+
sourceFile,
|
|
1731
|
+
metadata,
|
|
1732
|
+
'providers',
|
|
1733
|
+
warnings,
|
|
1734
|
+
),
|
|
1735
|
+
);
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
if (options.includeViewProviders && angularKind === 'component') {
|
|
1739
|
+
entries.push(
|
|
1740
|
+
...extractProvidedDependencyArrayProperty(
|
|
1741
|
+
sourceFile,
|
|
1742
|
+
metadata,
|
|
1743
|
+
'viewProviders',
|
|
1744
|
+
warnings,
|
|
1745
|
+
),
|
|
1746
|
+
);
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
return {
|
|
1750
|
+
entries: mergeGeneratedDependencyEntries(entries),
|
|
1751
|
+
warnings,
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
function extractProvidedDependencyArrayProperty(
|
|
1756
|
+
sourceFile: SourceFile,
|
|
1757
|
+
metadata: ObjectLiteralExpression,
|
|
1758
|
+
propertyName: 'providers' | 'viewProviders',
|
|
1759
|
+
warnings: string[],
|
|
1760
|
+
): GeneratedDependencyEntry[] {
|
|
1761
|
+
const property = getObjectPropertyAssignment(metadata, propertyName);
|
|
1762
|
+
if (!property) {
|
|
1763
|
+
return [];
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
const initializer = property.getInitializer();
|
|
1767
|
+
if (!Node.isArrayLiteralExpression(initializer)) {
|
|
1768
|
+
warnings.push(
|
|
1769
|
+
`Skipped metadata property "${propertyName}" because it is not a static array.`,
|
|
1770
|
+
);
|
|
1771
|
+
return [];
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
return extractProvidedDependencyEntriesFromArray(
|
|
1775
|
+
sourceFile,
|
|
1776
|
+
initializer,
|
|
1777
|
+
propertyName,
|
|
1778
|
+
warnings,
|
|
1779
|
+
);
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
function extractProvidedDependencyEntriesFromArray(
|
|
1783
|
+
sourceFile: SourceFile,
|
|
1784
|
+
arrayLiteral: ArrayLiteralExpression,
|
|
1785
|
+
context: 'providers' | 'viewProviders',
|
|
1786
|
+
warnings: string[],
|
|
1787
|
+
): GeneratedDependencyEntry[] {
|
|
1788
|
+
const entries: GeneratedDependencyEntry[] = [];
|
|
1789
|
+
|
|
1790
|
+
for (const element of arrayLiteral.getElements()) {
|
|
1791
|
+
if (Node.isArrayLiteralExpression(element)) {
|
|
1792
|
+
entries.push(
|
|
1793
|
+
...extractProvidedDependencyEntriesFromArray(
|
|
1794
|
+
sourceFile,
|
|
1795
|
+
element,
|
|
1796
|
+
context,
|
|
1797
|
+
warnings,
|
|
1798
|
+
),
|
|
1799
|
+
);
|
|
1800
|
+
continue;
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
if (Node.isSpreadElement(element)) {
|
|
1804
|
+
warnings.push(
|
|
1805
|
+
`Skipped spread element in "${context}" metadata providers.`,
|
|
1806
|
+
);
|
|
1807
|
+
continue;
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
if (Node.isCallExpression(element)) {
|
|
1811
|
+
const providerFactory = getStaticExpressionText(element.getExpression());
|
|
1812
|
+
if (!providerFactory) {
|
|
1813
|
+
warnings.push(
|
|
1814
|
+
`Skipped complex provider factory "${element.getText()}" in "${context}".`,
|
|
1815
|
+
);
|
|
1816
|
+
continue;
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
entries.push({
|
|
1820
|
+
key: createGeneratedDependencyKey(providerFactory),
|
|
1821
|
+
typeText: `ReturnType<typeof ${providerFactory}>`,
|
|
1822
|
+
});
|
|
1823
|
+
continue;
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
const staticExpression = getStaticExpressionText(element);
|
|
1827
|
+
if (staticExpression) {
|
|
1828
|
+
entries.push({
|
|
1829
|
+
key: createGeneratedDependencyKey(staticExpression),
|
|
1830
|
+
typeText: createGeneratedDependencyTypeText(
|
|
1831
|
+
sourceFile,
|
|
1832
|
+
staticExpression,
|
|
1833
|
+
),
|
|
1834
|
+
});
|
|
1835
|
+
continue;
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
if (Node.isObjectLiteralExpression(element)) {
|
|
1839
|
+
const providerEntry = extractProvidedDependencyEntryFromObjectLiteral(
|
|
1840
|
+
sourceFile,
|
|
1841
|
+
element,
|
|
1842
|
+
context,
|
|
1843
|
+
warnings,
|
|
1844
|
+
);
|
|
1845
|
+
if (providerEntry) {
|
|
1846
|
+
entries.push(providerEntry);
|
|
1847
|
+
}
|
|
1848
|
+
continue;
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
warnings.push(
|
|
1852
|
+
`Skipped complex expression "${element.getText()}" in "${context}" metadata providers.`,
|
|
1853
|
+
);
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
return entries;
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
function extractProvidedDependencyEntryFromObjectLiteral(
|
|
1860
|
+
sourceFile: SourceFile,
|
|
1861
|
+
objectLiteral: ObjectLiteralExpression,
|
|
1862
|
+
context: 'providers' | 'viewProviders',
|
|
1863
|
+
warnings: string[],
|
|
1864
|
+
): GeneratedDependencyEntry | undefined {
|
|
1865
|
+
const provideProperty = getObjectPropertyAssignment(objectLiteral, 'provide');
|
|
1866
|
+
const provideText = getStaticExpressionText(
|
|
1867
|
+
provideProperty?.getInitializer(),
|
|
1868
|
+
);
|
|
1869
|
+
|
|
1870
|
+
if (!provideText) {
|
|
1871
|
+
warnings.push(
|
|
1872
|
+
`Skipped provider object in "${context}" because "provide" is not static.`,
|
|
1873
|
+
);
|
|
1874
|
+
return undefined;
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
const useClass = getStaticExpressionText(
|
|
1878
|
+
getObjectPropertyAssignment(objectLiteral, 'useClass')?.getInitializer(),
|
|
1879
|
+
);
|
|
1880
|
+
const useExisting = getStaticExpressionText(
|
|
1881
|
+
getObjectPropertyAssignment(objectLiteral, 'useExisting')?.getInitializer(),
|
|
1882
|
+
);
|
|
1883
|
+
const useValue = getStaticExpressionText(
|
|
1884
|
+
getObjectPropertyAssignment(objectLiteral, 'useValue')?.getInitializer(),
|
|
1885
|
+
);
|
|
1886
|
+
const useFactory = getStaticExpressionText(
|
|
1887
|
+
getObjectPropertyAssignment(objectLiteral, 'useFactory')?.getInitializer(),
|
|
1888
|
+
);
|
|
1889
|
+
|
|
1890
|
+
if (useFactory) {
|
|
1891
|
+
return {
|
|
1892
|
+
key: createGeneratedDependencyKey(provideText),
|
|
1893
|
+
typeText: `ReturnType<typeof ${useFactory}>`,
|
|
1894
|
+
};
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
if (useValue) {
|
|
1898
|
+
return {
|
|
1899
|
+
key: createGeneratedDependencyKey(provideText),
|
|
1900
|
+
typeText: `typeof ${useValue}`,
|
|
1901
|
+
};
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
const valueText = useClass ?? useExisting ?? provideText;
|
|
1905
|
+
return {
|
|
1906
|
+
key: createGeneratedDependencyKey(provideText),
|
|
1907
|
+
typeText: createGeneratedDependencyTypeText(sourceFile, valueText),
|
|
1908
|
+
};
|
|
1909
|
+
}
|
|
1910
|
+
|
|
999
1911
|
function extractMetadataArrayProperty(
|
|
1000
1912
|
metadata: ObjectLiteralExpression,
|
|
1001
1913
|
propertyName: string,
|
|
@@ -1166,6 +2078,268 @@ function getStaticPropertyName(nameNode: Node): string | undefined {
|
|
|
1166
2078
|
return undefined;
|
|
1167
2079
|
}
|
|
1168
2080
|
|
|
2081
|
+
function resolveDependencyReference(
|
|
2082
|
+
sourceFile: SourceFile,
|
|
2083
|
+
dependencyText: string,
|
|
2084
|
+
): DependencyReferenceResolution {
|
|
2085
|
+
const [rootIdentifier] = dependencyText.split('.');
|
|
2086
|
+
if (!rootIdentifier) {
|
|
2087
|
+
return { kind: 'unknown' };
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
const importResolution = resolveImportedDependencyReference(
|
|
2091
|
+
sourceFile,
|
|
2092
|
+
dependencyText,
|
|
2093
|
+
rootIdentifier,
|
|
2094
|
+
);
|
|
2095
|
+
if (importResolution) {
|
|
2096
|
+
return importResolution;
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
const localClass = sourceFile.getClass(rootIdentifier);
|
|
2100
|
+
if (localClass) {
|
|
2101
|
+
return { kind: 'class', classDeclaration: localClass };
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
if (sourceFile.getEnum(rootIdentifier)) {
|
|
2105
|
+
return { kind: 'enum' };
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
if (sourceFile.getFunction(rootIdentifier)) {
|
|
2109
|
+
return { kind: 'function' };
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
if (
|
|
2113
|
+
sourceFile
|
|
2114
|
+
.getVariableDeclarations()
|
|
2115
|
+
.some((declaration) => declaration.getName() === rootIdentifier)
|
|
2116
|
+
) {
|
|
2117
|
+
return { kind: 'variable' };
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
return { kind: 'unknown' };
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
function resolveImportedDependencyReference(
|
|
2124
|
+
sourceFile: SourceFile,
|
|
2125
|
+
dependencyText: string,
|
|
2126
|
+
rootIdentifier: string,
|
|
2127
|
+
): DependencyReferenceResolution | undefined {
|
|
2128
|
+
for (const importDeclaration of sourceFile.getImportDeclarations()) {
|
|
2129
|
+
const moduleSpecifier = importDeclaration.getModuleSpecifierValue();
|
|
2130
|
+
|
|
2131
|
+
if (importDeclaration.getDefaultImport()?.getText() === rootIdentifier) {
|
|
2132
|
+
const declaration = getDefaultImportClassDeclaration(importDeclaration);
|
|
2133
|
+
if (declaration) {
|
|
2134
|
+
return {
|
|
2135
|
+
kind: 'class',
|
|
2136
|
+
classDeclaration: declaration,
|
|
2137
|
+
moduleSpecifier,
|
|
2138
|
+
};
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
return { kind: 'unknown', moduleSpecifier };
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
if (importDeclaration.getNamespaceImport()?.getText() === rootIdentifier) {
|
|
2145
|
+
const declaration = getNamespaceImportClassDeclaration(
|
|
2146
|
+
importDeclaration,
|
|
2147
|
+
dependencyText,
|
|
2148
|
+
);
|
|
2149
|
+
if (declaration) {
|
|
2150
|
+
return {
|
|
2151
|
+
kind: 'class',
|
|
2152
|
+
classDeclaration: declaration,
|
|
2153
|
+
moduleSpecifier,
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
return { kind: 'namespace', moduleSpecifier };
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
const namedImport = importDeclaration
|
|
2161
|
+
.getNamedImports()
|
|
2162
|
+
.find((specifier) => {
|
|
2163
|
+
const localName =
|
|
2164
|
+
specifier.getAliasNode()?.getText() ??
|
|
2165
|
+
specifier.getNameNode().getText();
|
|
2166
|
+
return localName === rootIdentifier;
|
|
2167
|
+
});
|
|
2168
|
+
|
|
2169
|
+
if (!namedImport) {
|
|
2170
|
+
continue;
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
const localIdentifier =
|
|
2174
|
+
namedImport.getAliasNode() ?? namedImport.getNameNode();
|
|
2175
|
+
const symbol =
|
|
2176
|
+
localIdentifier.getSymbol() ?? localIdentifier.getType().getSymbol();
|
|
2177
|
+
const aliasedSymbol = symbol?.getAliasedSymbol() ?? symbol;
|
|
2178
|
+
const declarations = aliasedSymbol?.getDeclarations() ?? [];
|
|
2179
|
+
const classDeclaration = declarations.find((declaration) =>
|
|
2180
|
+
Node.isClassDeclaration(declaration),
|
|
2181
|
+
);
|
|
2182
|
+
|
|
2183
|
+
if (classDeclaration && Node.isClassDeclaration(classDeclaration)) {
|
|
2184
|
+
return {
|
|
2185
|
+
kind: 'class',
|
|
2186
|
+
classDeclaration,
|
|
2187
|
+
moduleSpecifier,
|
|
2188
|
+
};
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2191
|
+
if (
|
|
2192
|
+
declarations.some((declaration) => Node.isEnumDeclaration(declaration))
|
|
2193
|
+
) {
|
|
2194
|
+
return { kind: 'enum', moduleSpecifier };
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
if (
|
|
2198
|
+
declarations.some((declaration) =>
|
|
2199
|
+
Node.isFunctionDeclaration(declaration),
|
|
2200
|
+
)
|
|
2201
|
+
) {
|
|
2202
|
+
return { kind: 'function', moduleSpecifier };
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
if (
|
|
2206
|
+
declarations.some((declaration) =>
|
|
2207
|
+
Node.isVariableDeclaration(declaration),
|
|
2208
|
+
)
|
|
2209
|
+
) {
|
|
2210
|
+
return { kind: 'variable', moduleSpecifier };
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
return { kind: 'unknown', moduleSpecifier };
|
|
2214
|
+
}
|
|
2215
|
+
|
|
2216
|
+
return undefined;
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
function getDefaultImportClassDeclaration(
|
|
2220
|
+
importDeclaration: import('ts-morph').ImportDeclaration,
|
|
2221
|
+
): ClassDeclaration | undefined {
|
|
2222
|
+
const moduleSourceFile = importDeclaration.getModuleSpecifierSourceFile();
|
|
2223
|
+
if (!moduleSourceFile) {
|
|
2224
|
+
return undefined;
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
const defaultExportSymbol = moduleSourceFile.getDefaultExportSymbol();
|
|
2228
|
+
const declarations = defaultExportSymbol?.getDeclarations() ?? [];
|
|
2229
|
+
return declarations.find((declaration) =>
|
|
2230
|
+
Node.isClassDeclaration(declaration),
|
|
2231
|
+
) as ClassDeclaration | undefined;
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
function getNamespaceImportClassDeclaration(
|
|
2235
|
+
importDeclaration: import('ts-morph').ImportDeclaration,
|
|
2236
|
+
dependencyText: string,
|
|
2237
|
+
): ClassDeclaration | undefined {
|
|
2238
|
+
const moduleSourceFile = importDeclaration.getModuleSpecifierSourceFile();
|
|
2239
|
+
if (!moduleSourceFile) {
|
|
2240
|
+
return undefined;
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
const leafIdentifier = dependencyText.split('.').pop();
|
|
2244
|
+
if (!leafIdentifier) {
|
|
2245
|
+
return undefined;
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2248
|
+
const exportedDeclarations =
|
|
2249
|
+
moduleSourceFile.getExportedDeclarations().get(leafIdentifier) ?? [];
|
|
2250
|
+
return exportedDeclarations.find((declaration) =>
|
|
2251
|
+
Node.isClassDeclaration(declaration),
|
|
2252
|
+
) as ClassDeclaration | undefined;
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
function isProvidedInInjectableTree(
|
|
2256
|
+
sourceFile: SourceFile,
|
|
2257
|
+
dependencyText: string,
|
|
2258
|
+
): boolean {
|
|
2259
|
+
const resolution = resolveDependencyReference(sourceFile, dependencyText);
|
|
2260
|
+
const classDeclaration = resolution.classDeclaration;
|
|
2261
|
+
|
|
2262
|
+
if (!classDeclaration || getAngularKind(classDeclaration) !== 'injectable') {
|
|
2263
|
+
return false;
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
const metadata = getDecoratorMetadataObject(classDeclaration);
|
|
2267
|
+
const providedInProperty = metadata
|
|
2268
|
+
? getObjectPropertyAssignment(metadata, 'providedIn')
|
|
2269
|
+
: undefined;
|
|
2270
|
+
const initializer = providedInProperty?.getInitializer();
|
|
2271
|
+
|
|
2272
|
+
if (!initializer) {
|
|
2273
|
+
return false;
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2276
|
+
return (
|
|
2277
|
+
!Node.isNullLiteral(initializer) &&
|
|
2278
|
+
!Node.isFalseLiteral(initializer) &&
|
|
2279
|
+
!(Node.isIdentifier(initializer) && initializer.getText() === 'undefined')
|
|
2280
|
+
);
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
function isDependencyProvidedElsewhere(
|
|
2284
|
+
sourceFile: SourceFile,
|
|
2285
|
+
dependencyText: string,
|
|
2286
|
+
): boolean {
|
|
2287
|
+
const trackedHelper = resolveTrackedInjectHelperByName(sourceFile, dependencyText);
|
|
2288
|
+
if (trackedHelper) {
|
|
2289
|
+
return trackedHelper.scope === 'global' || trackedHelper.scope === 'function';
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
return isProvidedInInjectableTree(sourceFile, dependencyText);
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
function resolveTrackedInjectHelperByName(
|
|
2296
|
+
sourceFile: SourceFile,
|
|
2297
|
+
dependencyText: string,
|
|
2298
|
+
): TrackedHelperResolution | undefined {
|
|
2299
|
+
const [rootIdentifier] = dependencyText.split('.');
|
|
2300
|
+
if (!rootIdentifier) {
|
|
2301
|
+
return undefined;
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
for (const declaration of resolveDependencyIdentifierDeclarations(
|
|
2305
|
+
sourceFile,
|
|
2306
|
+
rootIdentifier,
|
|
2307
|
+
)) {
|
|
2308
|
+
const helper = resolveTrackedInjectHelperFromDeclaration(declaration);
|
|
2309
|
+
if (helper) {
|
|
2310
|
+
return helper;
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2314
|
+
return undefined;
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
function resolveDependencyIdentifierDeclarations(
|
|
2318
|
+
sourceFile: SourceFile,
|
|
2319
|
+
rootIdentifier: string,
|
|
2320
|
+
): Node[] {
|
|
2321
|
+
for (const importDeclaration of sourceFile.getImportDeclarations()) {
|
|
2322
|
+
const namedImport = importDeclaration.getNamedImports().find((specifier) => {
|
|
2323
|
+
const localName =
|
|
2324
|
+
specifier.getAliasNode()?.getText() ?? specifier.getNameNode().getText();
|
|
2325
|
+
return localName === rootIdentifier;
|
|
2326
|
+
});
|
|
2327
|
+
|
|
2328
|
+
if (!namedImport) {
|
|
2329
|
+
continue;
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
const identifier = namedImport.getAliasNode() ?? namedImport.getNameNode();
|
|
2333
|
+
const symbol = identifier.getSymbol() ?? identifier.getType().getSymbol();
|
|
2334
|
+
const aliasedSymbol = symbol?.getAliasedSymbol() ?? symbol;
|
|
2335
|
+
return aliasedSymbol?.getDeclarations() ?? [namedImport];
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
return sourceFile
|
|
2339
|
+
.getDescendantsOfKind(SyntaxKind.BindingElement)
|
|
2340
|
+
.filter((bindingElement) => bindingElement.getName() === rootIdentifier);
|
|
2341
|
+
}
|
|
2342
|
+
|
|
1169
2343
|
function getDependencyTextFromTypeNode(typeNode: TypeNode): string | undefined {
|
|
1170
2344
|
if (isPrimitiveTypeNode(typeNode)) {
|
|
1171
2345
|
return undefined;
|
|
@@ -1415,7 +2589,11 @@ function getHelperImportSafety(
|
|
|
1415
2589
|
sourceFile: SourceFile,
|
|
1416
2590
|
helperImportPath: string,
|
|
1417
2591
|
): { safe: true; warnings: [] } | { safe: false; warnings: string[] } {
|
|
1418
|
-
const warnings = [
|
|
2592
|
+
const warnings = [
|
|
2593
|
+
'GetDeps',
|
|
2594
|
+
'GetInjectedServiceDependencies',
|
|
2595
|
+
'GetServiceOutput',
|
|
2596
|
+
]
|
|
1419
2597
|
.filter((helperName) =>
|
|
1420
2598
|
hasConflictingTopLevelBinding(sourceFile, helperImportPath, helperName),
|
|
1421
2599
|
)
|
|
@@ -1680,9 +2858,9 @@ function logFileResult(
|
|
|
1680
2858
|
`file=${report.filePath}`,
|
|
1681
2859
|
report.angularKind ? `kind=${report.angularKind}` : undefined,
|
|
1682
2860
|
report.className ? `class=${report.className}` : undefined,
|
|
1683
|
-
`
|
|
1684
|
-
`
|
|
1685
|
-
`
|
|
2861
|
+
`deps=[${report.generatedDependencyGroups.deps.map((entry) => entry.key).join(', ')}]`,
|
|
2862
|
+
`provided=[${report.generatedDependencyGroups.provided.map((entry) => entry.key).join(', ')}]`,
|
|
2863
|
+
`missingProvider=[${report.generatedDependencyGroups.missingProvider.map((entry) => entry.key).join(', ')}]`,
|
|
1686
2864
|
`status=${status}`,
|
|
1687
2865
|
].filter(Boolean);
|
|
1688
2866
|
|
|
@@ -1760,17 +2938,22 @@ function printHelpAndExit(): never {
|
|
|
1760
2938
|
Options:
|
|
1761
2939
|
--root <dir> Project root. Defaults to cwd.
|
|
1762
2940
|
--tsconfig <path> tsconfig path. Defaults to <root>/tsconfig.json.
|
|
1763
|
-
--helper-import <path> Import path for
|
|
2941
|
+
--helper-import <path> Import path for GetDeps.
|
|
1764
2942
|
--transform-only-standalone-declarables Only transform standalone components/directives.
|
|
1765
|
-
--no-providers Do not include metadata providers in
|
|
1766
|
-
--no-view-providers Do not include component viewProviders in
|
|
2943
|
+
--no-providers Do not include metadata providers in generated types.
|
|
2944
|
+
--no-view-providers Do not include component viewProviders in generated types.
|
|
1767
2945
|
--dry-run Print results without writing files.
|
|
1768
2946
|
--help Show this help.
|
|
1769
2947
|
`);
|
|
1770
2948
|
process.exit(0);
|
|
1771
2949
|
}
|
|
1772
2950
|
|
|
1773
|
-
if (require.main === module)
|
|
2951
|
+
// Check if this module is being run directly (ESM equivalent of require.main === module)
|
|
2952
|
+
if (
|
|
2953
|
+
import.meta.url === `file://${process.argv[1]}` ||
|
|
2954
|
+
(import.meta.url.startsWith('file:') &&
|
|
2955
|
+
process.argv[1] === fileURLToPath(import.meta.url))
|
|
2956
|
+
) {
|
|
1774
2957
|
runAngularBrandCodemod(parseCliArgs(process.argv.slice(2))).catch(
|
|
1775
2958
|
(error: unknown) => {
|
|
1776
2959
|
console.error(error);
|