From e36af00d83cd26c224406d8bcac9a9fc48072009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Fri, 18 May 2018 15:09:23 +0800 Subject: [PATCH 1/2] feat(@schematics/angular): support entryComponent Fixes https://github.com/angular/angular-cli/issues/7749 --- .../schematics/angular/component/index.ts | 28 ++++++++++++++++++- .../angular/component/index_spec.ts | 8 ++++++ .../schematics/angular/component/schema.d.ts | 4 +++ .../schematics/angular/component/schema.json | 5 ++++ .../schematics/angular/utility/ast-utils.ts | 12 ++++++++ 5 files changed, 56 insertions(+), 1 deletion(-) diff --git a/packages/schematics/angular/component/index.ts b/packages/schematics/angular/component/index.ts index 00b9e75cc6..6317bdfc31 100644 --- a/packages/schematics/angular/component/index.ts +++ b/packages/schematics/angular/component/index.ts @@ -22,7 +22,11 @@ import { url, } from '@angular-devkit/schematics'; import * as ts from 'typescript'; -import { addDeclarationToModule, addExportToModule } from '../utility/ast-utils'; +import { + addDeclarationToModule, + addEntryComponentToModule, + addExportToModule, +} from '../utility/ast-utils'; import { InsertChange } from '../utility/change'; import { getWorkspace } from '../utility/config'; import { buildRelativePath, findModuleFromOptions } from '../utility/find-module'; @@ -86,6 +90,28 @@ function addDeclarationToNgModule(options: ComponentOptions): Rule { host.commitUpdate(exportRecorder); } + if (options.entryComponent) { + // Need to refresh the AST because we overwrote the file in the host. + const text = host.read(modulePath); + if (text === null) { + throw new SchematicsException(`File ${modulePath} does not exist.`); + } + const sourceText = text.toString('utf-8'); + const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true); + + const entryComponentRecorder = host.beginUpdate(modulePath); + const entryComponentChanges = addEntryComponentToModule(source, modulePath, + strings.classify(`${options.name}Component`), + relativePath); + + for (const change of entryComponentChanges) { + if (change instanceof InsertChange) { + entryComponentRecorder.insertLeft(change.pos, change.toAdd); + } + } + host.commitUpdate(entryComponentRecorder); + } + return host; }; diff --git a/packages/schematics/angular/component/index_spec.ts b/packages/schematics/angular/component/index_spec.ts index eb7ce900a7..90fd99db8b 100644 --- a/packages/schematics/angular/component/index_spec.ts +++ b/packages/schematics/angular/component/index_spec.ts @@ -135,6 +135,14 @@ describe('Component Schematic', () => { expect(appModuleContent).toMatch(/exports: \[FooComponent\]/); }); + it('should set the entry component', () => { + const options = { ...defaultOptions, entryComponent: true }; + + const tree = schematicRunner.runSchematic('component', options, appTree); + const appModuleContent = tree.readContent('/projects/bar/src/app/app.module.ts'); + expect(appModuleContent).toMatch(/entryComponents: \[FooComponent\]/); + }); + it('should import into a specified module', () => { const options = { ...defaultOptions, module: 'app.module.ts' }; diff --git a/packages/schematics/angular/component/schema.d.ts b/packages/schematics/angular/component/schema.d.ts index 81c02e4ab5..cc7b8cd518 100644 --- a/packages/schematics/angular/component/schema.d.ts +++ b/packages/schematics/angular/component/schema.d.ts @@ -67,4 +67,8 @@ export interface Schema { * Specifies if declaring module exports the component. */ export?: boolean; + /** + * Specifies if the component is an entry component of declaring module. + */ + entryComponent?: boolean; } diff --git a/packages/schematics/angular/component/schema.json b/packages/schematics/angular/component/schema.json index 72c180782f..b626626941 100644 --- a/packages/schematics/angular/component/schema.json +++ b/packages/schematics/angular/component/schema.json @@ -98,6 +98,11 @@ "type": "boolean", "default": false, "description": "Specifies if declaring module exports the component." + }, + "entryComponent": { + "type": "boolean", + "default": false, + "description": "Specifies if the component is an entry component of declaring module." } }, "required": [] diff --git a/packages/schematics/angular/utility/ast-utils.ts b/packages/schematics/angular/utility/ast-utils.ts index 99640ce86a..d584ae9801 100644 --- a/packages/schematics/angular/utility/ast-utils.ts +++ b/packages/schematics/angular/utility/ast-utils.ts @@ -531,6 +531,18 @@ export function addBootstrapToModule(source: ts.SourceFile, return addSymbolToNgModuleMetadata(source, modulePath, 'bootstrap', classifiedName, importPath); } +/** + * Custom function to insert an entryComponent into NgModule. It also imports it. + */ +export function addEntryComponentToModule(source: ts.SourceFile, + modulePath: string, classifiedName: string, + importPath: string): Change[] { + return addSymbolToNgModuleMetadata( + source, modulePath, + 'entryComponents', classifiedName, importPath, + ); +} + /** * Determine if an import already exists. */ From 9d8a5e57443d4fae728ca26c7ac12ef2ce0c11ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Fri, 18 May 2018 18:08:58 +0800 Subject: [PATCH 2/2] refactor(@schematics/angular): add readIntoSourceFile # Conflicts: # packages/schematics/angular/component/index.ts --- .../schematics/angular/component/index.ts | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/packages/schematics/angular/component/index.ts b/packages/schematics/angular/component/index.ts index 6317bdfc31..efa53995ab 100644 --- a/packages/schematics/angular/component/index.ts +++ b/packages/schematics/angular/component/index.ts @@ -34,6 +34,15 @@ import { parseName } from '../utility/parse-name'; import { validateHtmlSelector, validateName } from '../utility/validation'; import { Schema as ComponentOptions } from './schema'; +function readIntoSourceFile(host: Tree, modulePath: string): ts.SourceFile { + const text = host.read(modulePath); + if (text === null) { + throw new SchematicsException(`File ${modulePath} does not exist.`); + } + const sourceText = text.toString('utf-8'); + + return ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true); +} function addDeclarationToNgModule(options: ComponentOptions): Rule { return (host: Tree) => { @@ -42,12 +51,7 @@ function addDeclarationToNgModule(options: ComponentOptions): Rule { } const modulePath = options.module; - const text = host.read(modulePath); - if (text === null) { - throw new SchematicsException(`File ${modulePath} does not exist.`); - } - const sourceText = text.toString('utf-8'); - const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true); + const source = readIntoSourceFile(host, modulePath); const componentPath = `/${options.path}/` + (options.flat ? '' : strings.dasherize(options.name) + '/') @@ -70,12 +74,7 @@ function addDeclarationToNgModule(options: ComponentOptions): Rule { if (options.export) { // Need to refresh the AST because we overwrote the file in the host. - const text = host.read(modulePath); - if (text === null) { - throw new SchematicsException(`File ${modulePath} does not exist.`); - } - const sourceText = text.toString('utf-8'); - const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true); + const source = readIntoSourceFile(host, modulePath); const exportRecorder = host.beginUpdate(modulePath); const exportChanges = addExportToModule(source, modulePath, @@ -92,15 +91,11 @@ function addDeclarationToNgModule(options: ComponentOptions): Rule { if (options.entryComponent) { // Need to refresh the AST because we overwrote the file in the host. - const text = host.read(modulePath); - if (text === null) { - throw new SchematicsException(`File ${modulePath} does not exist.`); - } - const sourceText = text.toString('utf-8'); - const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true); + const source = readIntoSourceFile(host, modulePath); const entryComponentRecorder = host.beginUpdate(modulePath); - const entryComponentChanges = addEntryComponentToModule(source, modulePath, + const entryComponentChanges = addEntryComponentToModule( + source, modulePath, strings.classify(`${options.name}Component`), relativePath);