Skip to content

Commit c682965

Browse files
wagnermacielmmalerba
authored andcommitted
feat(material/schematics): create updateModuleSpecifier ts migration fn (#25128)
* move Update and writeUpdate to new file to avoid circular deps
1 parent 62607c2 commit c682965

File tree

5 files changed

+127
-16
lines changed

5 files changed

+127
-16
lines changed

src/material/schematics/migration-utilities/BUILD.bazel

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ ts_library(
88
["**/*.ts"],
99
exclude = ["**/*.spec.ts"],
1010
),
11-
deps = [],
11+
deps = [
12+
"@npm//typescript",
13+
],
1214
)
1315

1416
ts_library(
@@ -18,6 +20,7 @@ ts_library(
1820
deps = [
1921
":migration-utilities",
2022
"@npm//@types/jasmine",
23+
"@npm//typescript",
2124
],
2225
)
2326

src/material/schematics/migration-utilities/index.ts

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,5 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
/** Stores the data needed to make a single update to a file. */
10-
export interface Update {
11-
/** The start index of the location of the update. */
12-
offset: number;
13-
14-
/** A function to be used to update the file content. */
15-
updateFn: (html: string) => string;
16-
}
17-
18-
/** Applies the updates to the given file content in reverse offset order. */
19-
export function writeUpdates(content: string, updates: Update[]): string {
20-
updates.sort((a, b) => b.offset - a.offset);
21-
updates.forEach(update => (content = update.updateFn(content)));
22-
return content;
23-
}
9+
export {updateModuleSpecifier} from './typescript/import-operations';
10+
export {Update, writeUpdates} from './update';
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import * as ts from 'typescript';
2+
import {updateModuleSpecifier} from './import-operations';
3+
4+
describe('import operations', () => {
5+
describe('updateModuleSpecifier', () => {
6+
function runUpdateModuleSpecifierTest(
7+
description: string,
8+
opts: {old: string; new: string},
9+
): void {
10+
const node = createNode(opts.old, ts.SyntaxKind.ImportDeclaration) as ts.ImportDeclaration;
11+
const update = updateModuleSpecifier(node!, {moduleSpecifier: 'new-module-name'});
12+
const newImport = update?.updateFn(opts.old);
13+
expect(newImport).withContext(description).toBe(opts.new);
14+
}
15+
it('updates the module specifier of import declarations', () => {
16+
runUpdateModuleSpecifierTest('default export', {
17+
old: `import defaultExport from 'old-module-name';`,
18+
new: `import defaultExport from 'new-module-name';`,
19+
});
20+
runUpdateModuleSpecifierTest('namespace import', {
21+
old: `import * as name from 'old-module-name';`,
22+
new: `import * as name from 'new-module-name';`,
23+
});
24+
runUpdateModuleSpecifierTest('named import', {
25+
old: `import { export1 } from 'old-module-name';`,
26+
new: `import { export1 } from 'new-module-name';`,
27+
});
28+
runUpdateModuleSpecifierTest('aliased named import', {
29+
old: `import { export1 as alias1 } from 'old-module-name';`,
30+
new: `import { export1 as alias1 } from 'new-module-name';`,
31+
});
32+
runUpdateModuleSpecifierTest('multiple named import', {
33+
old: `import { export1, export2 } from 'old-module-name';`,
34+
new: `import { export1, export2 } from 'new-module-name';`,
35+
});
36+
runUpdateModuleSpecifierTest('multiple named import w/ alias', {
37+
old: `import { export1, export2 as alias2 } from 'old-module-name';`,
38+
new: `import { export1, export2 as alias2 } from 'new-module-name';`,
39+
});
40+
});
41+
});
42+
});
43+
44+
function createSourceFile(text: string): ts.SourceFile {
45+
return ts.createSourceFile('file.ts', text, ts.ScriptTarget.Latest);
46+
}
47+
48+
function visitNodes(node: ts.SourceFile | ts.Node, visitFn: (node: ts.Node) => void): void {
49+
node.forEachChild(child => {
50+
visitFn(child);
51+
visitNodes(child, visitFn);
52+
});
53+
}
54+
55+
function getNodeByKind(file: ts.SourceFile, kind: ts.SyntaxKind): ts.Node | null {
56+
let node: ts.Node | null = null;
57+
visitNodes(file, (_node: ts.Node) => {
58+
if (_node.kind === kind) {
59+
node = _node;
60+
}
61+
});
62+
return node;
63+
}
64+
65+
function createNode(text: string, kind: ts.SyntaxKind): ts.Node | null {
66+
return getNodeByKind(createSourceFile(text), kind);
67+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {Update} from '../update';
10+
import * as ts from 'typescript';
11+
12+
/** Returns an Update that renames the module specifier of the given import declaration node. */
13+
export function updateModuleSpecifier(
14+
node: ts.ImportDeclaration,
15+
opts: {
16+
moduleSpecifier: string;
17+
},
18+
): Update {
19+
const moduleSpecifier = node.moduleSpecifier as ts.StringLiteral;
20+
return {
21+
offset: moduleSpecifier.pos,
22+
updateFn: (text: string) => {
23+
const index = text.indexOf(moduleSpecifier.text, moduleSpecifier.pos);
24+
return (
25+
text.slice(0, index) +
26+
opts.moduleSpecifier +
27+
text.slice(index + moduleSpecifier.text.length)
28+
);
29+
},
30+
};
31+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
/** Stores the data needed to make a single update to a file. */
10+
export interface Update {
11+
/** The start index of the location of the update. */
12+
offset: number;
13+
14+
/** A function to be used to update the file content. */
15+
updateFn: (text: string) => string;
16+
}
17+
18+
/** Applies the updates to the given file content in reverse offset order. */
19+
export function writeUpdates(content: string, updates: Update[]): string {
20+
updates.sort((a, b) => b.offset - a.offset);
21+
updates.forEach(update => (content = update.updateFn(content)));
22+
return content;
23+
}

0 commit comments

Comments
 (0)