From 878c64be1550eaa19f5fec0ebc92695ea290e8a5 Mon Sep 17 00:00:00 2001 From: Alan Date: Tue, 5 Mar 2019 08:12:01 +0100 Subject: [PATCH 1/4] feat(@schematics/angular): change layout for root applications This change aligns the file layout of applications generated with `ng new` and `ng generate` Ref: TOOL-686 --- .../files/{root => }/browserslist.template | 0 .../files/{root => }/karma.conf.js.template | 0 ...ot__gitkeep.template => .gitkeep.template} | 0 .../{root => }/tsconfig.app.json.template | 8 +- .../{root => }/tsconfig.spec.json.template | 8 +- .../__tsLintRoot__ => }/tslint.json.template | 5 +- .../schematics/angular/application/index.ts | 202 ++++++++++-------- .../angular/application/index_spec.ts | 38 ++-- .../angular/e2e/files/tsconfig.json.template | 2 +- packages/schematics/angular/e2e/index.ts | 4 +- .../schematics/angular/ng-new/index_spec.ts | 2 +- .../schematics/angular/universal/index.ts | 8 +- .../angular/universal/index_spec.ts | 8 +- .../angular/utility/workspace-models.ts | 2 +- .../workspace/files/tslint.json.template | 2 +- 15 files changed, 169 insertions(+), 120 deletions(-) rename packages/schematics/angular/application/files/{root => }/browserslist.template (100%) rename packages/schematics/angular/application/files/{root => }/karma.conf.js.template (100%) rename packages/schematics/angular/application/files/src/assets/{__dot__gitkeep.template => .gitkeep.template} (100%) rename packages/schematics/angular/application/files/{root => }/tsconfig.app.json.template (77%) rename packages/schematics/angular/application/files/{root => }/tsconfig.spec.json.template (65%) rename packages/schematics/angular/application/files/{lint/__tsLintRoot__ => }/tslint.json.template (73%) diff --git a/packages/schematics/angular/application/files/root/browserslist.template b/packages/schematics/angular/application/files/browserslist.template similarity index 100% rename from packages/schematics/angular/application/files/root/browserslist.template rename to packages/schematics/angular/application/files/browserslist.template diff --git a/packages/schematics/angular/application/files/root/karma.conf.js.template b/packages/schematics/angular/application/files/karma.conf.js.template similarity index 100% rename from packages/schematics/angular/application/files/root/karma.conf.js.template rename to packages/schematics/angular/application/files/karma.conf.js.template diff --git a/packages/schematics/angular/application/files/src/assets/__dot__gitkeep.template b/packages/schematics/angular/application/files/src/assets/.gitkeep.template similarity index 100% rename from packages/schematics/angular/application/files/src/assets/__dot__gitkeep.template rename to packages/schematics/angular/application/files/src/assets/.gitkeep.template diff --git a/packages/schematics/angular/application/files/root/tsconfig.app.json.template b/packages/schematics/angular/application/files/tsconfig.app.json.template similarity index 77% rename from packages/schematics/angular/application/files/root/tsconfig.app.json.template rename to packages/schematics/angular/application/files/tsconfig.app.json.template index d3cef1c5aa03..d24bbb988f70 100644 --- a/packages/schematics/angular/application/files/root/tsconfig.app.json.template +++ b/packages/schematics/angular/application/files/tsconfig.app.json.template @@ -4,10 +4,12 @@ "outDir": "<%= relativePathToWorkspaceRoot %>/out-tsc/app", "types": [] }, + "include": [ + "src/**/*.ts" + ], "exclude": [ - "test.ts", - "**/*.spec.ts", - "e2e/**" + "src/test.ts", + "src/**/*.spec.ts" ]<% if (enableIvy) { %>, "angularCompilerOptions": { "enableIvy": true diff --git a/packages/schematics/angular/application/files/root/tsconfig.spec.json.template b/packages/schematics/angular/application/files/tsconfig.spec.json.template similarity index 65% rename from packages/schematics/angular/application/files/root/tsconfig.spec.json.template rename to packages/schematics/angular/application/files/tsconfig.spec.json.template index d275c27a05df..72aa864c8732 100644 --- a/packages/schematics/angular/application/files/root/tsconfig.spec.json.template +++ b/packages/schematics/angular/application/files/tsconfig.spec.json.template @@ -8,11 +8,11 @@ ] }, "files": [ - "<%= rootInSrc ? '' : 'src/' %>test.ts", - "<%= rootInSrc ? '' : 'src/' %>polyfills.ts" + "src/test.ts", + "src/polyfills.ts" ], "include": [ - "**/*.spec.ts", - "**/*.d.ts" + "src/**/*.spec.ts", + "src/**/*.d.ts" ] } diff --git a/packages/schematics/angular/application/files/lint/__tsLintRoot__/tslint.json.template b/packages/schematics/angular/application/files/tslint.json.template similarity index 73% rename from packages/schematics/angular/application/files/lint/__tsLintRoot__/tslint.json.template rename to packages/schematics/angular/application/files/tslint.json.template index 1ebe192c95f2..b9ce956376d6 100644 --- a/packages/schematics/angular/application/files/lint/__tsLintRoot__/tslint.json.template +++ b/packages/schematics/angular/application/files/tslint.json.template @@ -1,5 +1,6 @@ -{ - "extends": "<%= relativePathToWorkspaceRoot %>/tslint.json", +{<% if (!isRootApp) { %> + "extends": "<%= relativePathToWorkspaceRoot %>/tslint.json",<% + } %> "rules": { "directive-selector": [ true, diff --git a/packages/schematics/angular/application/index.ts b/packages/schematics/angular/application/index.ts index 8d0b1eb22fa5..7a5d0ea19705 100644 --- a/packages/schematics/angular/application/index.ts +++ b/packages/schematics/angular/application/index.ts @@ -6,12 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ import { + JsonAstObject, JsonObject, JsonParseMode, join, normalize, parseJsonAst, - relative, strings, } from '@angular-devkit/core'; import { @@ -103,6 +103,78 @@ function addDependenciesToPackageJson(options: ApplicationOptions) { }; } +function readTsLintConfig(host: Tree, path: string): JsonAstObject { + const buffer = host.read(path); + if (!buffer) { + throw new SchematicsException(`Could not read ${path}.`); + } + + const config = parseJsonAst(buffer.toString(), JsonParseMode.Loose); + if (config.kind !== 'object') { + throw new SchematicsException(`Invalid ${path}. Was expecting an object.`); + } + + return config; +} + +/** + * Merges the application tslint.json with the workspace tslint.json + * when the application being created is a root application + * + * @param {Tree} parentHost The root host of the schematic + */ +function mergeWithRootTsLint(parentHost: Tree) { + return (host: Tree) => { + const tsLintPath = '/tslint.json'; + if (!host.exists(tsLintPath)) { + return; + } + + const rootTslintConfig = readTsLintConfig(parentHost, tsLintPath); + const appTslintConfig = readTsLintConfig(host, tsLintPath); + + const recorder = host.beginUpdate(tsLintPath); + rootTslintConfig.properties.forEach(prop => { + if (findPropertyInAstObject(appTslintConfig, prop.key.value)) { + // property already exists. Skip! + return; + } + + insertPropertyInAstObjectInOrder( + recorder, + appTslintConfig, + prop.key.value, + prop.value.value, + 2, + ); + }); + + const rootRules = findPropertyInAstObject(rootTslintConfig, 'rules'); + const appRules = findPropertyInAstObject(appTslintConfig, 'rules'); + + if (!appRules || appRules.kind !== 'object' || !rootRules || rootRules.kind !== 'object') { + // rules are not valid. Skip! + return; + } + + rootRules.properties.forEach(prop => { + insertPropertyInAstObjectInOrder( + recorder, + appRules, + prop.key.value, + prop.value.value, + 4, + ); + }); + + host.commitUpdate(recorder); + + // this shouldn't be needed but at the moment without this formatting is not correct. + const content = readTsLintConfig(host, tsLintPath); + host.overwrite(tsLintPath, JSON.stringify(content.value, undefined, 2)); + }; +} + function addPostInstallScript() { return (host: Tree) => { const pkgJsonPath = '/package.json'; @@ -148,15 +220,14 @@ function addAppToWorkspaceFile(options: ApplicationOptions, workspace: Workspace // if (workspaceJson.value === null) { // throw new SchematicsException(`Unable to parse configuration file (${workspacePath}).`); // } + let projectRoot = options.projectRoot !== undefined ? options.projectRoot - : `${workspace.newProjectRoot || ''}/${options.name}`; + : `${workspace.newProjectRoot}/${options.name}`; + if (projectRoot !== '' && !projectRoot.endsWith('/')) { projectRoot += '/'; } - const rootFilesRoot = options.projectRoot === undefined - ? projectRoot - : projectRoot + 'src/'; const schematics: JsonObject = {}; @@ -186,9 +257,10 @@ function addAppToWorkspaceFile(options: ApplicationOptions, workspace: Workspace }); } + const sourceRoot = join(normalize(projectRoot), 'src'); const project: WorkspaceProject = { root: projectRoot, - sourceRoot: join(normalize(projectRoot), 'src'), + sourceRoot, projectType: ProjectType.Application, prefix: options.prefix || 'app', schematics, @@ -197,16 +269,16 @@ function addAppToWorkspaceFile(options: ApplicationOptions, workspace: Workspace builder: Builders.Browser, options: { outputPath: `dist/${options.name}`, - index: `${projectRoot}src/index.html`, - main: `${projectRoot}src/main.ts`, - polyfills: `${projectRoot}src/polyfills.ts`, - tsConfig: `${rootFilesRoot}tsconfig.app.json`, + index: `${sourceRoot}/index.html`, + main: `${sourceRoot}/main.ts`, + polyfills: `${sourceRoot}/polyfills.ts`, + tsConfig: `${projectRoot}tsconfig.app.json`, assets: [ - join(normalize(projectRoot), 'src', 'favicon.ico'), - join(normalize(projectRoot), 'src', 'assets'), + `${sourceRoot}/favicon.ico`, + `${sourceRoot}/assets`, ], styles: [ - `${projectRoot}src/styles.${options.style}`, + `${sourceRoot}/styles.${options.style}`, ], scripts: [], es5BrowserSupport: true, @@ -214,8 +286,8 @@ function addAppToWorkspaceFile(options: ApplicationOptions, workspace: Workspace configurations: { production: { fileReplacements: [{ - replace: `${projectRoot}src/environments/environment.ts`, - with: `${projectRoot}src/environments/environment.prod.ts`, + replace: `${sourceRoot}/environments/environment.ts`, + with: `${sourceRoot}/environments/environment.prod.ts`, }], optimization: true, outputHashing: 'all', @@ -254,26 +326,26 @@ function addAppToWorkspaceFile(options: ApplicationOptions, workspace: Workspace test: { builder: Builders.Karma, options: { - main: `${projectRoot}src/test.ts`, - polyfills: `${projectRoot}src/polyfills.ts`, - tsConfig: `${rootFilesRoot}tsconfig.spec.json`, - karmaConfig: `${rootFilesRoot}karma.conf.js`, + main: `${sourceRoot}/test.ts`, + polyfills: `${sourceRoot}/polyfills.ts`, + tsConfig: `${projectRoot}tsconfig.spec.json`, + karmaConfig: `${projectRoot}karma.conf.js`, + assets: [ + `${sourceRoot}/favicon.ico`, + `${sourceRoot}/assets`, + ], styles: [ - `${projectRoot}src/styles.${options.style}`, + `${sourceRoot}/styles.${options.style}`, ], scripts: [], - assets: [ - join(normalize(projectRoot), 'src', 'favicon.ico'), - join(normalize(projectRoot), 'src', 'assets'), - ], }, }, lint: { builder: Builders.TsLint, options: { tsConfig: [ - `${rootFilesRoot}tsconfig.app.json`, - `${rootFilesRoot}tsconfig.spec.json`, + `${projectRoot}tsconfig.app.json`, + `${projectRoot}tsconfig.spec.json`, ], exclude: [ '**/node_modules/**', @@ -282,19 +354,12 @@ function addAppToWorkspaceFile(options: ApplicationOptions, workspace: Workspace }, }, }; - // tslint:disable-next-line:no-any - // const projects: JsonObject = ( workspaceAst.value).projects || {}; - // tslint:disable-next-line:no-any - // if (!( workspaceAst.value).projects) { - // // tslint:disable-next-line:no-any - // ( workspaceAst.value).projects = projects; - // } return addProjectToWorkspace(workspace, options.name, project); } function minimalPathFilter(path: string): boolean { - const toRemoveList = /(test.ts|tsconfig.spec.json|karma.conf.js).template$/; + const toRemoveList = /(test.ts|tsconfig.spec.json|karma.conf.js|tslint.json).template$/; return !toRemoveList.test(path); } @@ -305,8 +370,8 @@ export default function (options: ApplicationOptions): Rule { throw new SchematicsException(`Invalid options, "name" is required.`); } validateProjectName(options.name); - const prefix = options.prefix || 'app'; - const appRootSelector = `${prefix}-root`; + options.prefix = options.prefix || 'app'; + const appRootSelector = `${options.prefix}-root`; const componentOptions: Partial = !options.minimal ? { inlineStyle: options.inlineStyle, @@ -323,23 +388,15 @@ export default function (options: ApplicationOptions): Rule { }; const workspace = getWorkspace(host); - let newProjectRoot = workspace.newProjectRoot || ''; - let appDir = `${newProjectRoot}/${options.name}`; - let sourceRoot = `${appDir}/src`; - let sourceDir = `${sourceRoot}/app`; - let relativePathToWorkspaceRoot = appDir.split('/').map(x => '..').join('/'); - const rootInSrc = options.projectRoot !== undefined; - if (options.projectRoot !== undefined) { - newProjectRoot = options.projectRoot; - appDir = `${newProjectRoot}/src`; - sourceRoot = appDir; - sourceDir = `${sourceRoot}/app`; - relativePathToWorkspaceRoot = relative(normalize('/' + sourceRoot), normalize('/')); - if (relativePathToWorkspaceRoot === '') { - relativePathToWorkspaceRoot = '.'; - } - } - const tsLintRoot = appDir; + const newProjectRoot = workspace.newProjectRoot || ''; + const isRootApp = options.projectRoot !== undefined; + const appDir = isRootApp + ? options.projectRoot as string + : `${newProjectRoot}/${options.name}`; + const relativePathToWorkspaceRoot = appDir + ? appDir.split('/').map(() => '..').join('/') + : '.'; + const sourceDir = `${appDir}/src/app`; const e2eOptions: E2eOptions = { relatedAppName: options.name, @@ -349,43 +406,18 @@ export default function (options: ApplicationOptions): Rule { return chain([ addAppToWorkspaceFile(options, workspace), mergeWith( - apply(url('./files/src'), [ - options.minimal ? filter(minimalPathFilter) : noop(), - applyTemplates({ - utils: strings, - ...options, - 'dot': '.', - relativePathToWorkspaceRoot, - }), - move(sourceRoot), - ])), - mergeWith( - apply(url('./files/root'), [ + apply(url('./files'), [ options.minimal ? filter(minimalPathFilter) : noop(), applyTemplates({ utils: strings, ...options, - 'dot': '.', relativePathToWorkspaceRoot, - rootInSrc, appName: options.name, + isRootApp, }), + isRootApp ? mergeWithRootTsLint(host) : noop(), move(appDir), - ])), - options.minimal ? noop() : mergeWith( - apply(url('./files/lint'), [ - applyTemplates({ - utils: strings, - ...options, - tsLintRoot, - relativePathToWorkspaceRoot, - prefix, - }), - // TODO: Moving should work but is bugged right now. - // The __tsLintRoot__ is being used meanwhile. - // Otherwise the tslint.json file could be inside of the root folder and - // this block and the lint folder could be removed. - ])), + ]), MergeStrategy.Overwrite), schematic('module', { name: 'app', commonModule: false, @@ -410,11 +442,11 @@ export default function (options: ApplicationOptions): Rule { ? filter(path => !path.endsWith('.html.template')) : noop(), componentOptions.skipTests - ? filter(path => !/[.|-]spec.ts.template$/.test(path)) + ? filter(path => !path.endsWith('.spec.ts.template')) : noop(), applyTemplates({ utils: strings, - ...options as any, // tslint:disable-line:no-any + ...options, selector: appRootSelector, ...componentOptions, }), @@ -423,7 +455,7 @@ export default function (options: ApplicationOptions): Rule { options.minimal ? noop() : schematic('e2e', e2eOptions), options.enableIvy ? addPostInstallScript() : noop(), options.skipPackageJson ? noop() : addDependenciesToPackageJson(options), - options.lintFix ? applyLintFix(sourceDir) : noop(), + options.lintFix ? applyLintFix(appDir) : noop(), ]); }; } diff --git a/packages/schematics/angular/application/index_spec.ts b/packages/schematics/angular/application/index_spec.ts index efa28c282202..9555887e4838 100644 --- a/packages/schematics/angular/application/index_spec.ts +++ b/packages/schematics/angular/application/index_spec.ts @@ -253,10 +253,10 @@ describe('Application Schematic', () => { const tree = schematicRunner.runSchematic('application', options, workspaceTree); const files = tree.files; expect(files).toEqual(jasmine.arrayContaining([ - '/src/karma.conf.js', - '/src/tsconfig.app.json', - '/src/tsconfig.spec.json', - '/src/tslint.json', + '/karma.conf.js', + '/tsconfig.app.json', + '/tsconfig.spec.json', + '/tslint.json', '/src/environments/environment.ts', '/src/environments/environment.prod.ts', '/src/favicon.ico', @@ -284,12 +284,12 @@ describe('Application Schematic', () => { expect(buildOpt.index).toEqual('src/index.html'); expect(buildOpt.main).toEqual('src/main.ts'); expect(buildOpt.polyfills).toEqual('src/polyfills.ts'); - expect(buildOpt.tsConfig).toEqual('src/tsconfig.app.json'); + expect(buildOpt.tsConfig).toEqual('tsconfig.app.json'); const testOpt = prj.architect.test.options; expect(testOpt.main).toEqual('src/test.ts'); - expect(testOpt.tsConfig).toEqual('src/tsconfig.spec.json'); - expect(testOpt.karmaConfig).toEqual('src/karma.conf.js'); + expect(testOpt.tsConfig).toEqual('tsconfig.spec.json'); + expect(testOpt.karmaConfig).toEqual('karma.conf.js'); expect(testOpt.styles).toEqual([ 'src/styles.css', ]); @@ -314,21 +314,31 @@ describe('Application Schematic', () => { it('should set the relative tsconfig paths', () => { const options = { ...defaultOptions, projectRoot: '' }; const tree = schematicRunner.runSchematic('application', options, workspaceTree); - const appTsConfig = JSON.parse(tree.readContent('/src/tsconfig.app.json')); - expect(appTsConfig.extends).toEqual('../tsconfig.json'); - const specTsConfig = JSON.parse(tree.readContent('/src/tsconfig.spec.json')); - expect(specTsConfig.extends).toEqual('../tsconfig.json'); - expect(specTsConfig.files).toEqual(['test.ts', 'polyfills.ts']); + const appTsConfig = JSON.parse(tree.readContent('/tsconfig.app.json')); + expect(appTsConfig.extends).toEqual('./tsconfig.json'); + const specTsConfig = JSON.parse(tree.readContent('/tsconfig.spec.json')); + expect(specTsConfig.extends).toEqual('./tsconfig.json'); + expect(specTsConfig.files).toEqual(['src/test.ts', 'src/polyfills.ts']); }); it('should set the relative path and prefix in the tslint file', () => { const options = { ...defaultOptions, projectRoot: '' }; const tree = schematicRunner.runSchematic('application', options, workspaceTree); - const content = JSON.parse(tree.readContent('/src/tslint.json')); - expect(content.extends).toMatch('../tslint.json'); + const content = JSON.parse(tree.readContent('/tslint.json')); + expect(content.extends).toMatch('tslint:recommended'); expect(content.rules['directive-selector'][2]).toMatch('app'); expect(content.rules['component-selector'][2]).toMatch('app'); }); + + it('should merge tslint file', () => { + const options = { ...defaultOptions, projectRoot: '' }; + + const tree = schematicRunner.runSchematic('application', options, workspaceTree); + const content = JSON.parse(tree.readContent('/tslint.json')); + expect(content.extends).toMatch('tslint:recommended'); + expect(content.rules['component-selector'][2]).toMatch('app'); + expect(content.rules['trailing-comma']).toBeDefined(); + }); }); }); diff --git a/packages/schematics/angular/e2e/files/tsconfig.json.template b/packages/schematics/angular/e2e/files/tsconfig.json.template index 5df5f432cbca..cc3e6d1cb952 100644 --- a/packages/schematics/angular/e2e/files/tsconfig.json.template +++ b/packages/schematics/angular/e2e/files/tsconfig.json.template @@ -10,4 +10,4 @@ "node" ] } -} \ No newline at end of file +} diff --git a/packages/schematics/angular/e2e/index.ts b/packages/schematics/angular/e2e/index.ts index 9445f2288303..94a47bf8744f 100644 --- a/packages/schematics/angular/e2e/index.ts +++ b/packages/schematics/angular/e2e/index.ts @@ -5,7 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import { strings } from '@angular-devkit/core'; +import { normalize, strings } from '@angular-devkit/core'; import { Rule, SchematicContext, @@ -24,7 +24,7 @@ import { Builders, WorkspaceSchema } from '../utility/workspace-models'; import { Schema as E2eOptions } from './schema'; function getE2eRoot(projectRoot: string): string { - const root = projectRoot.split('/').filter(x => x).join('/'); + const root = normalize(projectRoot); return root ? root + '/e2e' : 'e2e'; } diff --git a/packages/schematics/angular/ng-new/index_spec.ts b/packages/schematics/angular/ng-new/index_spec.ts index 772ab5b1fd39..96396fc942ad 100644 --- a/packages/schematics/angular/ng-new/index_spec.ts +++ b/packages/schematics/angular/ng-new/index_spec.ts @@ -34,7 +34,7 @@ describe('Ng New Schematic', () => { const tree = schematicRunner.runSchematic('ng-new', options); const files = tree.files; expect(files).toEqual(jasmine.arrayContaining([ - '/bar/src/tsconfig.app.json', + '/bar/tsconfig.app.json', '/bar/src/main.ts', '/bar/src/app/app.module.ts', '/bar/e2e/src/app.po.ts', diff --git a/packages/schematics/angular/universal/index.ts b/packages/schematics/angular/universal/index.ts index 85886ab2bcae..d838d187ee16 100644 --- a/packages/schematics/angular/universal/index.ts +++ b/packages/schematics/angular/universal/index.ts @@ -248,8 +248,12 @@ export default function (options: UniversalOptions): Rule { if (!clientTargets.build) { throw targetBuildNotFoundError(); } - const tsConfigExtends = basename(normalize(clientTargets.build.options.tsConfig)); - const rootInSrc = clientProject.root === ''; + + const clientTsConfig = normalize(clientTargets.build.options.tsConfig); + const tsConfigExtends = basename(clientTsConfig); + // this is needed because prior to version 8, tsconfig might have been in 'src' + // and we don't want to break the 'ng add @nguniversal/express-engine schematics' + const rootInSrc = clientProject.root === '' && clientTsConfig.includes('src/'); const tsConfigDirectory = join(normalize(clientProject.root), rootInSrc ? 'src' : ''); if (!options.skipInstall) { diff --git a/packages/schematics/angular/universal/index_spec.ts b/packages/schematics/angular/universal/index_spec.ts index e91de58f7e66..9c788f0f591d 100644 --- a/packages/schematics/angular/universal/index_spec.ts +++ b/packages/schematics/angular/universal/index_spec.ts @@ -73,22 +73,22 @@ describe('Universal Schematic', () => { it('should create a tsconfig file for the workspace project', () => { const tree = schematicRunner.runSchematic('universal', workspaceUniversalOptions, appTree); - const filePath = '/src/tsconfig.server.json'; + const filePath = '/tsconfig.server.json'; expect(tree.exists(filePath)).toEqual(true); const contents = tree.readContent(filePath); expect(JSON.parse(contents)).toEqual({ extends: './tsconfig.app.json', compilerOptions: { - outDir: '../out-tsc/app-server', + outDir: './out-tsc/app-server', baseUrl: '.', }, angularCompilerOptions: { - entryModule: 'app/app.server.module#AppServerModule', + entryModule: 'src/app/app.server.module#AppServerModule', }, }); const angularConfig = JSON.parse(tree.readContent('angular.json')); expect(angularConfig.projects.workspace.architect - .server.options.tsConfig).toEqual('src/tsconfig.server.json'); + .server.options.tsConfig).toEqual('tsconfig.server.json'); }); it('should create a tsconfig file for a generated application', () => { diff --git a/packages/schematics/angular/utility/workspace-models.ts b/packages/schematics/angular/utility/workspace-models.ts index d697a4bb121d..3d226000317b 100644 --- a/packages/schematics/angular/utility/workspace-models.ts +++ b/packages/schematics/angular/utility/workspace-models.ts @@ -37,7 +37,7 @@ export interface BrowserBuilderBaseOptions { outputPath?: string; index?: string; polyfills: string; - assets?: object[]; + assets?: (object|string)[]; styles?: string[]; scripts?: string[]; sourceMap?: boolean; diff --git a/packages/schematics/angular/workspace/files/tslint.json.template b/packages/schematics/angular/workspace/files/tslint.json.template index 868ecba0db1b..e7e7c249a543 100644 --- a/packages/schematics/angular/workspace/files/tslint.json.template +++ b/packages/schematics/angular/workspace/files/tslint.json.template @@ -72,4 +72,4 @@ "component-class-suffix": true, "directive-class-suffix": true } -} +} \ No newline at end of file From 4eebf5c1fb76ac52c20126a3997b4be0677a5b35 Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Tue, 5 Mar 2019 08:58:08 +0100 Subject: [PATCH 2/4] test: update e2e to support new `ng new` layout --- tests/legacy-cli/e2e/tests/basic/test.ts | 5 +- .../e2e/tests/build/build-app-shell.ts | 21 ++-- .../e2e/tests/build/build-errors.ts | 109 ++++++++++-------- .../e2e/tests/build/platform-server.ts | 32 ++--- .../e2e/tests/commands/new/new-minimal.ts | 38 +++--- .../e2e/tests/i18n/extract-default.ts | 26 ++--- .../e2e/tests/i18n/extract-locale.ts | 2 +- .../e2e/tests/i18n/extract-outfile.ts | 2 +- .../legacy-cli/e2e/tests/i18n/extract-xmb.ts | 10 +- .../e2e/tests/misc/different-file-format.ts | 2 +- tests/legacy-cli/e2e/utils/project.ts | 4 +- 11 files changed, 121 insertions(+), 130 deletions(-) diff --git a/tests/legacy-cli/e2e/tests/basic/test.ts b/tests/legacy-cli/e2e/tests/basic/test.ts index c95a026b867c..c3cd6a3d36f6 100644 --- a/tests/legacy-cli/e2e/tests/basic/test.ts +++ b/tests/legacy-cli/e2e/tests/basic/test.ts @@ -5,7 +5,6 @@ import { moveFile } from '../../utils/fs'; export default function () { // make sure both --watch=false work return ng('test', '--watch=false') - .then(() => moveFile('./src/karma.conf.js', - './src/karma.conf.bis.js')) - .then(() => ng('test', '--watch=false', '--karmaConfig=src/karma.conf.bis.js')); + .then(() => moveFile('./karma.conf.js', './karma.conf.bis.js')) + .then(() => ng('test', '--watch=false', '--karmaConfig=karma.conf.bis.js')); } diff --git a/tests/legacy-cli/e2e/tests/build/build-app-shell.ts b/tests/legacy-cli/e2e/tests/build/build-app-shell.ts index ee8fe2b672cd..70c0b8b8498d 100644 --- a/tests/legacy-cli/e2e/tests/build/build-app-shell.ts +++ b/tests/legacy-cli/e2e/tests/build/build-app-shell.ts @@ -1,10 +1,9 @@ -import { ng, npm } from '../../utils/process'; -import { expectFileToMatch, writeFile } from '../../utils/fs'; +import { stripIndent } from 'common-tags'; import { getGlobalVariable } from '../../utils/env'; -import { expectToFail } from '../../utils/utils'; +import { expectFileToMatch, writeFile } from '../../utils/fs'; +import { ng, npm } from '../../utils/process'; import { updateJsonFile } from '../../utils/project'; import { readNgVersion } from '../../utils/version'; -import { stripIndent } from 'common-tags'; export default function () { @@ -29,7 +28,7 @@ export default function () { options: { outputPath: 'dist/test-project-server', main: 'src/main.server.ts', - tsConfig: 'src/tsconfig.server.json' + tsConfig: 'tsconfig.server.json' } }; appArchitect['app-shell'] = { @@ -41,21 +40,17 @@ export default function () { } }; })) - .then(() => writeFile('./src/tsconfig.server.json', ` + .then(() => writeFile('./tsconfig.server.json', ` { - "extends": "../tsconfig.json", + "extends": "./tsconfig.app.json", "compilerOptions": { "outDir": "../dist-server", "baseUrl": "./", "module": "commonjs", "types": [] }, - "exclude": [ - "test.ts", - "**/*.spec.ts" - ], "angularCompilerOptions": { - "entryModule": "app/app.server.module#AppServerModule" + "entryModule": "src/app/app.server.module#AppServerModule" } } `)) @@ -137,4 +132,4 @@ export default function () { .then(() => npm('install'))) .then(() => ng('run', 'test-project:app-shell')) .then(() => expectFileToMatch('dist/test-project/index.html', /shell Works!/)); -} +} \ No newline at end of file diff --git a/tests/legacy-cli/e2e/tests/build/build-errors.ts b/tests/legacy-cli/e2e/tests/build/build-errors.ts index 66f4b36807ee..ccfe80b7c117 100644 --- a/tests/legacy-cli/e2e/tests/build/build-errors.ts +++ b/tests/legacy-cli/e2e/tests/build/build-errors.ts @@ -12,7 +12,7 @@ const extraErrors = [ `main.ts is not part of the TypeScript compilation.`, ]; -export default function () { +export default function() { // TODO(architect): Delete this test. It is now in devkit/build-angular. if (process.platform.startsWith('win')) { @@ -26,52 +26,61 @@ export default function () { let origContent: string; - return Promise.resolve() - // Save the original contents of `./src/app/app.component.ts`. - .then(() => readFile('./src/app/app.component.ts')) - .then((contents) => origContent = contents) - // Check `part of the TypeScript compilation` errors. - // These should show an error only for the missing file. - .then(() => updateJsonFile('./src/tsconfig.app.json', configJson => { - configJson.files = ['main.ts']; - })) - .then(() => expectToFail(() => ng('build'))) - .then(({ message }) => { - if (!message.includes('polyfills.ts is missing from the TypeScript compilation')) { - throw new Error(`Expected missing TS file error, got this instead:\n${message}`); - } - if (extraErrors.some((e) => message.includes(e))) { - throw new Error(`Did not expect extra errors but got:\n${message}`); - } - }) - .then(() => updateJsonFile('./src/tsconfig.app.json', configJson => { - configJson.files = undefined; - })) - // Check simple single syntax errors. - // These shouldn't skip emit and just show a TS error. - .then(() => appendToFile('./src/app/app.component.ts', ']]]')) - .then(() => expectToFail(() => ng('build'))) - .then(({ message }) => { - if (!message.includes('Declaration or statement expected.')) { - throw new Error(`Expected syntax error, got this instead:\n${message}`); - } - if (extraErrors.some((e) => message.includes(e))) { - throw new Error(`Did not expect extra errors but got:\n${message}`); - } - }) - .then(() => writeFile('./src/app/app.component.ts', origContent)) - // Check errors when files were not emitted due to static analysis errors. - .then(() => replaceInFile('./src/app/app.component.ts', `'app-root'`, `(() => 'app-root')()`)) - .then(() => expectToFail(() => ng('build', '--aot'))) - .then(({ message }) => { - if (!message.includes('Function calls are not supported') - && !message.includes('Function expressions are not supported in decorators') - && !message.includes('selector must be a string')) { - throw new Error(`Expected static analysis error, got this instead:\n${message}`); - } - if (extraErrors.some((e) => message.includes(e))) { - throw new Error(`Did not expect extra errors but got:\n${message}`); - } - }) - .then(() => writeFile('./src/app/app.component.ts', origContent)); -} + return ( + Promise.resolve() + // Save the original contents of `./src/app/app.component.ts`. + .then(() => readFile('./src/app/app.component.ts')) + .then(contents => (origContent = contents)) + // Check `part of the TypeScript compilation` errors. + // These should show an error only for the missing file. + .then(() => + updateJsonFile('./tsconfig.app.json', configJson => { + (configJson.include = undefined), (configJson.files = ['src/main.ts']); + }), + ) + .then(() => expectToFail(() => ng('build'))) + .then(({ message }) => { + if (!message.includes('polyfills.ts is missing from the TypeScript compilation')) { + throw new Error(`Expected missing TS file error, got this instead:\n${message}`); + } + if (extraErrors.some(e => message.includes(e))) { + throw new Error(`Did not expect extra errors but got:\n${message}`); + } + }) + .then(() => + updateJsonFile('./tsconfig.app.json', configJson => { + configJson.include = ['src/**/*.ts']; + configJson.files = undefined; + }), + ) + // Check simple single syntax errors. + // These shouldn't skip emit and just show a TS error. + .then(() => appendToFile('./src/app/app.component.ts', ']]]')) + .then(() => expectToFail(() => ng('build'))) + .then(({ message }) => { + if (!message.includes('Declaration or statement expected.')) { + throw new Error(`Expected syntax error, got this instead:\n${message}`); + } + if (extraErrors.some(e => message.includes(e))) { + throw new Error(`Did not expect extra errors but got:\n${message}`); + } + }) + .then(() => writeFile('./src/app/app.component.ts', origContent)) + // Check errors when files were not emitted due to static analysis errors. + .then(() => replaceInFile('./src/app/app.component.ts', `'app-root'`, `(() => 'app-root')()`)) + .then(() => expectToFail(() => ng('build', '--aot'))) + .then(({ message }) => { + if ( + !message.includes('Function calls are not supported') && + !message.includes('Function expressions are not supported in decorators') && + !message.includes('selector must be a string') + ) { + throw new Error(`Expected static analysis error, got this instead:\n${message}`); + } + if (extraErrors.some(e => message.includes(e))) { + throw new Error(`Did not expect extra errors but got:\n${message}`); + } + }) + .then(() => writeFile('./src/app/app.component.ts', origContent)) + ); +} \ No newline at end of file diff --git a/tests/legacy-cli/e2e/tests/build/platform-server.ts b/tests/legacy-cli/e2e/tests/build/platform-server.ts index 3c7ab71a42b7..fd471cf2b755 100644 --- a/tests/legacy-cli/e2e/tests/build/platform-server.ts +++ b/tests/legacy-cli/e2e/tests/build/platform-server.ts @@ -1,17 +1,14 @@ import { normalize } from 'path'; - -import { updateJsonFile, updateTsConfig } from '../../utils/project'; +import { getGlobalVariable } from '../../utils/env'; import { + appendToFile, expectFileToMatch, - writeFile, - replaceInFile, prependToFile, - appendToFile, + writeFile, } from '../../utils/fs'; -import { ng, silentNpm, exec } from '../../utils/process'; -import { getGlobalVariable } from '../../utils/env'; +import { exec, ng, silentNpm } from '../../utils/process'; +import { updateJsonFile } from '../../utils/project'; import { readNgVersion } from '../../utils/version'; -import { expectToFail } from '../../utils/utils'; export default function () { let platformServerVersion = readNgVersion(); @@ -41,25 +38,21 @@ export default function () { options: { outputPath: 'dist/test-project-server', main: 'src/main.server.ts', - tsConfig: 'src/tsconfig.server.json', + tsConfig: 'tsconfig.server.json', }, }; })) - .then(() => writeFile('./src/tsconfig.server.json', ` + .then(() => writeFile('./tsconfig.server.json', ` { - "extends": "../tsconfig.json", + "extends": "./tsconfig.app.json", "compilerOptions": { "outDir": "../dist-server", "baseUrl": "./", "module": "commonjs", "types": [] }, - "exclude": [ - "test.ts", - "**/*.spec.ts" - ], "angularCompilerOptions": { - "entryModule": "app/app.server.module#AppServerModule" + "entryModule": "src/app/app.server.module#AppServerModule" } } `)) @@ -155,9 +148,4 @@ export default function () { fs.writeFileSync('dist/test-project-server/index.html', html); \}); `))) - // This part of the test requires a non-aot build, which isn't available anymore. - // .then(() => ng('run', 'test-project:server', '--bundle-dependencies=all')) - // .then(() => expectToFail(() => expectFileToMatch('./dist/test-project-server/main.js', - // /require\(["']@angular\/[^"']*["']\)/))) - // .then(() => exec(normalize('node'), 'dist/test-project-server/main.js')); -} +} \ No newline at end of file diff --git a/tests/legacy-cli/e2e/tests/commands/new/new-minimal.ts b/tests/legacy-cli/e2e/tests/commands/new/new-minimal.ts index c12d47fd6101..8134d2490179 100644 --- a/tests/legacy-cli/e2e/tests/commands/new/new-minimal.ts +++ b/tests/legacy-cli/e2e/tests/commands/new/new-minimal.ts @@ -6,24 +6,26 @@ import {expectToFail} from '../../../utils/utils'; export default function() { // TODO(architect): re-enable after figuring out how a new minimal project looks like. - return Promise.resolve() - .then(() => createProject('minimal-project', '--minimal')) - .then(() => expectFileNotToExist('.editorconfig')) - .then(() => expectFileNotToExist('README.md')) - .then(() => expectFileNotToExist('karma.conf.js')) - .then(() => expectFileNotToExist('protractor.conf.js')) - .then(() => expectFileNotToExist('src/test.ts')) - .then(() => expectFileNotToExist('src/tsconfig.spec.json')) - .then(() => expectFileNotToExist('tslint.json')) - .then(() => expectFileNotToExist('src/app/app.component.html')) - .then(() => expectFileNotToExist('src/app/app.component.css')) - .then(() => expectFileNotToExist('src/app/app.component.spec.ts')) - .then(() => expectFileNotToExist('src/app/favicon.ico')) + return ( + Promise.resolve() + .then(() => createProject('minimal-project', '--minimal')) + .then(() => expectFileNotToExist('.editorconfig')) + .then(() => expectFileNotToExist('README.md')) + .then(() => expectFileNotToExist('karma.conf.js')) + .then(() => expectFileNotToExist('protractor.conf.js')) + .then(() => expectFileNotToExist('src/test.ts')) + .then(() => expectFileNotToExist('tsconfig.spec.json')) + .then(() => expectFileNotToExist('tslint.json')) + .then(() => expectFileNotToExist('src/app/app.component.html')) + .then(() => expectFileNotToExist('src/app/app.component.css')) + .then(() => expectFileNotToExist('src/app/app.component.spec.ts')) + .then(() => expectFileNotToExist('src/app/favicon.ico')) - .then(() => expectToFail(() => expectFileToMatch('package.json', '"protractor":'))) - .then(() => expectToFail(() => expectFileToMatch('package.json', '"karma":'))) - .then(() => expectToFail(() => expectFileToMatch('package.json', '"jasmine-core":'))) + .then(() => expectToFail(() => expectFileToMatch('package.json', '"protractor":'))) + .then(() => expectToFail(() => expectFileToMatch('package.json', '"karma":'))) + .then(() => expectToFail(() => expectFileToMatch('package.json', '"jasmine-core":'))) - // Try to run a build. - .then(() => ng('build')); + // Try to run a build. + .then(() => ng('build')) + ); } diff --git a/tests/legacy-cli/e2e/tests/i18n/extract-default.ts b/tests/legacy-cli/e2e/tests/i18n/extract-default.ts index af1c0330a396..fb7a2d135993 100644 --- a/tests/legacy-cli/e2e/tests/i18n/extract-default.ts +++ b/tests/legacy-cli/e2e/tests/i18n/extract-default.ts @@ -1,19 +1,17 @@ -import {join} from 'path'; -import {ng} from '../../utils/process'; -import { - expectFileToExist, writeFile, - expectFileToMatch -} from '../../utils/fs'; +import { join } from 'path'; +import { expectFileToExist, expectFileToMatch, writeFile } from '../../utils/fs'; +import { ng } from '../../utils/process'; -export default function() { +export default async function() { // TODO(architect): Delete this test. It is now in devkit/build-angular. - return ng('generate', 'component', 'i18n-test') - .then(() => writeFile( - join('src/app/i18n-test', 'i18n-test.component.html'), - '

Hello world

')) - .then(() => ng('xi18n')) - .then(() => expectFileToExist(join('src', 'messages.xlf'))) - .then(() => expectFileToMatch(join('src', 'messages.xlf'), /Hello world/)); + await ng('generate', 'component', 'i18n-test'); + await writeFile( + join('src/app/i18n-test', 'i18n-test.component.html'), + '

Hello world

', + ); + await ng('xi18n'); + await expectFileToExist('messages.xlf'); + await expectFileToMatch('messages.xlf', /Hello world/); } diff --git a/tests/legacy-cli/e2e/tests/i18n/extract-locale.ts b/tests/legacy-cli/e2e/tests/i18n/extract-locale.ts index de09da9a8e1e..5c1b5e05bc09 100644 --- a/tests/legacy-cli/e2e/tests/i18n/extract-locale.ts +++ b/tests/legacy-cli/e2e/tests/i18n/extract-locale.ts @@ -13,7 +13,7 @@ export default function() { .then(() => ng('xi18n', '--i18n-locale', 'fr')) .then((output) => { if (!output.stderr.match(/starting from Angular v4/)) { - return expectFileToMatch('src/messages.xlf', 'source-language="fr"'); + return expectFileToMatch('messages.xlf', 'source-language="fr"'); } }); } diff --git a/tests/legacy-cli/e2e/tests/i18n/extract-outfile.ts b/tests/legacy-cli/e2e/tests/i18n/extract-outfile.ts index b431e953ef2e..5daf48417c98 100644 --- a/tests/legacy-cli/e2e/tests/i18n/extract-outfile.ts +++ b/tests/legacy-cli/e2e/tests/i18n/extract-outfile.ts @@ -13,7 +13,7 @@ export default function() { .then(() => ng('xi18n', '--out-file', 'messages.fr.xlf')) .then((output) => { if (!output.stdout.match(/starting from Angular v4/)) { - return expectFileToMatch('src/messages.fr.xlf', 'Hello world'); + return expectFileToMatch('messages.fr.xlf', 'Hello world'); } }); } diff --git a/tests/legacy-cli/e2e/tests/i18n/extract-xmb.ts b/tests/legacy-cli/e2e/tests/i18n/extract-xmb.ts index f03a37fcd606..e931ec8657ec 100644 --- a/tests/legacy-cli/e2e/tests/i18n/extract-xmb.ts +++ b/tests/legacy-cli/e2e/tests/i18n/extract-xmb.ts @@ -10,10 +10,10 @@ export default function() { // TODO(architect): Delete this test. It is now in devkit/build-angular. return ng('generate', 'component', 'i18n-test') - .then(() => writeFile( - join('src/app/i18n-test', 'i18n-test.component.html'), - '

Hello world

')) + .then(() => + writeFile(join('src/app/i18n-test', 'i18n-test.component.html'), '

Hello world

'), + ) .then(() => ng('xi18n', '--i18n-format', 'xmb')) - .then(() => expectFileToExist('src/messages.xmb')) - .then(() => expectFileToMatch('src/messages.xmb', /Hello world/)); + .then(() => expectFileToExist('messages.xmb')) + .then(() => expectFileToMatch('messages.xmb', /Hello world/)); } diff --git a/tests/legacy-cli/e2e/tests/misc/different-file-format.ts b/tests/legacy-cli/e2e/tests/misc/different-file-format.ts index 47ffe11c0943..60b544bc97a6 100644 --- a/tests/legacy-cli/e2e/tests/misc/different-file-format.ts +++ b/tests/legacy-cli/e2e/tests/misc/different-file-format.ts @@ -9,7 +9,7 @@ const options = { export default function() { return Promise.resolve() - .then(() => fs.prependToFile('./src/tsconfig.app.json', '\ufeff', options)) + .then(() => fs.prependToFile('./tsconfig.app.json', '\ufeff', options)) .then(() => fs.prependToFile('angular.json', '\ufeff', options)) .then(() => ng('build', '--environment=dev')); } diff --git a/tests/legacy-cli/e2e/utils/project.ts b/tests/legacy-cli/e2e/utils/project.ts index 1a64118a99c4..af3c7e13133a 100644 --- a/tests/legacy-cli/e2e/utils/project.ts +++ b/tests/legacy-cli/e2e/utils/project.ts @@ -55,7 +55,7 @@ export async function prepareProjectForE2e(name) { await git('config', 'commit.gpgSign', 'false'); await useBuiltPackages(); await useCIChrome('e2e'); - await useCIChrome('src'); + await useCIChrome(''); await useDevKitSnapshots(); await argv['ng2'] ? useNg2() : Promise.resolve(); await argv['ng4'] ? useNg4() : Promise.resolve(); @@ -224,7 +224,7 @@ export function useCIDefaults(projectName = 'test-project') { export function useCIChrome(projectDir: string) { const protractorConf = `${projectDir}/protractor.conf.js`; - const karmaConf = `${projectDir}/karma.conf.js`; + const karmaConf = `${projectDir ? projectDir + '/' : ''}karma.conf.js`; return Promise.resolve() .then(() => updateJsonFile('package.json', json => { From 84c21078c814de905c995fd29cae1db3efc9d18f Mon Sep 17 00:00:00 2001 From: Alan Date: Tue, 5 Mar 2019 09:49:11 +0100 Subject: [PATCH 3/4] style: format touched files in e2e --- .../e2e/tests/build/build-app-shell.ts | 114 +++++++---- .../e2e/tests/build/platform-server.ts | 179 +++++++++++------- 2 files changed, 182 insertions(+), 111 deletions(-) diff --git a/tests/legacy-cli/e2e/tests/build/build-app-shell.ts b/tests/legacy-cli/e2e/tests/build/build-app-shell.ts index 70c0b8b8498d..8292aff13260 100644 --- a/tests/legacy-cli/e2e/tests/build/build-app-shell.ts +++ b/tests/legacy-cli/e2e/tests/build/build-app-shell.ts @@ -5,8 +5,7 @@ import { ng, npm } from '../../utils/process'; import { updateJsonFile } from '../../utils/project'; import { readNgVersion } from '../../utils/version'; - -export default function () { +export default function() { // Skip this test in Angular 2/4. if (getGlobalVariable('argv').ng2 || getGlobalVariable('argv').ng4) { return Promise.resolve(); @@ -21,26 +20,31 @@ export default function () { } return Promise.resolve() - .then(() => updateJsonFile('angular.json', workspaceJson => { - const appArchitect = workspaceJson.projects['test-project'].architect; - appArchitect['server'] = { - builder: '@angular-devkit/build-angular:server', - options: { - outputPath: 'dist/test-project-server', - main: 'src/main.server.ts', - tsConfig: 'tsconfig.server.json' - } - }; - appArchitect['app-shell'] = { - builder: '@angular-devkit/build-angular:app-shell', - options: { - browserTarget: 'test-project:build:production', - serverTarget: 'test-project:server', - route: '/shell' - } - }; - })) - .then(() => writeFile('./tsconfig.server.json', ` + .then(() => + updateJsonFile('angular.json', workspaceJson => { + const appArchitect = workspaceJson.projects['test-project'].architect; + appArchitect['server'] = { + builder: '@angular-devkit/build-angular:server', + options: { + outputPath: 'dist/test-project-server', + main: 'src/main.server.ts', + tsConfig: 'tsconfig.server.json', + }, + }; + appArchitect['app-shell'] = { + builder: '@angular-devkit/build-angular:app-shell', + options: { + browserTarget: 'test-project:build:production', + serverTarget: 'test-project:server', + route: '/shell', + }, + }; + }), + ) + .then(() => + writeFile( + './tsconfig.server.json', + ` { "extends": "./tsconfig.app.json", "compilerOptions": { @@ -53,8 +57,13 @@ export default function () { "entryModule": "src/app/app.server.module#AppServerModule" } } - `)) - .then(() => writeFile('./src/main.server.ts', ` + `, + ), + ) + .then(() => + writeFile( + './src/main.server.ts', + ` import { enableProdMode } from '@angular/core'; import { environment } from './environments/environment'; @@ -64,12 +73,22 @@ export default function () { } export { AppServerModule } from './app/app.server.module'; - `)) - .then(() => writeFile('./src/app/app.component.html', stripIndent` + `, + ), + ) + .then(() => + writeFile( + './src/app/app.component.html', + stripIndent` Hello World - `)) - .then(() => writeFile('./src/app/app.module.ts', stripIndent` + `, + ), + ) + .then(() => + writeFile( + './src/app/app.module.ts', + stripIndent` import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; @@ -85,8 +104,13 @@ export default function () { bootstrap: [AppComponent] }) export class AppModule { } - `)) - .then(() => writeFile('./src/app/app.server.module.ts', stripIndent` + `, + ), + ) + .then(() => + writeFile( + './src/app/app.server.module.ts', + stripIndent` import {NgModule} from '@angular/core'; import {ServerModule} from '@angular/platform-server'; import { Routes, RouterModule } from '@angular/router'; @@ -113,8 +137,13 @@ export default function () { declarations: [ShellComponent], }) export class AppServerModule {} - `)) - .then(() => writeFile('./src/app/shell.component.ts', stripIndent` + `, + ), + ) + .then(() => + writeFile( + './src/app/shell.component.ts', + stripIndent` import { Component } from '@angular/core'; @Component({ selector: 'app-shell', @@ -122,14 +151,17 @@ export default function () { styles: [] }) export class ShellComponent {} - `)) - .then(() => updateJsonFile('package.json', packageJson => { - const dependencies = packageJson['dependencies']; - dependencies['@angular/platform-server'] = platformServerVersion; - // ServerModule depends on @angular/http regardless the app's dependency. - dependencies['@angular/http'] = httpVersion; - }) - .then(() => npm('install'))) + `, + ), + ) + .then(() => + updateJsonFile('package.json', packageJson => { + const dependencies = packageJson['dependencies']; + dependencies['@angular/platform-server'] = platformServerVersion; + // ServerModule depends on @angular/http regardless the app's dependency. + dependencies['@angular/http'] = httpVersion; + }).then(() => npm('install')), + ) .then(() => ng('run', 'test-project:app-shell')) .then(() => expectFileToMatch('dist/test-project/index.html', /shell Works!/)); -} \ No newline at end of file +} diff --git a/tests/legacy-cli/e2e/tests/build/platform-server.ts b/tests/legacy-cli/e2e/tests/build/platform-server.ts index fd471cf2b755..0440ffbeaccb 100644 --- a/tests/legacy-cli/e2e/tests/build/platform-server.ts +++ b/tests/legacy-cli/e2e/tests/build/platform-server.ts @@ -1,16 +1,11 @@ import { normalize } from 'path'; import { getGlobalVariable } from '../../utils/env'; -import { - appendToFile, - expectFileToMatch, - prependToFile, - writeFile, -} from '../../utils/fs'; +import { appendToFile, expectFileToMatch, prependToFile, writeFile } from '../../utils/fs'; import { exec, ng, silentNpm } from '../../utils/process'; import { updateJsonFile } from '../../utils/project'; import { readNgVersion } from '../../utils/version'; -export default function () { +export default function() { let platformServerVersion = readNgVersion(); let httpVersion = readNgVersion(); @@ -24,25 +19,33 @@ export default function () { return Promise.resolve(); } - return Promise.resolve() - .then(() => updateJsonFile('package.json', packageJson => { - const dependencies = packageJson['dependencies']; - dependencies['@angular/platform-server'] = platformServerVersion; - // ServerModule depends on @angular/http regardless the app's dependency. - dependencies['@angular/http'] = httpVersion; - })) - .then(() => updateJsonFile('angular.json', workspaceJson => { - const appArchitect = workspaceJson.projects['test-project'].architect; - appArchitect['server'] = { - builder: '@angular-devkit/build-angular:server', - options: { - outputPath: 'dist/test-project-server', - main: 'src/main.server.ts', - tsConfig: 'tsconfig.server.json', - }, - }; - })) - .then(() => writeFile('./tsconfig.server.json', ` + return ( + Promise.resolve() + .then(() => + updateJsonFile('package.json', packageJson => { + const dependencies = packageJson['dependencies']; + dependencies['@angular/platform-server'] = platformServerVersion; + // ServerModule depends on @angular/http regardless the app's dependency. + dependencies['@angular/http'] = httpVersion; + }), + ) + .then(() => + updateJsonFile('angular.json', workspaceJson => { + const appArchitect = workspaceJson.projects['test-project'].architect; + appArchitect['server'] = { + builder: '@angular-devkit/build-angular:server', + options: { + outputPath: 'dist/test-project-server', + main: 'src/main.server.ts', + tsConfig: 'tsconfig.server.json', + }, + }; + }), + ) + .then(() => + writeFile( + './tsconfig.server.json', + ` { "extends": "./tsconfig.app.json", "compilerOptions": { @@ -55,8 +58,13 @@ export default function () { "entryModule": "src/app/app.server.module#AppServerModule" } } - `)) - .then(() => writeFile('./src/main.server.ts', ` + `, + ), + ) + .then(() => + writeFile( + './src/main.server.ts', + ` import { enableProdMode } from '@angular/core'; import { environment } from './environments/environment'; @@ -66,8 +74,13 @@ export default function () { } export { AppServerModule } from './app/app.server.module'; - `)) - .then(() => writeFile('./src/app/app.server.module.ts', ` + `, + ), + ) + .then(() => + writeFile( + './src/app/app.server.module.ts', + ` import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { ServerModule } from '@angular/platform-server'; @@ -84,35 +97,41 @@ export default function () { bootstrap: [AppComponent], }) export class AppServerModule {} - `)) - .then(() => silentNpm('install')) - // This part of the test requires a non-aot build, which isn't available anymore. - // .then(() => ng('run', 'test-project:server')) - // // files were created successfully - // .then(() => expectFileToMatch('dist/test-project-server/main.js', - // /exports.*AppServerModule/)) - // .then(() => writeFile('./index.js', ` - // require('zone.js/dist/zone-node'); - // require('reflect-metadata'); - // const fs = require('fs'); - // const \{ AppServerModule \} = require('./dist/test-project-server/main'); - // const \{ renderModule \} = require('@angular/platform-server'); + `, + ), + ) + .then(() => silentNpm('install')) + // This part of the test requires a non-aot build, which isn't available anymore. + // .then(() => ng('run', 'test-project:server')) + // // files were created successfully + // .then(() => expectFileToMatch('dist/test-project-server/main.js', + // /exports.*AppServerModule/)) + // .then(() => writeFile('./index.js', ` + // require('zone.js/dist/zone-node'); + // require('reflect-metadata'); + // const fs = require('fs'); + // const \{ AppServerModule \} = require('./dist/test-project-server/main'); + // const \{ renderModule \} = require('@angular/platform-server'); - // renderModule(AppServerModule, \{ - // url: '/', - // document: '' - // \}).then(html => \{ - // fs.writeFileSync('dist/test-project-server/index.html', html); - // \}); - // `)) - // .then(() => exec(normalize('node'), 'index.js')) - // .then(() => expectFileToMatch('dist/test-project-server/index.html', - // new RegExp('

Here are some links to help you start:

'))) - .then(() => ng('run', 'test-project:server')) - // files were created successfully - .then(() => expectFileToMatch('dist/test-project-server/main.js', - /exports.*AppServerModuleNgFactory/)) - .then(() => writeFile('./index.js', ` + // renderModule(AppServerModule, \{ + // url: '/', + // document: '' + // \}).then(html => \{ + // fs.writeFileSync('dist/test-project-server/index.html', html); + // \}); + // `)) + // .then(() => exec(normalize('node'), 'index.js')) + // .then(() => expectFileToMatch('dist/test-project-server/index.html', + // new RegExp('

Here are some links to help you start:

'))) + .then(() => ng('run', 'test-project:server')) + // files were created successfully + .then(() => + expectFileToMatch('dist/test-project-server/main.js', /exports.*AppServerModuleNgFactory/), + ) + .then(() => + writeFile( + './index.js', + ` require('zone.js/dist/zone-node'); require('reflect-metadata'); const fs = require('fs'); @@ -125,19 +144,35 @@ export default function () { \}).then(html => \{ fs.writeFileSync('dist/test-project-server/index.html', html); \}); - `)) - .then(() => exec(normalize('node'), 'index.js')) - .then(() => expectFileToMatch('dist/test-project-server/index.html', - /Here are some links to help you start: <\/h2>/)) - .then(() => expectFileToMatch('./dist/test-project-server/main.js', - /require\(["']@angular\/[^"']*["']\)/)) + `, + ), + ) + .then(() => exec(normalize('node'), 'index.js')) + .then(() => + expectFileToMatch( + 'dist/test-project-server/index.html', + /Here are some links to help you start: <\/h2>/, + ), + ) + .then(() => + expectFileToMatch( + './dist/test-project-server/main.js', + /require\(["']@angular\/[^"']*["']\)/, + ), + ) - // Check externals. - .then(() => prependToFile('./src/app/app.server.module.ts', ` + // Check externals. + .then(() => + prependToFile( + './src/app/app.server.module.ts', + ` import 'zone.js/dist/zone-node'; import 'reflect-metadata'; - `) - .then(() => appendToFile('./src/app/app.server.module.ts', ` + `, + ).then(() => + appendToFile( + './src/app/app.server.module.ts', + ` import * as fs from 'fs'; import { renderModule } from '@angular/platform-server'; @@ -147,5 +182,9 @@ export default function () { \}).then(html => \{ fs.writeFileSync('dist/test-project-server/index.html', html); \}); - `))) -} \ No newline at end of file + `, + ), + ), + ) + ); +} From 2f23384edd7ece8548c1d1d69eb09beacef8a75a Mon Sep 17 00:00:00 2001 From: Alan Date: Wed, 6 Mar 2019 07:21:14 +0100 Subject: [PATCH 4/4] ci: add configuration for legacy projects In some cases, there are still legacy projects, prior to migrations and we should not use `webdriverUpdate` and we should use headless chrome. --- tests/legacy-cli/e2e/utils/project.ts | 116 ++++++++++++++++++++------ 1 file changed, 90 insertions(+), 26 deletions(-) diff --git a/tests/legacy-cli/e2e/utils/project.ts b/tests/legacy-cli/e2e/utils/project.ts index af3c7e13133a..f9ca35ce8dc9 100644 --- a/tests/legacy-cli/e2e/utils/project.ts +++ b/tests/legacy-cli/e2e/utils/project.ts @@ -48,27 +48,82 @@ export async function createProject(name: string, ...args: string[]) { } export async function prepareProjectForE2e(name) { - const argv: string[] = getGlobalVariable('argv'); - - await git('config', 'user.email', 'angular-core+e2e@google.com'); - await git('config', 'user.name', 'Angular CLI E2e'); - await git('config', 'commit.gpgSign', 'false'); - await useBuiltPackages(); - await useCIChrome('e2e'); - await useCIChrome(''); - await useDevKitSnapshots(); - await argv['ng2'] ? useNg2() : Promise.resolve(); - await argv['ng4'] ? useNg4() : Promise.resolve(); - await argv['ng-snapshots'] || argv['ng-tag'] ? useSha() : Promise.resolve(); - await console.log(`Project ${name} created... Installing npm.`); - await silentNpm('install'); - await useCIDefaults(name); - // Force sourcemaps to be from the root of the filesystem. - await updateJsonFile('tsconfig.json', json => { - json['compilerOptions']['sourceRoot'] = '/'; - }); - await gitCommit('prepare-project-for-e2e'); -} + const argv: string[] = getGlobalVariable( + 'argv', + ); + + await git( + 'config', + 'user.email', + 'angular-core+e2e@google.com', + ); + await git( + 'config', + 'user.name', + 'Angular CLI E2e', + ); + await git( + 'config', + 'commit.gpgSign', + 'false', + ); + await useBuiltPackages(); + await useCIChrome( + 'e2e', + ); + await useCIChrome( + '', + ); + + // legacy projects + await useCIChrome( + 'src', + ); + + await useDevKitSnapshots(); + (await argv[ + 'ng2' + ]) + ? useNg2() + : Promise.resolve(); + (await argv[ + 'ng4' + ]) + ? useNg4() + : Promise.resolve(); + (await argv[ + 'ng-snapshots' + ]) || + argv[ + 'ng-tag' + ] + ? useSha() + : Promise.resolve(); + await console.log( + `Project ${name} created... Installing npm.`, + ); + await silentNpm( + 'install', + ); + await useCIDefaults( + name, + ); + // Force sourcemaps to be from the root of the filesystem. + await updateJsonFile( + 'tsconfig.json', + json => { + json[ + 'compilerOptions' + ][ + 'sourceRoot' + ] = + '/'; + }, + ); + await gitCommit( + 'prepare-project-for-e2e', + ); + } export function useDevKit(devkitRoot: string) { @@ -99,7 +154,7 @@ export function useDevKit(devkitRoot: string) { export function useDevKitSnapshots() { return updateJsonFile('package.json', json => { // TODO: actually add these. - // These were not working on any test that ran `npm i`. + // These were not working on any test that ran `npm i`. // json['devDependencies']['@angular-devkit/build-angular'] = // 'github:angular/angular-devkit-build-angular-builds'; // // By adding build-ng-packagr preemptively, adding a lib will not update it. @@ -119,8 +174,9 @@ export function useBuiltPackages() { } for (const packageName of Object.keys(packages)) { - if (json['dependencies'].hasOwnProperty(packageName)) { - json['dependencies'][packageName] = packages[packageName].tar; + if (json['dependencies'].hasOwnProperty(packageName) + ) { + json['dependencies'][packageName] = packages[packageName].tar; } else if (json['devDependencies'].hasOwnProperty(packageName)) { json['devDependencies'][packageName] = packages[packageName].tar; } @@ -212,6 +268,13 @@ export function useCIDefaults(projectName = 'test-project') { if (appTargets.e2e) { appTargets.e2e.options.webdriverUpdate = false; } + + // legacy project structure + const e2eProject = workspaceJson.projects[projectName + '-e2e']; + if (e2eProject){ + const e2eTargets = e2eProject.targets || e2eProject.architect; + e2eTargets.e2e.options.webdriverUpdate = false; + } }) .then(() => updateJsonFile('package.json', json => { // Use matching versions of Chrome and Webdriver. @@ -223,8 +286,9 @@ export function useCIDefaults(projectName = 'test-project') { } export function useCIChrome(projectDir: string) { - const protractorConf = `${projectDir}/protractor.conf.js`; - const karmaConf = `${projectDir ? projectDir + '/' : ''}karma.conf.js`; + const dir = projectDir ? projectDir + '/' : ''; + const protractorConf = `${dir}protractor.conf.js`; + const karmaConf = `${dir}karma.conf.js`; return Promise.resolve() .then(() => updateJsonFile('package.json', json => {