Skip to content

Commit 9eb1bd5

Browse files
timdeschryverbrandonroberts
authored andcommitted
feat(schematics): add createEffect migration schematic (#2136)
1 parent de07805 commit 9eb1bd5

File tree

22 files changed

+916
-176
lines changed

22 files changed

+916
-176
lines changed

modules/data/schematics-core/utility/ast-utils.ts

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import {
1313
NoopChange,
1414
createReplaceChange,
1515
ReplaceChange,
16+
RemoveChange,
17+
createRemoveChange,
1618
} from './change';
1719
import { Path } from '@angular-devkit/core';
1820

@@ -650,7 +652,7 @@ export function replaceImport(
650652
importFrom: string,
651653
importAsIs: string,
652654
importToBe: string
653-
): ReplaceChange[] {
655+
): (ReplaceChange | RemoveChange)[] {
654656
const imports = sourceFile.statements
655657
.filter(ts.isImportDeclaration)
656658
.filter(
@@ -663,32 +665,67 @@ export function replaceImport(
663665
return [];
664666
}
665667

666-
const changes = imports
667-
.map(p => (p.importClause!.namedBindings! as ts.NamedImports).elements)
668-
.reduce((imports, curr) => imports.concat(curr), [] as ts.ImportSpecifier[])
669-
.map(specifier => {
670-
if (!ts.isImportSpecifier(specifier)) {
671-
return { hit: false };
668+
const importText = (specifier: ts.ImportSpecifier) => {
669+
if (specifier.name.text) {
670+
return specifier.name.text;
671+
}
672+
673+
// if import is renamed
674+
if (specifier.propertyName && specifier.propertyName.text) {
675+
return specifier.propertyName.text;
676+
}
677+
678+
return '';
679+
};
680+
681+
const changes = imports.map(p => {
682+
const importSpecifiers = (p.importClause!.namedBindings! as ts.NamedImports)
683+
.elements;
684+
685+
const isAlreadyImported = importSpecifiers
686+
.map(importText)
687+
.includes(importToBe);
688+
689+
const importChanges = importSpecifiers.map((specifier, index) => {
690+
const text = importText(specifier);
691+
692+
// import is not the one we're looking for, can be skipped
693+
if (text !== importAsIs) {
694+
return undefined;
672695
}
673696

674-
if (specifier.name.text === importAsIs) {
675-
return { hit: true, specifier, text: specifier.name.text };
697+
// identifier has not been imported, simply replace the old text with the new text
698+
if (!isAlreadyImported) {
699+
return createReplaceChange(
700+
sourceFile,
701+
specifier!,
702+
importAsIs,
703+
importToBe
704+
);
676705
}
677706

678-
// if import is renamed
679-
if (
680-
specifier.propertyName &&
681-
specifier.propertyName.text === importAsIs
682-
) {
683-
return { hit: true, specifier, text: specifier.propertyName.text };
707+
const nextIdentifier = importSpecifiers[index + 1];
708+
// identifer is not the last, also clean up the comma
709+
if (nextIdentifier) {
710+
return createRemoveChange(
711+
sourceFile,
712+
specifier,
713+
specifier.getStart(sourceFile),
714+
nextIdentifier.getStart(sourceFile)
715+
);
684716
}
685717

686-
return { hit: false };
687-
})
688-
.filter(({ hit }) => hit)
689-
.map(({ specifier, text }) =>
690-
createReplaceChange(sourceFile, specifier!, text!, importToBe)
691-
);
718+
// there are no imports following, just remove it
719+
return createRemoveChange(
720+
sourceFile,
721+
specifier,
722+
specifier.getStart(sourceFile),
723+
specifier.getEnd()
724+
);
725+
});
726+
727+
return importChanges.filter(Boolean) as (ReplaceChange | RemoveChange)[];
728+
});
692729

693-
return changes;
730+
return changes.reduce((imports, curr) => imports.concat(curr), []);
694731
}

modules/data/schematics-core/utility/change.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,15 @@ export function createReplaceChange(
148148
);
149149
}
150150

151+
export function createRemoveChange(
152+
sourceFile: ts.SourceFile,
153+
node: ts.Node,
154+
from = node.getStart(sourceFile),
155+
to = node.getEnd()
156+
): RemoveChange {
157+
return new RemoveChange(sourceFile.fileName, from, to);
158+
}
159+
151160
export function createChangeRecorder(
152161
tree: Tree,
153162
path: string,

modules/effects/schematics-core/utility/ast-utils.ts

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import {
1313
NoopChange,
1414
createReplaceChange,
1515
ReplaceChange,
16+
RemoveChange,
17+
createRemoveChange,
1618
} from './change';
1719
import { Path } from '@angular-devkit/core';
1820

@@ -650,7 +652,7 @@ export function replaceImport(
650652
importFrom: string,
651653
importAsIs: string,
652654
importToBe: string
653-
): ReplaceChange[] {
655+
): (ReplaceChange | RemoveChange)[] {
654656
const imports = sourceFile.statements
655657
.filter(ts.isImportDeclaration)
656658
.filter(
@@ -663,32 +665,67 @@ export function replaceImport(
663665
return [];
664666
}
665667

666-
const changes = imports
667-
.map(p => (p.importClause!.namedBindings! as ts.NamedImports).elements)
668-
.reduce((imports, curr) => imports.concat(curr), [] as ts.ImportSpecifier[])
669-
.map(specifier => {
670-
if (!ts.isImportSpecifier(specifier)) {
671-
return { hit: false };
668+
const importText = (specifier: ts.ImportSpecifier) => {
669+
if (specifier.name.text) {
670+
return specifier.name.text;
671+
}
672+
673+
// if import is renamed
674+
if (specifier.propertyName && specifier.propertyName.text) {
675+
return specifier.propertyName.text;
676+
}
677+
678+
return '';
679+
};
680+
681+
const changes = imports.map(p => {
682+
const importSpecifiers = (p.importClause!.namedBindings! as ts.NamedImports)
683+
.elements;
684+
685+
const isAlreadyImported = importSpecifiers
686+
.map(importText)
687+
.includes(importToBe);
688+
689+
const importChanges = importSpecifiers.map((specifier, index) => {
690+
const text = importText(specifier);
691+
692+
// import is not the one we're looking for, can be skipped
693+
if (text !== importAsIs) {
694+
return undefined;
672695
}
673696

674-
if (specifier.name.text === importAsIs) {
675-
return { hit: true, specifier, text: specifier.name.text };
697+
// identifier has not been imported, simply replace the old text with the new text
698+
if (!isAlreadyImported) {
699+
return createReplaceChange(
700+
sourceFile,
701+
specifier!,
702+
importAsIs,
703+
importToBe
704+
);
676705
}
677706

678-
// if import is renamed
679-
if (
680-
specifier.propertyName &&
681-
specifier.propertyName.text === importAsIs
682-
) {
683-
return { hit: true, specifier, text: specifier.propertyName.text };
707+
const nextIdentifier = importSpecifiers[index + 1];
708+
// identifer is not the last, also clean up the comma
709+
if (nextIdentifier) {
710+
return createRemoveChange(
711+
sourceFile,
712+
specifier,
713+
specifier.getStart(sourceFile),
714+
nextIdentifier.getStart(sourceFile)
715+
);
684716
}
685717

686-
return { hit: false };
687-
})
688-
.filter(({ hit }) => hit)
689-
.map(({ specifier, text }) =>
690-
createReplaceChange(sourceFile, specifier!, text!, importToBe)
691-
);
718+
// there are no imports following, just remove it
719+
return createRemoveChange(
720+
sourceFile,
721+
specifier,
722+
specifier.getStart(sourceFile),
723+
specifier.getEnd()
724+
);
725+
});
726+
727+
return importChanges.filter(Boolean) as (ReplaceChange | RemoveChange)[];
728+
});
692729

693-
return changes;
730+
return changes.reduce((imports, curr) => imports.concat(curr), []);
694731
}

modules/effects/schematics-core/utility/change.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,15 @@ export function createReplaceChange(
148148
);
149149
}
150150

151+
export function createRemoveChange(
152+
sourceFile: ts.SourceFile,
153+
node: ts.Node,
154+
from = node.getStart(sourceFile),
155+
to = node.getEnd()
156+
): RemoveChange {
157+
return new RemoveChange(sourceFile.fileName, from, to);
158+
}
159+
151160
export function createChangeRecorder(
152161
tree: Tree,
153162
path: string,

modules/entity/schematics-core/utility/ast-utils.ts

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import {
1313
NoopChange,
1414
createReplaceChange,
1515
ReplaceChange,
16+
RemoveChange,
17+
createRemoveChange,
1618
} from './change';
1719
import { Path } from '@angular-devkit/core';
1820

@@ -650,7 +652,7 @@ export function replaceImport(
650652
importFrom: string,
651653
importAsIs: string,
652654
importToBe: string
653-
): ReplaceChange[] {
655+
): (ReplaceChange | RemoveChange)[] {
654656
const imports = sourceFile.statements
655657
.filter(ts.isImportDeclaration)
656658
.filter(
@@ -663,32 +665,67 @@ export function replaceImport(
663665
return [];
664666
}
665667

666-
const changes = imports
667-
.map(p => (p.importClause!.namedBindings! as ts.NamedImports).elements)
668-
.reduce((imports, curr) => imports.concat(curr), [] as ts.ImportSpecifier[])
669-
.map(specifier => {
670-
if (!ts.isImportSpecifier(specifier)) {
671-
return { hit: false };
668+
const importText = (specifier: ts.ImportSpecifier) => {
669+
if (specifier.name.text) {
670+
return specifier.name.text;
671+
}
672+
673+
// if import is renamed
674+
if (specifier.propertyName && specifier.propertyName.text) {
675+
return specifier.propertyName.text;
676+
}
677+
678+
return '';
679+
};
680+
681+
const changes = imports.map(p => {
682+
const importSpecifiers = (p.importClause!.namedBindings! as ts.NamedImports)
683+
.elements;
684+
685+
const isAlreadyImported = importSpecifiers
686+
.map(importText)
687+
.includes(importToBe);
688+
689+
const importChanges = importSpecifiers.map((specifier, index) => {
690+
const text = importText(specifier);
691+
692+
// import is not the one we're looking for, can be skipped
693+
if (text !== importAsIs) {
694+
return undefined;
672695
}
673696

674-
if (specifier.name.text === importAsIs) {
675-
return { hit: true, specifier, text: specifier.name.text };
697+
// identifier has not been imported, simply replace the old text with the new text
698+
if (!isAlreadyImported) {
699+
return createReplaceChange(
700+
sourceFile,
701+
specifier!,
702+
importAsIs,
703+
importToBe
704+
);
676705
}
677706

678-
// if import is renamed
679-
if (
680-
specifier.propertyName &&
681-
specifier.propertyName.text === importAsIs
682-
) {
683-
return { hit: true, specifier, text: specifier.propertyName.text };
707+
const nextIdentifier = importSpecifiers[index + 1];
708+
// identifer is not the last, also clean up the comma
709+
if (nextIdentifier) {
710+
return createRemoveChange(
711+
sourceFile,
712+
specifier,
713+
specifier.getStart(sourceFile),
714+
nextIdentifier.getStart(sourceFile)
715+
);
684716
}
685717

686-
return { hit: false };
687-
})
688-
.filter(({ hit }) => hit)
689-
.map(({ specifier, text }) =>
690-
createReplaceChange(sourceFile, specifier!, text!, importToBe)
691-
);
718+
// there are no imports following, just remove it
719+
return createRemoveChange(
720+
sourceFile,
721+
specifier,
722+
specifier.getStart(sourceFile),
723+
specifier.getEnd()
724+
);
725+
});
726+
727+
return importChanges.filter(Boolean) as (ReplaceChange | RemoveChange)[];
728+
});
692729

693-
return changes;
730+
return changes.reduce((imports, curr) => imports.concat(curr), []);
694731
}

modules/entity/schematics-core/utility/change.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,15 @@ export function createReplaceChange(
148148
);
149149
}
150150

151+
export function createRemoveChange(
152+
sourceFile: ts.SourceFile,
153+
node: ts.Node,
154+
from = node.getStart(sourceFile),
155+
to = node.getEnd()
156+
): RemoveChange {
157+
return new RemoveChange(sourceFile.fileName, from, to);
158+
}
159+
151160
export function createChangeRecorder(
152161
tree: Tree,
153162
path: string,

0 commit comments

Comments
 (0)