Skip to content

Commit 2dfeadb

Browse files
authored
refactor/clone-cdk-schematics (#21)
* refactor(ast): clone original code * refactor(ast): clone original code * refactor(ng-module-imports): clone original code * refactor(build-components): clone original code * refactor(get-project): clone original code * refactor(html-head-element): clone original code * refactor(package-config): clone original code * build: remove @angular/cdk
1 parent 999869f commit 2dfeadb

21 files changed

+634
-24
lines changed

package-lock.json

Lines changed: 0 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
"dependencies": {
3838
"@angular-devkit/core": "^8.3.21",
3939
"@angular-devkit/schematics": "^8.3.21",
40-
"@angular/cdk": "^8.2.3",
4140
"@schematics/angular": "^8.3.21",
4241
"@schematics/update": "^0.803.21",
4342
"rxjs": "^6.4.0",

src/angular/workspace-models.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ export interface WorkspaceTargets<TProjectType extends ProjectType = ProjectType
151151
e2e?: E2EBuilderTarget;
152152
'app-shell'?: AppShellBuilderTarget;
153153
'extract-i18n'?: ExtractI18nBuilderTarget;
154-
// TODO(hans): change this any to unknown when google3 supports TypeScript 3.0.
154+
// TODO change this any to unknown when google3 supports TypeScript 3.0.
155155
// tslint:disable-next-line:no-any
156156
[key: string]: any;
157157
}

src/cdk/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export * from './ng-add/package-config';
2+
export * from './utils/ast';
3+
export * from './utils/build-component';
4+
export * from './utils/get-project';
5+
export * from './utils/html-head-element';
6+
export * from './utils/project-main-file';
7+
export * from './utils/project-targets';
8+
export * from './utils/version-agnostic-typescript';

src/cdk/ng-add/package-config.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Tree } from '@angular-devkit/schematics';
2+
3+
/**
4+
* Sorts the keys of the given object.
5+
* @returns A new object instance with sorted keys
6+
*/
7+
function sortObjectByKeys(obj: object) {
8+
return Object.keys(obj)
9+
.sort()
10+
.reduce((result, key) => (result[key] = obj[key]) && result, {});
11+
}
12+
13+
/** Adds a package to the package.json in the given host tree. */
14+
export function addPackageToPackageJson(host: Tree, pkg: string, version: string): Tree {
15+
if (host.exists('package.json')) {
16+
const sourceText = host.read('package.json')!.toString('utf-8');
17+
const json = JSON.parse(sourceText);
18+
19+
if (!json.dependencies) {
20+
json.dependencies = {};
21+
}
22+
23+
if (!json.dependencies[pkg]) {
24+
json.dependencies[pkg] = version;
25+
json.dependencies = sortObjectByKeys(json.dependencies);
26+
}
27+
28+
host.overwrite('package.json', JSON.stringify(json, null, 2));
29+
}
30+
31+
return host;
32+
}

src/cdk/utils/ast.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { SchematicsException, Tree } from '@angular-devkit/schematics';
2+
import { addImportToModule, getAppModulePath, InsertChange, WorkspaceProject } from '../../angular';
3+
import { getProjectMainFile } from './project-main-file';
4+
import { ts, typescript } from './version-agnostic-typescript';
5+
6+
/** Reads file given path and returns TypeScript source file. */
7+
export function getSourceFile(host: Tree, path: string): typescript.SourceFile {
8+
const buffer = host.read(path);
9+
if (!buffer) {
10+
throw new SchematicsException(`Could not find file for path: ${path}`);
11+
}
12+
return ts.createSourceFile(path, buffer.toString(), ts.ScriptTarget.Latest, true);
13+
}
14+
15+
/** Import and add module to root app module. */
16+
export function addModuleImportToRootModule(host: Tree, moduleName: string, src: string, project: WorkspaceProject) {
17+
const modulePath = getAppModulePath(host, getProjectMainFile(project));
18+
addModuleImportToModule(host, modulePath, moduleName, src);
19+
}
20+
21+
/**
22+
* Import and add module to specific module path.
23+
* @param host the tree we are updating
24+
* @param modulePath src location of the module to import
25+
* @param moduleName name of module to import
26+
* @param src src location to import
27+
*/
28+
export function addModuleImportToModule(host: Tree, modulePath: string, moduleName: string, src: string) {
29+
const moduleSource = getSourceFile(host, modulePath);
30+
31+
if (!moduleSource) {
32+
throw new SchematicsException(`Module not found: ${modulePath}`);
33+
}
34+
35+
const changes = addImportToModule(<any>moduleSource, modulePath, moduleName, src);
36+
const recorder = host.beginUpdate(modulePath);
37+
38+
changes.forEach(change => {
39+
if (change instanceof InsertChange) {
40+
recorder.insertLeft(change.pos, change.toAdd);
41+
}
42+
});
43+
44+
host.commitUpdate(recorder);
45+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { SchematicsException, Tree } from '@angular-devkit/schematics';
2+
import * as ts from 'typescript';
3+
4+
/**
5+
* Whether the Angular module in the given path imports the specified module class name.
6+
*/
7+
export function hasNgModuleImport(tree: Tree, modulePath: string, className: string): boolean {
8+
const moduleFileContent = tree.read(modulePath);
9+
10+
if (!moduleFileContent) {
11+
throw new SchematicsException(`Could not read Angular module file: ${modulePath}`);
12+
}
13+
14+
const parsedFile = ts.createSourceFile(modulePath, moduleFileContent.toString(), ts.ScriptTarget.Latest, true);
15+
const ngModuleMetadata = findNgModuleMetadata(parsedFile);
16+
17+
if (!ngModuleMetadata) {
18+
throw new SchematicsException(`Could not find NgModule declaration inside: "${modulePath}"`);
19+
}
20+
21+
for (let property of ngModuleMetadata!.properties) {
22+
if (
23+
!ts.isPropertyAssignment(property) ||
24+
property.name.getText() !== 'imports' ||
25+
!ts.isArrayLiteralExpression(property.initializer)
26+
) {
27+
continue;
28+
}
29+
30+
if (property.initializer.elements.some(element => element.getText() === className)) {
31+
return true;
32+
}
33+
}
34+
35+
return false;
36+
}
37+
38+
/**
39+
* Resolves the last identifier that is part of the given expression. This helps resolving
40+
* identifiers of nested property access expressions (e.g. myNamespace.core.NgModule).
41+
*/
42+
function resolveIdentifierOfExpression(expression: ts.Expression): ts.Identifier | null {
43+
if (ts.isIdentifier(expression)) {
44+
return expression;
45+
} else if (ts.isPropertyAccessExpression(expression)) {
46+
return expression.name;
47+
}
48+
return null;
49+
}
50+
51+
/**
52+
* Finds a NgModule declaration within the specified TypeScript node and returns the
53+
* corresponding metadata for it. This function searches breadth first because
54+
* NgModule's are usually not nested within other expressions or declarations.
55+
*/
56+
function findNgModuleMetadata(rootNode: ts.Node): ts.ObjectLiteralExpression | null {
57+
// Add immediate child nodes of the root node to the queue.
58+
const nodeQueue: ts.Node[] = [...rootNode.getChildren()];
59+
60+
while (nodeQueue.length) {
61+
const node = nodeQueue.shift()!;
62+
63+
if (ts.isDecorator(node) && ts.isCallExpression(node.expression) && isNgModuleCallExpression(node.expression)) {
64+
return node.expression.arguments[0] as ts.ObjectLiteralExpression;
65+
} else {
66+
nodeQueue.push(...node.getChildren());
67+
}
68+
}
69+
70+
return null;
71+
}
72+
73+
/** Whether the specified call expression is referring to a NgModule definition. */
74+
function isNgModuleCallExpression(callExpression: ts.CallExpression): boolean {
75+
if (!callExpression.arguments.length || !ts.isObjectLiteralExpression(callExpression.arguments[0])) {
76+
return false;
77+
}
78+
79+
const decoratorIdentifier = resolveIdentifierOfExpression(callExpression.expression);
80+
return decoratorIdentifier ? decoratorIdentifier.text === 'NgModule' : false;
81+
}

0 commit comments

Comments
 (0)