@craft-ng/dev-tools 0.1.5 → 0.1.7
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.
|
@@ -100,12 +100,39 @@ type ProvidedDependencyExtractionResult = {
|
|
|
100
100
|
warnings: string[];
|
|
101
101
|
};
|
|
102
102
|
|
|
103
|
+
type InjectedDependencyDescriptor = {
|
|
104
|
+
dependencyText: string;
|
|
105
|
+
entry: GeneratedDependencyEntry;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
type InjectCallDependencyExtractionResult = DependencyExtractionResult & {
|
|
109
|
+
generatedDependencies: InjectedDependencyDescriptor[];
|
|
110
|
+
};
|
|
111
|
+
|
|
103
112
|
type DependencyReferenceResolution = {
|
|
104
113
|
kind: 'class' | 'enum' | 'function' | 'variable' | 'namespace' | 'unknown';
|
|
105
114
|
classDeclaration?: ClassDeclaration;
|
|
106
115
|
moduleSpecifier?: string;
|
|
107
116
|
};
|
|
108
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
|
+
|
|
109
136
|
type NormalizedOptions = Required<AngularBrandCodemodOptions>;
|
|
110
137
|
|
|
111
138
|
type InjectDecoratorTokenResult =
|
|
@@ -320,7 +347,13 @@ export function analyzeSourceFileDependencies(
|
|
|
320
347
|
result.generatedDependencyGroups = createGeneratedDependencyGroups(
|
|
321
348
|
sourceFile,
|
|
322
349
|
result.dependencyGroups.importDeps,
|
|
323
|
-
|
|
350
|
+
[
|
|
351
|
+
...constructorDeps.dependencies.map((dependencyText) => ({
|
|
352
|
+
dependencyText,
|
|
353
|
+
entry: createGeneratedDependencyEntry(sourceFile, dependencyText, 'inject'),
|
|
354
|
+
})),
|
|
355
|
+
...injectCallDeps.generatedDependencies,
|
|
356
|
+
],
|
|
324
357
|
providedDeps.entries,
|
|
325
358
|
);
|
|
326
359
|
|
|
@@ -485,9 +518,11 @@ export function extractConstructorDeps(
|
|
|
485
518
|
|
|
486
519
|
export function extractInjectCallDeps(
|
|
487
520
|
classDeclaration: ClassDeclaration,
|
|
488
|
-
):
|
|
521
|
+
): InjectCallDependencyExtractionResult {
|
|
489
522
|
const warnings: string[] = [];
|
|
490
523
|
const dependencies: string[] = [];
|
|
524
|
+
const generatedDependencies: InjectedDependencyDescriptor[] = [];
|
|
525
|
+
const sourceFile = classDeclaration.getSourceFile();
|
|
491
526
|
|
|
492
527
|
for (const callExpression of classDeclaration.getDescendantsOfKind(
|
|
493
528
|
SyntaxKind.CallExpression,
|
|
@@ -511,9 +546,25 @@ export function extractInjectCallDeps(
|
|
|
511
546
|
}
|
|
512
547
|
|
|
513
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
|
+
});
|
|
514
561
|
}
|
|
515
562
|
|
|
516
|
-
return {
|
|
563
|
+
return {
|
|
564
|
+
dependencies: mergeDeps(dependencies),
|
|
565
|
+
warnings,
|
|
566
|
+
generatedDependencies,
|
|
567
|
+
};
|
|
517
568
|
}
|
|
518
569
|
|
|
519
570
|
export function mergeDeps(...dependencyGroups: readonly string[][]): string[] {
|
|
@@ -557,30 +608,39 @@ function getGeneratedDepsTypeName(className: string): string {
|
|
|
557
608
|
function createGeneratedDependencyGroups(
|
|
558
609
|
sourceFile: SourceFile,
|
|
559
610
|
importDependencies: string[],
|
|
560
|
-
injectedDependencies:
|
|
611
|
+
injectedDependencies: InjectedDependencyDescriptor[],
|
|
561
612
|
providedEntries: GeneratedDependencyEntry[],
|
|
562
613
|
): GeneratedDependencyGroups {
|
|
563
614
|
const deps = mergeGeneratedDependencyEntries(
|
|
564
615
|
importDependencies.map((dependency) =>
|
|
565
616
|
createGeneratedDependencyEntry(sourceFile, dependency, 'import'),
|
|
566
617
|
),
|
|
567
|
-
injectedDependencies.map((dependency) =>
|
|
568
|
-
createGeneratedDependencyEntry(sourceFile, dependency, 'inject'),
|
|
569
|
-
),
|
|
618
|
+
injectedDependencies.map((dependency) => dependency.entry),
|
|
570
619
|
);
|
|
571
620
|
const provided = mergeGeneratedDependencyEntries(providedEntries);
|
|
572
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
|
+
);
|
|
573
625
|
const missingProvider = mergeGeneratedDependencyEntries(
|
|
574
626
|
injectedDependencies
|
|
575
627
|
.filter((dependency) => {
|
|
576
|
-
const dependencyKey =
|
|
628
|
+
const dependencyKey = dependency.entry.key;
|
|
577
629
|
return (
|
|
578
630
|
!providedKeys.has(dependencyKey) &&
|
|
579
|
-
|
|
631
|
+
shouldGenerateLocalMissingProvider(
|
|
632
|
+
sourceFile,
|
|
633
|
+
dependency.dependencyText,
|
|
634
|
+
)
|
|
580
635
|
);
|
|
581
636
|
})
|
|
582
637
|
.map((dependency) =>
|
|
583
|
-
|
|
638
|
+
injectedEntriesByKey.get(dependency.entry.key) ??
|
|
639
|
+
createGeneratedDependencyEntry(
|
|
640
|
+
sourceFile,
|
|
641
|
+
dependency.dependencyText,
|
|
642
|
+
'inject',
|
|
643
|
+
),
|
|
584
644
|
),
|
|
585
645
|
);
|
|
586
646
|
|
|
@@ -640,6 +700,342 @@ function createGeneratedDependencyEntry(
|
|
|
640
700
|
};
|
|
641
701
|
}
|
|
642
702
|
|
|
703
|
+
function createGeneratedInjectHelperDependencyEntry(
|
|
704
|
+
sourceFile: SourceFile,
|
|
705
|
+
callExpression: import('ts-morph').CallExpression,
|
|
706
|
+
expression: Node,
|
|
707
|
+
dependencyText: string,
|
|
708
|
+
): GeneratedDependencyEntry {
|
|
709
|
+
const trackedHelper = resolveTrackedInjectHelper(expression);
|
|
710
|
+
if (!trackedHelper) {
|
|
711
|
+
return createGeneratedDependencyEntry(sourceFile, dependencyText, 'inject');
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
const exposureTracking = extractHelperExposureTracking(callExpression);
|
|
715
|
+
|
|
716
|
+
return {
|
|
717
|
+
key: trackedHelper.serviceName,
|
|
718
|
+
typeText: createTrackedInjectHelperTypeText(
|
|
719
|
+
dependencyText,
|
|
720
|
+
exposureTracking,
|
|
721
|
+
),
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function createTrackedInjectHelperTypeText(
|
|
726
|
+
dependencyText: string,
|
|
727
|
+
exposureTracking: HelperExposureTracking | undefined,
|
|
728
|
+
): string {
|
|
729
|
+
const baseType = `GetInjectedServiceDependencies<typeof ${dependencyText}>`;
|
|
730
|
+
if (!exposureTracking) {
|
|
731
|
+
return baseType;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
return [
|
|
735
|
+
`DerivedService<${baseType}, {`,
|
|
736
|
+
` derivedPropertiesUsed: ${formatHelperExposurePropertiesType(
|
|
737
|
+
dependencyText,
|
|
738
|
+
exposureTracking.usedProperties.map((sourceKey) => ({
|
|
739
|
+
propertyKey: sourceKey,
|
|
740
|
+
sourceKey,
|
|
741
|
+
})),
|
|
742
|
+
)};`,
|
|
743
|
+
` derivedPropertiesExposed: ${formatHelperExposurePropertiesType(
|
|
744
|
+
dependencyText,
|
|
745
|
+
exposureTracking.exposedProperties.map(
|
|
746
|
+
({ exposedKey, sourceKey }) => ({
|
|
747
|
+
propertyKey: exposedKey,
|
|
748
|
+
sourceKey,
|
|
749
|
+
}),
|
|
750
|
+
),
|
|
751
|
+
)};`,
|
|
752
|
+
'}>',
|
|
753
|
+
].join('\n');
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
function formatHelperExposurePropertiesType(
|
|
757
|
+
dependencyText: string,
|
|
758
|
+
properties: Array<{ propertyKey: string; sourceKey: string }>,
|
|
759
|
+
): string {
|
|
760
|
+
if (properties.length === 0) {
|
|
761
|
+
return '{}';
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
return [
|
|
765
|
+
'{',
|
|
766
|
+
...properties.map(
|
|
767
|
+
({ propertyKey, sourceKey }) =>
|
|
768
|
+
` ${formatObjectKey(propertyKey)}: ${createTrackedHelperOutputTypeText(
|
|
769
|
+
dependencyText,
|
|
770
|
+
sourceKey,
|
|
771
|
+
)};`,
|
|
772
|
+
),
|
|
773
|
+
' }',
|
|
774
|
+
].join('\n');
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
function createTrackedHelperOutputTypeText(
|
|
778
|
+
dependencyText: string,
|
|
779
|
+
sourceKey: string,
|
|
780
|
+
): string {
|
|
781
|
+
if (sourceKey === '$self') {
|
|
782
|
+
return `GetServiceOutput<typeof ${dependencyText}>`;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
return `GetServiceOutput<typeof ${dependencyText}>[${JSON.stringify(sourceKey)}]`;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
function resolveTrackedInjectHelper(
|
|
789
|
+
expression: Node,
|
|
790
|
+
): TrackedHelperResolution | undefined {
|
|
791
|
+
const symbol = expression.getSymbol() ?? expression.getType().getSymbol();
|
|
792
|
+
const declarations =
|
|
793
|
+
symbol?.getAliasedSymbol()?.getDeclarations() ?? symbol?.getDeclarations() ?? [];
|
|
794
|
+
|
|
795
|
+
for (const declaration of declarations) {
|
|
796
|
+
const helper = resolveTrackedInjectHelperFromDeclaration(declaration);
|
|
797
|
+
if (helper) {
|
|
798
|
+
return helper;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
return undefined;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
function resolveTrackedInjectHelperFromDeclaration(
|
|
806
|
+
declaration: Node,
|
|
807
|
+
): TrackedHelperResolution | undefined {
|
|
808
|
+
if (!Node.isBindingElement(declaration)) {
|
|
809
|
+
return undefined;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
const objectBindingPattern = declaration.getFirstAncestorByKind(
|
|
813
|
+
SyntaxKind.ObjectBindingPattern,
|
|
814
|
+
);
|
|
815
|
+
const variableDeclaration =
|
|
816
|
+
objectBindingPattern?.getFirstAncestorByKind(SyntaxKind.VariableDeclaration);
|
|
817
|
+
const initializer = variableDeclaration?.getInitializer();
|
|
818
|
+
if (!Node.isCallExpression(initializer) || !isServiceFactoryCall(initializer)) {
|
|
819
|
+
return undefined;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const [optionsArgument] = initializer.getArguments();
|
|
823
|
+
if (!Node.isObjectLiteralExpression(optionsArgument)) {
|
|
824
|
+
return undefined;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
const nameInitializer = getObjectPropertyAssignment(
|
|
828
|
+
optionsArgument,
|
|
829
|
+
'name',
|
|
830
|
+
)?.getInitializer();
|
|
831
|
+
const scopeInitializer = getObjectPropertyAssignment(
|
|
832
|
+
optionsArgument,
|
|
833
|
+
'scope',
|
|
834
|
+
)?.getInitializer();
|
|
835
|
+
|
|
836
|
+
if (
|
|
837
|
+
!Node.isStringLiteral(nameInitializer) ||
|
|
838
|
+
!Node.isStringLiteral(scopeInitializer)
|
|
839
|
+
) {
|
|
840
|
+
return undefined;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
return {
|
|
844
|
+
serviceName: nameInitializer.getLiteralText(),
|
|
845
|
+
scope: scopeInitializer.getLiteralText(),
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function isServiceFactoryCall(
|
|
850
|
+
callExpression: import('ts-morph').CallExpression,
|
|
851
|
+
): boolean {
|
|
852
|
+
const expression = callExpression.getExpression();
|
|
853
|
+
if (Node.isIdentifier(expression)) {
|
|
854
|
+
return expression.getText() === 'craftService' || expression.getText() === 'toCraftService';
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
if (Node.isPropertyAccessExpression(expression)) {
|
|
858
|
+
const methodName = expression.getName();
|
|
859
|
+
return methodName === 'craftService' || methodName === 'toCraftService';
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
return false;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
function extractHelperExposureTracking(
|
|
866
|
+
callExpression: import('ts-morph').CallExpression,
|
|
867
|
+
): HelperExposureTracking | undefined {
|
|
868
|
+
const exposeArgument = [...callExpression.getArguments()]
|
|
869
|
+
.reverse()
|
|
870
|
+
.find(
|
|
871
|
+
(argument) =>
|
|
872
|
+
Node.isArrowFunction(argument) || Node.isFunctionExpression(argument),
|
|
873
|
+
);
|
|
874
|
+
|
|
875
|
+
if (
|
|
876
|
+
!exposeArgument ||
|
|
877
|
+
(!Node.isArrowFunction(exposeArgument) &&
|
|
878
|
+
!Node.isFunctionExpression(exposeArgument))
|
|
879
|
+
) {
|
|
880
|
+
return undefined;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
const parameter = exposeArgument.getParameters()[0];
|
|
884
|
+
const nameNode = parameter?.getNameNode();
|
|
885
|
+
if (!nameNode || !Node.isObjectBindingPattern(nameNode)) {
|
|
886
|
+
return undefined;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
const bindings = extractHelperExposureBindings(nameNode);
|
|
890
|
+
if (bindings.length === 0) {
|
|
891
|
+
return undefined;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
const returnObjectLiteral = getExposureReturnObjectLiteral(exposeArgument);
|
|
895
|
+
if (!returnObjectLiteral) {
|
|
896
|
+
return undefined;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
const exposedProperties = extractHelperExposedProperties(
|
|
900
|
+
returnObjectLiteral,
|
|
901
|
+
bindings,
|
|
902
|
+
);
|
|
903
|
+
const usedProperties = mergeDeps(
|
|
904
|
+
exposedProperties.map(({ sourceKey }) => sourceKey),
|
|
905
|
+
extractHelperYieldedProperties(exposeArgument, bindings),
|
|
906
|
+
);
|
|
907
|
+
|
|
908
|
+
return {
|
|
909
|
+
usedProperties,
|
|
910
|
+
exposedProperties,
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
function extractHelperExposureBindings(
|
|
915
|
+
bindingPattern: import('ts-morph').ObjectBindingPattern,
|
|
916
|
+
): HelperExposureBinding[] {
|
|
917
|
+
const bindings: HelperExposureBinding[] = [];
|
|
918
|
+
|
|
919
|
+
for (const element of bindingPattern.getElements()) {
|
|
920
|
+
const nameNode = element.getNameNode();
|
|
921
|
+
if (!Node.isIdentifier(nameNode)) {
|
|
922
|
+
continue;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
const propertyNameNode = element.getPropertyNameNode();
|
|
926
|
+
const sourceKey =
|
|
927
|
+
getStaticPropertyName(propertyNameNode ?? nameNode) ?? nameNode.getText();
|
|
928
|
+
|
|
929
|
+
bindings.push({
|
|
930
|
+
localName: nameNode.getText(),
|
|
931
|
+
sourceKey,
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
return bindings;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
function getExposureReturnObjectLiteral(
|
|
939
|
+
exposeArgument: import('ts-morph').ArrowFunction | import('ts-morph').FunctionExpression,
|
|
940
|
+
): ObjectLiteralExpression | undefined {
|
|
941
|
+
const body = exposeArgument.getBody();
|
|
942
|
+
if (Node.isObjectLiteralExpression(body)) {
|
|
943
|
+
return body;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
if (Node.isParenthesizedExpression(body)) {
|
|
947
|
+
const expression = body.getExpression();
|
|
948
|
+
return Node.isObjectLiteralExpression(expression) ? expression : undefined;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
if (!Node.isBlock(body)) {
|
|
952
|
+
return undefined;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
const returnStatement = [...body.getDescendantsOfKind(SyntaxKind.ReturnStatement)]
|
|
956
|
+
.reverse()
|
|
957
|
+
.find((statement) => Node.isObjectLiteralExpression(statement.getExpression()));
|
|
958
|
+
|
|
959
|
+
const expression = returnStatement?.getExpression();
|
|
960
|
+
return Node.isObjectLiteralExpression(expression) ? expression : undefined;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
function extractHelperExposedProperties(
|
|
964
|
+
objectLiteral: ObjectLiteralExpression,
|
|
965
|
+
bindings: HelperExposureBinding[],
|
|
966
|
+
): Array<{ exposedKey: string; sourceKey: string }> {
|
|
967
|
+
const bindingMap = new Map(
|
|
968
|
+
bindings.map((binding) => [binding.localName, binding.sourceKey]),
|
|
969
|
+
);
|
|
970
|
+
const exposedProperties: Array<{ exposedKey: string; sourceKey: string }> = [];
|
|
971
|
+
|
|
972
|
+
for (const property of objectLiteral.getProperties()) {
|
|
973
|
+
if (Node.isShorthandPropertyAssignment(property)) {
|
|
974
|
+
const sourceKey = bindingMap.get(property.getName());
|
|
975
|
+
if (sourceKey) {
|
|
976
|
+
exposedProperties.push({
|
|
977
|
+
exposedKey: property.getName(),
|
|
978
|
+
sourceKey,
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
if (!Node.isPropertyAssignment(property)) {
|
|
985
|
+
continue;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
const exposedKey = getStaticPropertyName(property.getNameNode());
|
|
989
|
+
const initializer = property.getInitializer();
|
|
990
|
+
if (!exposedKey || !Node.isIdentifier(initializer)) {
|
|
991
|
+
continue;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
const sourceKey = bindingMap.get(initializer.getText());
|
|
995
|
+
if (!sourceKey) {
|
|
996
|
+
continue;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
exposedProperties.push({
|
|
1000
|
+
exposedKey,
|
|
1001
|
+
sourceKey,
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
return exposedProperties;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
function extractHelperYieldedProperties(
|
|
1009
|
+
exposeArgument: import('ts-morph').ArrowFunction | import('ts-morph').FunctionExpression,
|
|
1010
|
+
bindings: HelperExposureBinding[],
|
|
1011
|
+
): string[] {
|
|
1012
|
+
const bindingMap = new Map(
|
|
1013
|
+
bindings.map((binding) => [binding.localName, binding.sourceKey]),
|
|
1014
|
+
);
|
|
1015
|
+
const yieldedProperties: string[] = [];
|
|
1016
|
+
|
|
1017
|
+
for (const yieldExpression of exposeArgument.getDescendantsOfKind(
|
|
1018
|
+
SyntaxKind.YieldExpression,
|
|
1019
|
+
)) {
|
|
1020
|
+
const expression = yieldExpression.getExpression();
|
|
1021
|
+
if (!Node.isCallExpression(expression)) {
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
const target = expression.getExpression();
|
|
1026
|
+
if (!Node.isIdentifier(target)) {
|
|
1027
|
+
continue;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
const sourceKey = bindingMap.get(target.getText());
|
|
1031
|
+
if (sourceKey) {
|
|
1032
|
+
yieldedProperties.push(sourceKey);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
return yieldedProperties;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
643
1039
|
function createGeneratedDependencyKey(dependencyText: string): string {
|
|
644
1040
|
const lastSegment = dependencyText.split('.').pop() ?? dependencyText;
|
|
645
1041
|
const helperMatch = /^(inject|provide)([A-Z].*)$/.exec(lastSegment);
|
|
@@ -768,7 +1164,13 @@ function formatGeneratedDependencyType(
|
|
|
768
1164
|
'{',
|
|
769
1165
|
` deps: ${formatGeneratedDependencyObject(generatedDependencyGroups.deps)};`,
|
|
770
1166
|
` provided: ${formatGeneratedDependencyObject(generatedDependencyGroups.provided)};`,
|
|
771
|
-
|
|
1167
|
+
...(generatedDependencyGroups.missingProvider.length > 0
|
|
1168
|
+
? [
|
|
1169
|
+
` missingProvider: ${formatGeneratedDependencyObject(
|
|
1170
|
+
generatedDependencyGroups.missingProvider,
|
|
1171
|
+
)};`,
|
|
1172
|
+
]
|
|
1173
|
+
: []),
|
|
772
1174
|
'}',
|
|
773
1175
|
].join('\n');
|
|
774
1176
|
}
|
|
@@ -782,13 +1184,21 @@ function formatGeneratedDependencyObject(
|
|
|
782
1184
|
|
|
783
1185
|
return [
|
|
784
1186
|
'{',
|
|
785
|
-
...entries.map(
|
|
786
|
-
(entry) => ` ${formatObjectKey(entry.key)}: ${entry.typeText};`,
|
|
787
|
-
),
|
|
1187
|
+
...entries.map((entry) => formatGeneratedDependencyEntry(entry)),
|
|
788
1188
|
' }',
|
|
789
1189
|
].join('\n');
|
|
790
1190
|
}
|
|
791
1191
|
|
|
1192
|
+
function formatGeneratedDependencyEntry(entry: GeneratedDependencyEntry): string {
|
|
1193
|
+
const typeLines = entry.typeText.split('\n');
|
|
1194
|
+
const lines = [
|
|
1195
|
+
` ${formatObjectKey(entry.key)}: ${typeLines[0]}`,
|
|
1196
|
+
...typeLines.slice(1).map((line) => ` ${line}`),
|
|
1197
|
+
];
|
|
1198
|
+
lines[lines.length - 1] += ';';
|
|
1199
|
+
return lines.join('\n');
|
|
1200
|
+
}
|
|
1201
|
+
|
|
792
1202
|
function formatObjectKey(key: string): string {
|
|
793
1203
|
return /^[$A-Z_][0-9A-Z_$]*$/i.test(key) ? key : `'${key}'`;
|
|
794
1204
|
}
|
|
@@ -927,7 +1337,12 @@ export function ensureHelperImports(
|
|
|
927
1337
|
sourceFile: SourceFile,
|
|
928
1338
|
helperImportPath: string,
|
|
929
1339
|
): void {
|
|
930
|
-
const missingImports = [
|
|
1340
|
+
const missingImports = [
|
|
1341
|
+
'DerivedService',
|
|
1342
|
+
'GetDeps',
|
|
1343
|
+
'GetInjectedServiceDependencies',
|
|
1344
|
+
'GetServiceOutput',
|
|
1345
|
+
].filter(
|
|
931
1346
|
(importName) =>
|
|
932
1347
|
!hasLocalNamedImport(sourceFile, helperImportPath, importName),
|
|
933
1348
|
);
|
|
@@ -1875,6 +2290,64 @@ function isProvidedInInjectableTree(
|
|
|
1875
2290
|
);
|
|
1876
2291
|
}
|
|
1877
2292
|
|
|
2293
|
+
function shouldGenerateLocalMissingProvider(
|
|
2294
|
+
sourceFile: SourceFile,
|
|
2295
|
+
dependencyText: string,
|
|
2296
|
+
): boolean {
|
|
2297
|
+
return (
|
|
2298
|
+
!resolveTrackedInjectHelperByName(sourceFile, dependencyText) &&
|
|
2299
|
+
!isProvidedInInjectableTree(sourceFile, dependencyText)
|
|
2300
|
+
);
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
function resolveTrackedInjectHelperByName(
|
|
2304
|
+
sourceFile: SourceFile,
|
|
2305
|
+
dependencyText: string,
|
|
2306
|
+
): TrackedHelperResolution | undefined {
|
|
2307
|
+
const [rootIdentifier] = dependencyText.split('.');
|
|
2308
|
+
if (!rootIdentifier) {
|
|
2309
|
+
return undefined;
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2312
|
+
for (const declaration of resolveDependencyIdentifierDeclarations(
|
|
2313
|
+
sourceFile,
|
|
2314
|
+
rootIdentifier,
|
|
2315
|
+
)) {
|
|
2316
|
+
const helper = resolveTrackedInjectHelperFromDeclaration(declaration);
|
|
2317
|
+
if (helper) {
|
|
2318
|
+
return helper;
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
|
|
2322
|
+
return undefined;
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
function resolveDependencyIdentifierDeclarations(
|
|
2326
|
+
sourceFile: SourceFile,
|
|
2327
|
+
rootIdentifier: string,
|
|
2328
|
+
): Node[] {
|
|
2329
|
+
for (const importDeclaration of sourceFile.getImportDeclarations()) {
|
|
2330
|
+
const namedImport = importDeclaration.getNamedImports().find((specifier) => {
|
|
2331
|
+
const localName =
|
|
2332
|
+
specifier.getAliasNode()?.getText() ?? specifier.getNameNode().getText();
|
|
2333
|
+
return localName === rootIdentifier;
|
|
2334
|
+
});
|
|
2335
|
+
|
|
2336
|
+
if (!namedImport) {
|
|
2337
|
+
continue;
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
const identifier = namedImport.getAliasNode() ?? namedImport.getNameNode();
|
|
2341
|
+
const symbol = identifier.getSymbol() ?? identifier.getType().getSymbol();
|
|
2342
|
+
const aliasedSymbol = symbol?.getAliasedSymbol() ?? symbol;
|
|
2343
|
+
return aliasedSymbol?.getDeclarations() ?? [namedImport];
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
return sourceFile
|
|
2347
|
+
.getDescendantsOfKind(SyntaxKind.BindingElement)
|
|
2348
|
+
.filter((bindingElement) => bindingElement.getName() === rootIdentifier);
|
|
2349
|
+
}
|
|
2350
|
+
|
|
1878
2351
|
function getDependencyTextFromTypeNode(typeNode: TypeNode): string | undefined {
|
|
1879
2352
|
if (isPrimitiveTypeNode(typeNode)) {
|
|
1880
2353
|
return undefined;
|
|
@@ -2124,7 +2597,12 @@ function getHelperImportSafety(
|
|
|
2124
2597
|
sourceFile: SourceFile,
|
|
2125
2598
|
helperImportPath: string,
|
|
2126
2599
|
): { safe: true; warnings: [] } | { safe: false; warnings: string[] } {
|
|
2127
|
-
const warnings = [
|
|
2600
|
+
const warnings = [
|
|
2601
|
+
'DerivedService',
|
|
2602
|
+
'GetDeps',
|
|
2603
|
+
'GetInjectedServiceDependencies',
|
|
2604
|
+
'GetServiceOutput',
|
|
2605
|
+
]
|
|
2128
2606
|
.filter((helperName) =>
|
|
2129
2607
|
hasConflictingTopLevelBinding(sourceFile, helperImportPath, helperName),
|
|
2130
2608
|
)
|