From b1b0207496bb182de9df9d1fdf3d0fdd76e4e9cd Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 14 Aug 2018 14:31:48 -0400 Subject: [PATCH] fix(schematics): support app gen with inline-template This adds the support to generate an application with the `inline-template` flag activated. Two new utils added: `getDecoratorPropertyValueNode` & `replaceNodeValue`. Add some tests ensure to test the two ways of generating template, using `templateUrls` and `inlineTemplate`. Tests are unising the flag `inlineTemplate`. close #519 --- .../application/application.spec.ts | 27 ++++++++ .../src/collection/application/index.ts | 26 +++++++- packages/schematics/src/utils/ast-utils.ts | 62 +++++++++++++++++-- 3 files changed, 107 insertions(+), 8 deletions(-) diff --git a/packages/schematics/src/collection/application/application.spec.ts b/packages/schematics/src/collection/application/application.spec.ts index 45b162dc45..155f3bdf38 100644 --- a/packages/schematics/src/collection/application/application.spec.ts +++ b/packages/schematics/src/collection/application/application.spec.ts @@ -240,4 +240,31 @@ describe('app', () => { ).toContain('imports: [RouterTestingModule]'); }); }); + + describe('template generation mode', () => { + it('should create Nx specific `app.component.html` template', () => { + const tree = schematicRunner.runSchematic( + 'app', + { name: 'myApp', directory: 'myDir' }, + appTree + ); + expect( + getFileContent(tree, 'apps/my-dir/my-app/src/app/app.component.html') + ).toBeTruthy(); + expect( + getFileContent(tree, 'apps/my-dir/my-app/src/app/app.component.html') + ).toContain('This is an Angular CLI app built with Nrwl Nx!'); + }); + + it("should update `template`'s property of AppComponent with Nx content", () => { + const tree = schematicRunner.runSchematic( + 'app', + { name: 'myApp', directory: 'myDir', inlineTemplate: true }, + appTree + ); + expect( + getFileContent(tree, 'apps/my-dir/my-app/src/app/app.component.ts') + ).toContain('This is an Angular CLI app built with Nrwl Nx!'); + }); + }); }); diff --git a/packages/schematics/src/collection/application/index.ts b/packages/schematics/src/collection/application/index.ts index 7be9f2baeb..7b89bd0b5f 100644 --- a/packages/schematics/src/collection/application/index.ts +++ b/packages/schematics/src/collection/application/index.ts @@ -13,7 +13,9 @@ import { insertImport } from '@schematics/angular/utility/ast-utils'; import { addImportToModule, addImportToTestBed, + getDecoratorPropertyValueNode, insert, + replaceNodeValue, updateJsonInTree } from '../../utils/ast-utils'; import { toFileName } from '../../utils/name-utils'; @@ -117,9 +119,27 @@ Nx is designed to help you create and build enterprise grade Angular application const content = options.routing ? `${baseContent}\n` : baseContent; - host.overwrite( - `${options.appProjectRoot}/src/app/app.component.html`, - content + + if (!options.inlineTemplate) { + return host.overwrite( + `${options.appProjectRoot}/src/app/app.component.html`, + content + ); + } + + const modulePath = `${options.appProjectRoot}/src/app/app.component.ts`; + const templateNodeValue = getDecoratorPropertyValueNode( + host, + modulePath, + 'Component', + 'template', + '@angular/core' + ); + replaceNodeValue( + host, + modulePath, + templateNodeValue, + `\`\n${baseContent}\n\`,\n` ); }; } diff --git a/packages/schematics/src/utils/ast-utils.ts b/packages/schematics/src/utils/ast-utils.ts index 8da3b178cb..ba95826b9d 100755 --- a/packages/schematics/src/utils/ast-utils.ts +++ b/packages/schematics/src/utils/ast-utils.ts @@ -238,7 +238,12 @@ export function removeFromNgModule( } // Get all the children property assignment of object literals. - const matchingProperty = getMatchingProperty(source, property); + const matchingProperty = getMatchingProperty( + source, + property, + 'NgModule', + '@angular/core' + ); if (matchingProperty) { return [ new RemoveChange( @@ -343,7 +348,12 @@ export function getBootstrapComponent( source: ts.SourceFile, moduleClassName: string ): string { - const bootstrap = getMatchingProperty(source, 'bootstrap'); + const bootstrap = getMatchingProperty( + source, + 'bootstrap', + 'NgModule', + '@angular/core' + ); if (!bootstrap) { throw new Error(`Cannot find bootstrap components in '${moduleClassName}'`); } @@ -383,9 +393,11 @@ function getMatchingObjectLiteralElement( function getMatchingProperty( source: ts.SourceFile, - property: string + property: string, + identifier: string, + module: string ): ts.ObjectLiteralElement { - const nodes = getDecoratorMetadata(source, 'NgModule', '@angular/core'); + const nodes = getDecoratorMetadata(source, identifier, module); let node: any = nodes[0]; // tslint:disable-line:no-any if (!node) return null; @@ -424,7 +436,12 @@ export function addIncludeToTsConfig( } function getListOfRoutes(source: ts.SourceFile): ts.NodeArray { - const imports: any = getMatchingProperty(source, 'imports'); + const imports: any = getMatchingProperty( + source, + 'imports', + 'NgModule', + '@angular/core' + ); if (imports.initializer.kind === ts.SyntaxKind.ArrayLiteralExpression) { const a = imports.initializer as ts.ArrayLiteralExpression; @@ -888,3 +905,38 @@ export function insertImport( ts.SyntaxKind.StringLiteral ); } + +export function getDecoratorPropertyValueNode( + host: Tree, + modulePath: string, + identifier: string, + property: string, + module: string +) { + const moduleSourceText = host.read(modulePath)!.toString('utf-8'); + const moduleSource = ts.createSourceFile( + modulePath, + moduleSourceText, + ts.ScriptTarget.Latest, + true + ); + const templateNode = getMatchingProperty( + moduleSource, + property, + identifier, + module + ); + + return templateNode.getChildAt(templateNode.getChildCount() - 1); +} + +export function replaceNodeValue( + host: Tree, + modulePath: string, + node: ts.Node, + content: string +) { + insert(host, modulePath, [ + new ReplaceChange(modulePath, node.pos, node.getFullText(), content) + ]); +}