diff --git a/schematics/ng-add/index.ts b/schematics/ng-add/index.ts index f5418b36a09..36e0e5b9010 100644 --- a/schematics/ng-add/index.ts +++ b/schematics/ng-add/index.ts @@ -15,6 +15,7 @@ import { getAppModulePath } from '../utils/devkit-utils/ng-ast-utils'; import { insertImport } from '../utils/devkit-utils/route-utils'; import { zorroVersion } from '../utils/lib-versions'; import { addPackageToPackageJson } from '../utils/package'; +import { getProjectTargetOptions } from '../utils/project-targets'; import { Schema } from './schema'; const ADD_CONFIG = { @@ -45,7 +46,7 @@ function addI18n(options: Schema): (host: Tree) => Tree { return function (host: Tree): Tree { const workspace = getWorkspace(host); const project = getProjectFromWorkspace(workspace, options.project); - const modulePath = getAppModulePath(host, project.architect.build.options.main); + const modulePath = getAppModulePath(host, getProjectTargetOptions(project, 'build').main); const moduleSource = getSourceFile(host, modulePath); const locale = options.i18n; const localePrefix = locale.split('_')[0]; @@ -206,11 +207,11 @@ function insertCustomTheme(project: Project, host: Tree, workspace: Workspace): host.create(themePath, createCustomTheme()); } - if (project.architect) { - addStyleToTarget(project.architect.build, host, workspace, themePath, ADD_CONFIG.COMPILED_THEME_PATH); - addStyleToTarget(project.architect.test, host, workspace, themePath, ADD_CONFIG.COMPILED_THEME_PATH); + if ((project as any).targets || project.architect) { + addStyleToTarget('build', host, workspace, project, themePath, ADD_CONFIG.COMPILED_THEME_PATH); + addStyleToTarget('test', host, workspace, project, themePath, ADD_CONFIG.COMPILED_THEME_PATH); } else { - throw new SchematicsException(`${project.name} does not have an architect configuration`); + throw new SchematicsException(`${project.name} does not have an architect or targets configuration`); } } @@ -234,37 +235,35 @@ function installNodeDeps(): (host: Tree, context: SchematicContext) => void { function insertCompiledTheme(project: Project, host: Tree, workspace: Workspace): void { const themePath = ADD_CONFIG.COMPILED_THEME_PATH; - if (project.architect) { - addStyleToTarget(project.architect.build, host, workspace, themePath); - addStyleToTarget(project.architect.test, host, workspace, themePath); + if ((project as any).targets || project.architect) { + addStyleToTarget('build', host, workspace, project, themePath); + addStyleToTarget('test', host, workspace, project, themePath); } else { - throw new SchematicsException(`${project.name} does not have an architect configuration`); + throw new SchematicsException(`${project.name} does not have an architect or targets configuration`); } } /** Adds a style entry to the given target. */ -function addStyleToTarget(target: any, host: Tree, workspace: Workspace, asset: string, exclude: string = ''): void { +function addStyleToTarget(targetName: string, host: Tree, workspace: Workspace, project: Project, asset: string, exclude: string = ''): void { const styleEntry = asset; - + const targetOptions = getProjectTargetOptions(project, targetName); // We can't assume that any of these properties are defined, so safely add them as we go // if necessary. - if (!target.options) { - target.options = { styles: [ styleEntry ] }; - } else if (!target.options.styles) { - target.options.styles = [ styleEntry ]; + if (!targetOptions.styles) { + targetOptions.styles = [ styleEntry ]; } else { - const existingStyles = target.options.styles.map(s => typeof s === 'string' ? s : s.input); + const existingStyles = targetOptions.styles.map(s => typeof s === 'string' ? s : s.input); const hasGivenTheme = existingStyles.find(s => s.includes(asset)); if (exclude) { - const removeIndex = target.options.styles.indexOf(exclude); + const removeIndex = targetOptions.styles.indexOf(exclude); if (removeIndex >= 0) { - target.options.styles.splice(removeIndex, 1); + targetOptions.styles.splice(removeIndex, 1); } } if (!hasGivenTheme) { - target.options.styles.splice(0, 0, styleEntry); + targetOptions.styles.splice(0, 0, styleEntry); } } diff --git a/schematics/utils/ast.ts b/schematics/utils/ast.ts index 3eb9e8cf1cc..5ab02409f8c 100755 --- a/schematics/utils/ast.ts +++ b/schematics/utils/ast.ts @@ -3,9 +3,11 @@ import {SchematicsException, Tree} from '@angular-devkit/schematics'; import * as ts from 'typescript'; import {addImportToModule} from './devkit-utils/ast-utils'; import {InsertChange} from './devkit-utils/change'; -import {Project, getWorkspace} from './devkit-utils/config'; +import { Project, getWorkspace, Workspace } from './devkit-utils/config'; import {findBootstrapModulePath, getAppModulePath} from './devkit-utils/ng-ast-utils'; import {ModuleOptions, findModuleFromOptions as internalFindModule} from './devkit-utils/find-module'; +import { getProjectMainFile } from './project-main-file' +import { getProjectTargetOptions } from './project-targets' /** Reads file given path and returns TypeScript source file. */ @@ -19,8 +21,9 @@ export function getSourceFile(host: Tree, path: string): ts.SourceFile { } /** Import and add module to root app module. */ -export function addModuleImportToRootModule(host: Tree, moduleName: string, src: string, project: Project) { - const modulePath = getAppModulePath(host, project.architect.build.options.main); +export function addModuleImportToRootModule(host: Tree, moduleName: string, src: string, + project: Workspace | any) { + const modulePath = getAppModulePath(host, getProjectMainFile(project)); addModuleImportToModule(host, modulePath, moduleName, src); } @@ -53,10 +56,10 @@ export function addModuleImportToModule( /** Gets the app index.html file */ export function getIndexHtmlPath(host: Tree, project: Project): string { - const buildTarget = project.architect.build.options; + const buildOptions = getProjectTargetOptions(project, 'build'); - if (buildTarget.index && buildTarget.index.endsWith('index.html')) { - return buildTarget.index; + if (buildOptions.index && buildOptions.index.endsWith('index.html')) { + return buildOptions.index; } throw new SchematicsException('No index.html file was found.'); @@ -64,10 +67,10 @@ export function getIndexHtmlPath(host: Tree, project: Project): string { /** Get the root stylesheet file. */ export function getStylesPath(host: Tree, project: Project): string { - const buildTarget = project.architect['build']; + const buildOptions = getProjectTargetOptions(project, 'build'); - if (buildTarget.options && buildTarget.options.styles && buildTarget.options.styles.length) { - const styles = buildTarget.options.styles.map(s => typeof s === 'string' ? s : s.input); + if (buildOptions && buildOptions.styles && buildOptions.styles.length) { + const styles = buildOptions.styles.map(s => typeof s === 'string' ? s : s.input); // First, see if any of the assets is called "styles.(le|sc|c)ss", which is the default // "main" style sheet. diff --git a/schematics/utils/project-main-file.ts b/schematics/utils/project-main-file.ts new file mode 100644 index 00000000000..81a4c85456e --- /dev/null +++ b/schematics/utils/project-main-file.ts @@ -0,0 +1,23 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * 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 { SchematicsException } from '@angular-devkit/schematics'; +import { Workspace } from './devkit-utils/config'; +import { getProjectTargetOptions } from './project-targets'; + +/** Looks for the main TypeScript file in the given project and returns its path. */ +export function getProjectMainFile(project: Workspace): string { + const buildOptions = getProjectTargetOptions(project, 'build'); + + if (!buildOptions.main) { + throw new SchematicsException(`Could not find the project main file inside of the ` + + `workspace config (${(project as any).sourceRoot})`); + } + + return buildOptions.main; +} diff --git a/schematics/utils/project-targets.ts b/schematics/utils/project-targets.ts new file mode 100644 index 00000000000..9a7a5a43f96 --- /dev/null +++ b/schematics/utils/project-targets.ts @@ -0,0 +1,31 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * 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 { Project } from './devkit-utils/config'; + +/** Resolves the architect options for the build target of the given project. */ +export function getProjectTargetOptions(project: Project | any, buildTarget: string) { + if (project.targets && + project.targets[buildTarget] && + project.targets[buildTarget].options) { + + return project.targets[buildTarget].options; + } + + // TODO(devversion): consider removing this architect check if the CLI completely switched + // over to `targets`, and the `architect` support has been removed. + // See: https://github.com/angular/angular-cli/commit/307160806cb48c95ecb8982854f452303801ac9f + if (project.architect && + project.architect[buildTarget] && + project.architect[buildTarget].options) { + + return project.architect[buildTarget].options; + } + + throw new Error(`Cannot determine project target configuration for: ${buildTarget}.`); +}