diff --git a/src/lib/schematics/address-form/index.ts b/src/lib/schematics/address-form/index.ts
index 066264250666..7d411ba2aabc 100644
--- a/src/lib/schematics/address-form/index.ts
+++ b/src/lib/schematics/address-form/index.ts
@@ -30,7 +30,7 @@ export default function(options: Schema): Rule {
*/
function addFormModulesToModule(options: Schema) {
return (host: Tree) => {
- const modulePath = findModuleFromOptions(host, options);
+ const modulePath = findModuleFromOptions(host, options)!;
addModuleImportToModule(host, modulePath, 'MatInputModule', '@angular/material');
addModuleImportToModule(host, modulePath, 'MatButtonModule', '@angular/material');
addModuleImportToModule(host, modulePath, 'MatSelectModule', '@angular/material');
diff --git a/src/lib/schematics/dashboard/index.ts b/src/lib/schematics/dashboard/index.ts
index 0c4366781df5..57146d15cb94 100644
--- a/src/lib/schematics/dashboard/index.ts
+++ b/src/lib/schematics/dashboard/index.ts
@@ -30,7 +30,7 @@ export default function(options: Schema): Rule {
*/
function addNavModulesToModule(options: Schema) {
return (host: Tree) => {
- const modulePath = findModuleFromOptions(host, options);
+ const modulePath = findModuleFromOptions(host, options)!;
addModuleImportToModule(host, modulePath, 'MatGridListModule', '@angular/material');
addModuleImportToModule(host, modulePath, 'MatCardModule', '@angular/material');
addModuleImportToModule(host, modulePath, 'MatMenuModule', '@angular/material');
diff --git a/src/lib/schematics/install/fonts/head-element.ts b/src/lib/schematics/install/fonts/head-element.ts
index 323d2844cc88..1a97c3f7f67b 100644
--- a/src/lib/schematics/install/fonts/head-element.ts
+++ b/src/lib/schematics/install/fonts/head-element.ts
@@ -33,7 +33,9 @@ export function appendElementToHead(host: Tree, project: WorkspaceProject, eleme
throw `Could not find '
' element in HTML file: ${indexPath}`;
}
- const endTagOffset = headTag.sourceCodeLocation.endTag.startOffset;
+ // We always have access to the source code location here because the `getHeadTagElement`
+ // function explicitly has the `sourceCodeLocationInfo` option enabled.
+ const endTagOffset = headTag.sourceCodeLocation!.endTag.startOffset;
const indentationOffset = getChildElementIndentation(headTag);
const insertion = `${' '.repeat(indentationOffset)}${elementHtml}`;
diff --git a/src/lib/schematics/install/fonts/project-index-html.ts b/src/lib/schematics/install/fonts/project-index-html.ts
index e7dd5d464d62..a4b60fddad7d 100644
--- a/src/lib/schematics/install/fonts/project-index-html.ts
+++ b/src/lib/schematics/install/fonts/project-index-html.ts
@@ -8,14 +8,15 @@
import {SchematicsException} from '@angular-devkit/schematics';
import {WorkspaceProject} from '@schematics/angular/utility/config';
+import {getArchitectOptions} from '../../utils/architect-options';
/** Looks for the index HTML file in the given project and returns its path. */
export function getIndexHtmlPath(project: WorkspaceProject): string {
- const buildTarget = project.architect.build.options;
+ const buildOptions = getArchitectOptions(project, 'build');
- if (buildTarget.index && buildTarget.index.endsWith('index.html')) {
- return buildTarget.index;
+ if (!buildOptions.index) {
+ throw new SchematicsException('No project "index.html" file could be found.');
}
- throw new SchematicsException('No index.html file was found.');
+ return buildOptions.index;
}
diff --git a/src/lib/schematics/install/gestures/hammerjs-import.ts b/src/lib/schematics/install/gestures/hammerjs-import.ts
index 586cdf90d1ab..83c6382ea3ea 100644
--- a/src/lib/schematics/install/gestures/hammerjs-import.ts
+++ b/src/lib/schematics/install/gestures/hammerjs-import.ts
@@ -10,7 +10,7 @@ import {Rule, Tree} from '@angular-devkit/schematics';
import {getWorkspace} from '@schematics/angular/utility/config';
import {getProjectFromWorkspace} from '../../utils/get-project';
import {Schema} from '../schema';
-import {getProjectMainFile} from './project-main-file';
+import {getProjectMainFile} from '../../utils/project-main-file';
const hammerjsImportStatement = `import 'hammerjs';`;
diff --git a/src/lib/schematics/install/index.spec.ts b/src/lib/schematics/install/index.spec.ts
index 1f6b20542348..2e0b1ea398c6 100644
--- a/src/lib/schematics/install/index.spec.ts
+++ b/src/lib/schematics/install/index.spec.ts
@@ -1,6 +1,6 @@
import {Tree} from '@angular-devkit/schematics';
import {SchematicTestRunner} from '@angular-devkit/schematics/testing';
-import {getProjectStyleFile} from '@angular/material/schematics/utils/project-style-file';
+import {getProjectStyleFile} from '../utils/project-style-file';
import {getIndexHtmlPath} from './fonts/project-index-html';
import {getProjectFromWorkspace} from '../utils/get-project';
import {getFileContent} from '@schematics/angular/utility/test';
@@ -71,10 +71,10 @@ describe('material-install-schematic', () => {
const expectedStylesPath = normalize(`/${project.root}/src/styles.scss`);
const buffer = tree.read(expectedStylesPath);
- const src = buffer!.toString();
+ const themeContent = buffer!.toString();
- expect(src.indexOf(`@import '~@angular/material/theming';`)).toBeGreaterThan(-1);
- expect(src.indexOf(`$app-primary`)).toBeGreaterThan(-1);
+ expect(themeContent).toContain(`@import '~@angular/material/theming';`);
+ expect(themeContent).toContain(`$app-primary: mat-palette(`);
});
it('should create a custom theme file if no SCSS file could be found', () => {
@@ -112,7 +112,7 @@ describe('material-install-schematic', () => {
const workspace = getWorkspace(tree);
const project = getProjectFromWorkspace(workspace);
- const defaultStylesPath = getProjectStyleFile(project);
+ const defaultStylesPath = getProjectStyleFile(project)!;
const htmlContent = tree.read(defaultStylesPath)!.toString();
expect(htmlContent).toContain('html, body { height: 100%; }');
diff --git a/src/lib/schematics/install/index.ts b/src/lib/schematics/install/index.ts
index 990e0d65010a..3ca9ed2de52c 100644
--- a/src/lib/schematics/install/index.ts
+++ b/src/lib/schematics/install/index.ts
@@ -94,9 +94,9 @@ function addMaterialAppStyles(options: Schema) {
const workspace = getWorkspace(host);
const project = getProjectFromWorkspace(workspace, options.project);
const styleFilePath = getProjectStyleFile(project);
- const buffer = host.read(styleFilePath);
+ const buffer = host.read(styleFilePath!);
- if (!buffer) {
+ if (!styleFilePath || !buffer) {
return console.warn(`Could not find styles file: "${styleFilePath}". Skipping styles ` +
`generation. Please consider manually adding the "Roboto" font and resetting the ` +
`body margin.`);
diff --git a/src/lib/schematics/install/schema.json b/src/lib/schematics/install/schema.json
index 778b7484e8ff..4188ae29dac3 100644
--- a/src/lib/schematics/install/schema.json
+++ b/src/lib/schematics/install/schema.json
@@ -4,6 +4,13 @@
"title": "Material Install Options Schema",
"type": "object",
"properties": {
+ "project": {
+ "type": "string",
+ "description": "The name of the project.",
+ "$default": {
+ "$source": "projectName"
+ }
+ },
"skipPackageJson": {
"type": "boolean",
"default": false,
diff --git a/src/lib/schematics/install/schema.ts b/src/lib/schematics/install/schema.ts
index 429b4c222984..d1929dfbd534 100644
--- a/src/lib/schematics/install/schema.ts
+++ b/src/lib/schematics/install/schema.ts
@@ -7,6 +7,10 @@
*/
export interface Schema {
+
+ /** Name of the project to target. */
+ project: string;
+
/** Whether to skip package.json install. */
skipPackageJson: boolean;
@@ -15,7 +19,4 @@ export interface Schema {
/** Name of pre-built theme to install. */
theme: 'indigo-pink' | 'deeppurple-amber' | 'pink-bluegrey' | 'purple-green' | 'custom';
-
- /** Name of the project to target. */
- project?: string;
}
diff --git a/src/lib/schematics/install/theming/theming.ts b/src/lib/schematics/install/theming/theming.ts
index 670a0da9fcb0..d11a5f184976 100644
--- a/src/lib/schematics/install/theming/theming.ts
+++ b/src/lib/schematics/install/theming/theming.ts
@@ -31,7 +31,7 @@ export function addThemeToAppStyles(options: Schema): (host: Tree) => Tree {
if (themeName === 'custom') {
insertCustomTheme(project, options.project, host, workspace);
} else {
- insertPrebuiltTheme(project, host, themeName, workspace, options.project);
+ insertPrebuiltTheme(project, host, themeName, workspace);
}
return host;
@@ -49,13 +49,20 @@ function insertCustomTheme(project: WorkspaceProject, projectName: string, host:
const themeContent = createCustomTheme(projectName);
if (!stylesPath) {
+ if (!project.sourceRoot) {
+ throw new Error(`Could not find source root for project: "${projectName}". Please make ` +
+ `sure that the "sourceRoot" property is set in the workspace config.`);
+ }
+
// Normalize the path through the devkit utilities because we want to avoid having
// unnecessary path segments and windows backslash delimiters.
const customThemePath = normalize(join(project.sourceRoot, 'custom-theme.scss'));
host.create(customThemePath, themeContent);
- addStyleToTarget(project.architect['build'], host, customThemePath, workspace);
- return;
+
+ // Architect is always defined because we initially asserted if the default builder
+ // configuration is set up or not.
+ return addStyleToTarget(project.architect!['build'], host, customThemePath, workspace);
}
const insertion = new InsertChange(stylesPath, 0, themeContent);
@@ -67,17 +74,15 @@ function insertCustomTheme(project: WorkspaceProject, projectName: string, host:
/** Insert a pre-built theme into the angular.json file. */
function insertPrebuiltTheme(project: WorkspaceProject, host: Tree, theme: string,
- workspace: WorkspaceSchema, projectName: string) {
+ workspace: WorkspaceSchema) {
// Path needs to be always relative to the `package.json` or workspace root.
const themePath = `./node_modules/@angular/material/prebuilt-themes/${theme}.css`;
- if (project.architect) {
- addStyleToTarget(project.architect['build'], host, themePath, workspace);
- addStyleToTarget(project.architect['test'], host, themePath, workspace);
- } else {
- throw new SchematicsException(`${projectName} does not have an architect configuration`);
- }
+ // Architect is always defined because we initially asserted if the default builder
+ // configuration is set up or not.
+ addStyleToTarget(project.architect!['build'], host, themePath, workspace);
+ addStyleToTarget(project.architect!['test'], host, themePath, workspace);
}
/** Adds a style entry to the given target. */
diff --git a/src/lib/schematics/nav/index.ts b/src/lib/schematics/nav/index.ts
index e25d949a0f93..dcfb28c71920 100644
--- a/src/lib/schematics/nav/index.ts
+++ b/src/lib/schematics/nav/index.ts
@@ -30,7 +30,7 @@ export default function(options: Schema): Rule {
*/
function addNavModulesToModule(options: Schema) {
return (host: Tree) => {
- const modulePath = findModuleFromOptions(host, options);
+ const modulePath = findModuleFromOptions(host, options)!;
addModuleImportToModule(host, modulePath, 'LayoutModule', '@angular/cdk/layout');
addModuleImportToModule(host, modulePath, 'MatToolbarModule', '@angular/material');
addModuleImportToModule(host, modulePath, 'MatButtonModule', '@angular/material');
diff --git a/src/lib/schematics/table/index.ts b/src/lib/schematics/table/index.ts
index a3784bf6eb1c..b7215bd2183b 100644
--- a/src/lib/schematics/table/index.ts
+++ b/src/lib/schematics/table/index.ts
@@ -30,7 +30,7 @@ export default function(options: Schema): Rule {
*/
function addTableModulesToModule(options: Schema) {
return (host: Tree) => {
- const modulePath = findModuleFromOptions(host, options);
+ const modulePath = findModuleFromOptions(host, options)!;
addModuleImportToModule(host, modulePath, 'MatTableModule', '@angular/material');
addModuleImportToModule(host, modulePath, 'MatPaginatorModule', '@angular/material');
addModuleImportToModule(host, modulePath, 'MatSortModule', '@angular/material');
diff --git a/src/lib/schematics/tree/index.ts b/src/lib/schematics/tree/index.ts
index 726a39c28e5d..06277598d664 100644
--- a/src/lib/schematics/tree/index.ts
+++ b/src/lib/schematics/tree/index.ts
@@ -30,7 +30,7 @@ export default function(options: Schema): Rule {
*/
function addTreeModulesToModule(options: Schema) {
return (host: Tree) => {
- const modulePath = findModuleFromOptions(host, options);
+ const modulePath = findModuleFromOptions(host, options)!;
addModuleImportToModule(host, modulePath, 'MatTreeModule', '@angular/material');
addModuleImportToModule(host, modulePath, 'MatIconModule', '@angular/material');
addModuleImportToModule(host, modulePath, 'MatButtonModule', '@angular/material');
diff --git a/src/lib/schematics/tsconfig.json b/src/lib/schematics/tsconfig.json
index cf6563b3c13d..1417e0cca2fc 100644
--- a/src/lib/schematics/tsconfig.json
+++ b/src/lib/schematics/tsconfig.json
@@ -5,6 +5,7 @@
"moduleResolution": "node",
"outDir": "../../../dist/schematics",
"noEmitOnError": false,
+ "strictNullChecks": true,
"skipDefaultLibCheck": true,
"skipLibCheck": true,
"sourceMap": true,
diff --git a/src/lib/schematics/update/material/data/input-names.ts b/src/lib/schematics/update/material/data/input-names.ts
index b7885227b728..4813c9ef61da 100644
--- a/src/lib/schematics/update/material/data/input-names.ts
+++ b/src/lib/schematics/update/material/data/input-names.ts
@@ -15,17 +15,11 @@ export interface MaterialInputNameData {
/** The new name for the @Input(). */
replaceWith: string;
/** Whitelist where this replacement is made. If omitted it is made in all HTML & CSS */
- whitelist?: {
+ whitelist: {
/** Limit to elements with any of these element tags. */
elements?: string[],
/** Limit to elements with any of these attributes. */
attributes?: string[],
- /**
- * Whether inputs in stylesheets should be updated or not. Note that inputs inside of
- * stylesheets usually don't make sense, but if developers use an input as a plain one-time
- * attribute, it can be targeted through CSS selectors.
- */
- stylesheet?: boolean,
};
}
diff --git a/src/lib/schematics/update/material/data/output-names.ts b/src/lib/schematics/update/material/data/output-names.ts
index 54b80c211005..7be76533937c 100644
--- a/src/lib/schematics/update/material/data/output-names.ts
+++ b/src/lib/schematics/update/material/data/output-names.ts
@@ -15,7 +15,7 @@ export interface MaterialOutputNameData {
/** The new name for the @Output(). */
replaceWith: string;
/** Whitelist where this replacement is made. If omitted it is made in all HTML & CSS */
- whitelist?: {
+ whitelist: {
/** Limit to elements with any of these element tags. */
elements?: string[],
/** Limit to elements with any of these attributes. */
diff --git a/src/lib/schematics/update/material/data/property-names.ts b/src/lib/schematics/update/material/data/property-names.ts
index 0ad5019b0527..901a8bf4dbb8 100644
--- a/src/lib/schematics/update/material/data/property-names.ts
+++ b/src/lib/schematics/update/material/data/property-names.ts
@@ -17,7 +17,7 @@ export interface MaterialPropertyNameData {
/** Whitelist where this replacement is made. If omitted it is made for all Classes. */
whitelist: {
/** Replace the property only when its type is one of the given Classes. */
- classes?: string[];
+ classes: string[];
};
}
diff --git a/src/lib/schematics/update/material/transform-change-data.ts b/src/lib/schematics/update/material/transform-change-data.ts
index ebdd033f5120..0a6913a4fb93 100644
--- a/src/lib/schematics/update/material/transform-change-data.ts
+++ b/src/lib/schematics/update/material/transform-change-data.ts
@@ -33,5 +33,5 @@ export function getChangesForTarget(target: TargetVersion, data: VersionChang
return [];
}
- return data[target].reduce((result, changes) => result.concat(changes.changes), []);
+ return data[target]!.reduce((result, prData) => result.concat(prData.changes), [] as T[]);
}
diff --git a/src/lib/schematics/update/rules/class-inheritance/classInheritanceCheckRule.ts b/src/lib/schematics/update/rules/class-inheritance/classInheritanceCheckRule.ts
index 973d4ea2a56f..0b85ff9623e7 100644
--- a/src/lib/schematics/update/rules/class-inheritance/classInheritanceCheckRule.ts
+++ b/src/lib/schematics/update/rules/class-inheritance/classInheritanceCheckRule.ts
@@ -41,6 +41,7 @@ export class Walker extends ProgramAwareRuleWalker {
visitClassDeclaration(node: ts.ClassDeclaration) {
const baseTypes = determineBaseTypes(node);
+ const className = node.name ? node.name.text : '{unknown-name}';
if (!baseTypes) {
return;
@@ -50,7 +51,7 @@ export class Walker extends ProgramAwareRuleWalker {
const data = this.propertyNames.get(typeName);
if (data) {
- this.addFailureAtNode(node, `Found class "${bold(node.name.text)}" which extends class ` +
+ this.addFailureAtNode(node, `Found class "${bold(className)}" which extends class ` +
`"${bold(typeName)}". Please note that the base class property ` +
`"${red(data.replace)}" has changed to "${green(data.replaceWith)}". ` +
`You may need to update your class as well`);
diff --git a/src/lib/schematics/update/rules/class-inheritance/classInheritanceMiscRule.ts b/src/lib/schematics/update/rules/class-inheritance/classInheritanceMiscRule.ts
index 5fadc654c852..f01a628dfe3e 100644
--- a/src/lib/schematics/update/rules/class-inheritance/classInheritanceMiscRule.ts
+++ b/src/lib/schematics/update/rules/class-inheritance/classInheritanceMiscRule.ts
@@ -25,6 +25,7 @@ export class Walker extends ProgramAwareRuleWalker {
visitClassDeclaration(node: ts.ClassDeclaration) {
const baseTypes = determineBaseTypes(node);
+ const className = node.name ? node.name.text : '{unknown-name}';
if (!baseTypes) {
return;
@@ -32,10 +33,11 @@ export class Walker extends ProgramAwareRuleWalker {
if (baseTypes.includes('MatFormFieldControl')) {
const hasFloatLabelMember = node.members
- .find(member => member.name && member.name.getText() === 'shouldFloatLabel');
+ .filter(member => member.name)
+ .find(member => member.name!.getText() === 'shouldFloatLabel');
if (!hasFloatLabelMember) {
- this.addFailureAtNode(node, `Found class "${bold(node.name.text)}" which extends ` +
+ this.addFailureAtNode(node, `Found class "${bold(className)}" which extends ` +
`"${bold('MatFormFieldControl')}". This class must define ` +
`"${green('shouldLabelFloat')}" which is now a required property.`);
}
diff --git a/src/lib/schematics/update/rules/input-names/inputNamesStylesheetRule.ts b/src/lib/schematics/update/rules/input-names/inputNamesStylesheetRule.ts
index 14508bee15d5..18241caa53fe 100644
--- a/src/lib/schematics/update/rules/input-names/inputNamesStylesheetRule.ts
+++ b/src/lib/schematics/update/rules/input-names/inputNamesStylesheetRule.ts
@@ -68,10 +68,6 @@ export class Walker extends ComponentWalker {
const replacements: {failureMessage: string, replacement: Replacement}[] = [];
this.data.forEach(name => {
- if (name.whitelist && !name.whitelist.stylesheet) {
- return;
- }
-
const currentSelector = `[${name.replace}]`;
const updatedSelector = `[${name.replaceWith}]`;
diff --git a/src/lib/schematics/update/rules/input-names/inputNamesTemplateRule.ts b/src/lib/schematics/update/rules/input-names/inputNamesTemplateRule.ts
index 2492779ce801..55e3106fb059 100644
--- a/src/lib/schematics/update/rules/input-names/inputNamesTemplateRule.ts
+++ b/src/lib/schematics/update/rules/input-names/inputNamesTemplateRule.ts
@@ -55,16 +55,16 @@ export class Walker extends ComponentWalker {
this.data.forEach(name => {
const whitelist = name.whitelist;
- const relativeOffsets = [];
+ const relativeOffsets: number[] = [];
const failureMessage = `Found deprecated @Input() "${red(name.replace)}"` +
` which has been renamed to "${green(name.replaceWith)}"`;
- if (!whitelist || whitelist.attributes) {
+ if (whitelist.attributes) {
relativeOffsets.push(
...findInputsOnElementWithAttr(templateContent, name.replace, whitelist.attributes));
}
- if (!whitelist || whitelist.elements) {
+ if (whitelist.elements) {
relativeOffsets.push(
...findInputsOnElementWithTag(templateContent, name.replace, whitelist.elements));
}
diff --git a/src/lib/schematics/update/rules/output-names/outputNamesTemplateRule.ts b/src/lib/schematics/update/rules/output-names/outputNamesTemplateRule.ts
index 9b41ecfc7f41..885346bc2ac6 100644
--- a/src/lib/schematics/update/rules/output-names/outputNamesTemplateRule.ts
+++ b/src/lib/schematics/update/rules/output-names/outputNamesTemplateRule.ts
@@ -55,16 +55,16 @@ export class Walker extends ComponentWalker {
this.data.forEach(name => {
const whitelist = name.whitelist;
- const relativeOffsets = [];
+ const relativeOffsets: number[] = [];
const failureMessage = `Found deprecated @Output() "${red(name.replace)}"` +
` which has been renamed to "${green(name.replaceWith)}"`;
- if (!whitelist || whitelist.attributes) {
+ if (whitelist.attributes) {
relativeOffsets.push(
...findOutputsOnElementWithAttr(templateContent, name.replace, whitelist.attributes));
}
- if (!whitelist || whitelist.elements) {
+ if (whitelist.elements) {
relativeOffsets.push(
...findOutputsOnElementWithTag(templateContent, name.replace, whitelist.elements));
}
diff --git a/src/lib/schematics/update/rules/signature-check/constructorSignatureCheckRule.ts b/src/lib/schematics/update/rules/signature-check/constructorSignatureCheckRule.ts
index 4ef1a8348cb9..27ca5267668f 100644
--- a/src/lib/schematics/update/rules/signature-check/constructorSignatureCheckRule.ts
+++ b/src/lib/schematics/update/rules/signature-check/constructorSignatureCheckRule.ts
@@ -40,7 +40,8 @@ export class Walker extends ProgramAwareRuleWalker {
return signature.getParameters()
.map(param => param.declarations[0] as ts.ParameterDeclaration)
.map(node => node.type)
- .map(node => this.getTypeChecker().getTypeFromTypeNode(node));
+ // TODO(devversion): handle non resolvable constructor types
+ .map(typeNode => this.getTypeChecker().getTypeFromTypeNode(typeNode!));
}
private checkExpressionSignature(node: ts.CallExpression | ts.NewExpression) {
@@ -50,7 +51,7 @@ export class Walker extends ProgramAwareRuleWalker {
// TODO(devversion): Consider handling pass-through classes better.
// TODO(devversion): e.g. `export class CustomCalendar extends MatCalendar {}`
- if (!classType || !constructorChecks.includes(className)) {
+ if (!classType || !constructorChecks.includes(className) || !node.arguments) {
return;
}
@@ -62,6 +63,8 @@ export class Walker extends ProgramAwareRuleWalker {
// TODO(devversion): we should check if the type is assignable to the signature
// TODO(devversion): blocked on https://github.com/Microsoft/TypeScript/issues/9879
const doesMatchSignature = classSignatures.some(signature => {
+ // TODO(devversion): better handling if signature item type is unresolved but assignable
+ // to everything.
return signature.every((type, index) => callExpressionSignature[index] === type) &&
signature.length === callExpressionSignature.length;
});
diff --git a/src/lib/schematics/update/typescript/base-types.ts b/src/lib/schematics/update/typescript/base-types.ts
index 4c7b51b01386..13b5ebe3d06b 100644
--- a/src/lib/schematics/update/typescript/base-types.ts
+++ b/src/lib/schematics/update/typescript/base-types.ts
@@ -15,8 +15,8 @@ export function determineBaseTypes(node: ts.ClassDeclaration): string[] | null {
}
return node.heritageClauses
- .reduce((types, clause) => types.concat(clause.types), [])
+ .reduce((types, clause) => types.concat(clause.types), [] as ts.ExpressionWithTypeArguments[])
.map(typeExpression => typeExpression.expression)
.filter(expression => expression && ts.isIdentifier(expression))
- .map(identifier => identifier.text);
+ .map((identifier: ts.Identifier) => identifier.text);
}
diff --git a/src/lib/schematics/utils/architect-options.ts b/src/lib/schematics/utils/architect-options.ts
new file mode 100644
index 000000000000..82e9c31a4f14
--- /dev/null
+++ b/src/lib/schematics/utils/architect-options.ts
@@ -0,0 +1,21 @@
+/**
+ * @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 {WorkspaceProject} from '@schematics/angular/utility/config';
+
+/** Resolves the architect options for the build target of the given project. */
+export function getArchitectOptions(project: WorkspaceProject, buildTarget: string) {
+ if (project.architect &&
+ project.architect[buildTarget] &&
+ project.architect[buildTarget].options) {
+
+ return project.architect[buildTarget].options;
+ }
+
+ throw new Error(`Cannot determine architect configuration for target: ${buildTarget}.`);
+}
diff --git a/src/lib/schematics/utils/ast.ts b/src/lib/schematics/utils/ast.ts
index f95424846d12..5f8d010deda0 100644
--- a/src/lib/schematics/utils/ast.ts
+++ b/src/lib/schematics/utils/ast.ts
@@ -7,12 +7,14 @@
*/
import {SchematicsException, Tree} from '@angular-devkit/schematics';
+import {Schema as ComponentOptions} from '@schematics/angular/component/schema';
import {addImportToModule} from '@schematics/angular/utility/ast-utils';
import {InsertChange} from '@schematics/angular/utility/change';
import {getWorkspace, WorkspaceProject} from '@schematics/angular/utility/config';
import {findModuleFromOptions as internalFindModule} from '@schematics/angular/utility/find-module';
import {getAppModulePath} from '@schematics/angular/utility/ng-ast-utils';
import * as ts from 'typescript';
+import {getProjectMainFile} from './project-main-file';
/** Reads file given path and returns TypeScript source file. */
@@ -28,8 +30,7 @@ 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: WorkspaceProject) {
-
- const modulePath = getAppModulePath(host, project.architect.build.options.main);
+ const modulePath = getAppModulePath(host, getProjectMainFile(project));
addModuleImportToModule(host, modulePath, moduleName, src);
}
@@ -40,8 +41,9 @@ export function addModuleImportToRootModule(host: Tree, moduleName: string, src:
* @param moduleName name of module to import
* @param src src location to import
*/
-export function addModuleImportToModule(
- host: Tree, modulePath: string, moduleName: string, src: string) {
+export function addModuleImportToModule(host: Tree, modulePath: string, moduleName: string,
+ src: string) {
+
const moduleSource = getSourceFile(host, modulePath);
if (!moduleSource) {
@@ -63,8 +65,9 @@ export function addModuleImportToModule(
}
/** Wraps the internal find module from options with undefined path handling */
-export function findModuleFromOptions(host: Tree, options: any) {
+export function findModuleFromOptions(host: Tree, options: ComponentOptions): string | undefined {
const workspace = getWorkspace(host);
+
if (!options.project) {
options.project = Object.keys(workspace.projects)[0];
}
diff --git a/src/lib/schematics/utils/build-component.ts b/src/lib/schematics/utils/build-component.ts
index ad2ec2efe304..6e2c6f64504f 100644
--- a/src/lib/schematics/utils/build-component.ts
+++ b/src/lib/schematics/utils/build-component.ts
@@ -39,6 +39,7 @@ import {validateHtmlSelector, validateName} from '@schematics/angular/utility/va
import {readFileSync} from 'fs';
import {dirname, join, resolve} from 'path';
import * as ts from 'typescript';
+import {getProjectFromWorkspace} from './get-project';
import {getDefaultComponentOptions} from './schematic-options';
function readIntoSourceFile(host: Tree, modulePath: string): ts.SourceFile {
@@ -162,7 +163,7 @@ export function buildComponent(options: ComponentOptions,
return (host: Tree, context: FileSystemSchematicContext) => {
const workspace = getWorkspace(host);
- const project = workspace.projects[options.project || workspace.defaultProject];
+ const project = getProjectFromWorkspace(workspace, options.project);
const defaultComponentOptions = getDefaultComponentOptions(project);
const schematicFilesUrl = './files';
@@ -216,8 +217,10 @@ export function buildComponent(options: ComponentOptions,
options.inlineTemplate ? filter(path => !path.endsWith('.html')) : noop(),
// Treat the template options as any, because the type definition for the template options
// is made unnecessarily explicit. Every type of object can be used in the EJS template.
- template({ indentTextContent, resolvedFiles, ...baseTemplateContext} as any),
- move(null, parsedPath.path),
+ template({indentTextContent, resolvedFiles, ...baseTemplateContext} as any),
+ // TODO(devversion): figure out why we cannot just remove the first parameter
+ // See for example: angular-cli#schematics/angular/component/index.ts#L160
+ move(null as any, parsedPath.path),
]);
return chain([
@@ -228,5 +231,3 @@ export function buildComponent(options: ComponentOptions,
])(host, context);
};
}
-
-// TODO(paul): move this utility out of the `devkit-utils` because it's no longer taken from there.
diff --git a/src/lib/schematics/utils/parse5-element.ts b/src/lib/schematics/utils/parse5-element.ts
index 6b275ff1d484..e833805d448a 100644
--- a/src/lib/schematics/utils/parse5-element.ts
+++ b/src/lib/schematics/utils/parse5-element.ts
@@ -13,13 +13,18 @@ export function getChildElementIndentation(element: DefaultTreeElement) {
const childElement = element.childNodes
.find(node => node['tagName']) as DefaultTreeElement | null;
+ if ((childElement && !childElement.sourceCodeLocation) || !element.sourceCodeLocation) {
+ throw new Error('Cannot determine child element indentation because the specified Parse5 ' +
+ 'element does not have any source code location metadata.');
+ }
+
const startColumns = childElement ?
// In case there are child elements inside of the element, we assume that their
// indentation is also applicable for other child elements.
- childElement.sourceCodeLocation.startCol :
+ childElement.sourceCodeLocation!.startCol :
// In case there is no child element, we just assume that child elements should be indented
// by two spaces.
- element.sourceCodeLocation.startCol + 2;
+ element.sourceCodeLocation!.startCol + 2;
// Since Parse5 does not set the `startCol` properties as zero-based, we need to subtract
// one column in order to have a proper zero-based offset for the indentation.
diff --git a/src/lib/schematics/install/gestures/project-main-file.ts b/src/lib/schematics/utils/project-main-file.ts
similarity index 61%
rename from src/lib/schematics/install/gestures/project-main-file.ts
rename to src/lib/schematics/utils/project-main-file.ts
index 14ce6f603e02..71c87c6c264f 100644
--- a/src/lib/schematics/install/gestures/project-main-file.ts
+++ b/src/lib/schematics/utils/project-main-file.ts
@@ -8,15 +8,16 @@
import {SchematicsException} from '@angular-devkit/schematics';
import {WorkspaceProject} from '@schematics/angular/utility/config';
+import {getArchitectOptions} from './architect-options';
/** Looks for the main TypeScript file in the given project and returns its path. */
export function getProjectMainFile(project: WorkspaceProject): string {
- const buildTarget = project.architect.build.options;
+ const buildOptions = getArchitectOptions(project, 'build');
- if (buildTarget.main) {
- return buildTarget.main;
+ if (!buildOptions.main) {
+ throw new SchematicsException(`Could not find the project main file inside of the ` +
+ `workspace config (${project.sourceRoot})`);
}
- throw new SchematicsException(
- 'Could not find the project main file inside of the workspace config.');
+ return buildOptions.main;
}
diff --git a/src/lib/schematics/utils/project-style-file.ts b/src/lib/schematics/utils/project-style-file.ts
index 8a706be0b5fe..bb5340d2d65f 100644
--- a/src/lib/schematics/utils/project-style-file.ts
+++ b/src/lib/schematics/utils/project-style-file.ts
@@ -8,6 +8,7 @@
import {normalize} from '@angular-devkit/core';
import {WorkspaceProject} from '@schematics/angular/utility/config';
+import {getArchitectOptions} from './architect-options';
/** Regular expression that matches all possible Angular CLI default style files. */
const defaultStyleFileRegex = /styles\.(c|le|sc)ss/;
@@ -20,10 +21,10 @@ const validStyleFileRegex = /\.(c|le|sc)ss/;
* extension is specified, any style file with a valid extension will be returned.
*/
export function getProjectStyleFile(project: WorkspaceProject, extension?: string): string | null {
- const buildTarget = project.architect['build'];
+ const buildOptions = getArchitectOptions(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.styles && buildOptions.styles.length) {
+ const styles = buildOptions.styles.map(s => typeof s === 'string' ? s : s.input);
// Look for the default style file that is generated for new projects by the Angular CLI. This
// default style file is usually called `styles.ext` unless it has been changed explicitly.