diff --git a/CHANGELOG.md b/CHANGELOG.md
index 50dffbff352a..b529df95b721 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,31 @@
+
+
+# 13.3.2 (2022-04-06)
+
+### @angular/cli
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------------- |
+| [49dc63d09](https://github.com/angular/angular-cli/commit/49dc63d09a7a7f2b7759b47e79fac934b867e9b4) | fix | ensure lint command auto-add exits after completion |
+
+### @schematics/angular
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------- |
+| [bbe74b87e](https://github.com/angular/angular-cli/commit/bbe74b87e52579c06b911db6173f33c67b8010a6) | fix | provide actionable error message when routing declaration cannot be found |
+
+### @angular-devkit/build-angular
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------- |
+| [c97c8e7c9](https://github.com/angular/angular-cli/commit/c97c8e7c9bbcad66ba80967681cac46042c3aca7) | fix | update `minimatch` dependency to `3.0.5` |
+
+## Special Thanks
+
+Alan Agius, Charles Lyding and Morga Cezary
+
+
+
# 13.3.1 (2022-03-30)
diff --git a/package.json b/package.json
index d2f94159b874..363825132847 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@angular/devkit-repo",
- "version": "13.3.1",
+ "version": "13.3.2",
"private": true,
"description": "Software Development Kit for Angular",
"bin": {
@@ -172,7 +172,7 @@
"loader-utils": "3.2.0",
"magic-string": "0.25.7",
"mini-css-extract-plugin": "2.5.3",
- "minimatch": "3.0.4",
+ "minimatch": "3.0.5",
"minimist": "1.2.6",
"ng-packagr": "13.1.3",
"node-fetch": "^2.2.0",
diff --git a/packages/angular/cli/commands/lint-impl.ts b/packages/angular/cli/commands/lint-impl.ts
index e9fb4dc801b3..eb4c5d5fcfc1 100644
--- a/packages/angular/cli/commands/lint-impl.ts
+++ b/packages/angular/cli/commands/lint-impl.ts
@@ -50,8 +50,9 @@ export class LintCommand extends ArchitectCommand {
if (error) {
throw error;
}
-
- return status ?? 0;
}
+
+ // Return an exit code to force the command to exit after adding the package
+ return 1;
}
}
diff --git a/packages/angular_devkit/build_angular/package.json b/packages/angular_devkit/build_angular/package.json
index ce39934dbf5c..3c83f5db86fe 100644
--- a/packages/angular_devkit/build_angular/package.json
+++ b/packages/angular_devkit/build_angular/package.json
@@ -42,7 +42,7 @@
"license-webpack-plugin": "4.0.2",
"loader-utils": "3.2.0",
"mini-css-extract-plugin": "2.5.3",
- "minimatch": "3.0.4",
+ "minimatch": "3.0.5",
"open": "8.4.0",
"ora": "5.4.1",
"parse5-html-rewriting-stream": "6.0.1",
diff --git a/packages/schematics/angular/utility/ast-utils.ts b/packages/schematics/angular/utility/ast-utils.ts
index cec6ab579cd6..56bde71bc187 100644
--- a/packages/schematics/angular/utility/ast-utils.ts
+++ b/packages/schematics/angular/utility/ast-utils.ts
@@ -189,7 +189,6 @@ export function getSourceNodes(sourceFile: ts.SourceFile): ts.Node[] {
export function findNode(node: ts.Node, kind: ts.SyntaxKind, text: string): ts.Node | null {
if (node.kind === kind && node.getText() === text) {
- // throw new Error(node.getText());
return node;
}
@@ -367,29 +366,28 @@ export function addSymbolToNgModuleMetadata(
importPath: string | null = null,
): Change[] {
const nodes = getDecoratorMetadata(source, 'NgModule', '@angular/core');
- let node: any = nodes[0]; // eslint-disable-line @typescript-eslint/no-explicit-any
+ const node = nodes[0];
// Find the decorator declaration.
- if (!node) {
+ if (!node || !ts.isObjectLiteralExpression(node)) {
return [];
}
// Get all the children property assignment of object literals.
- const matchingProperties = getMetadataField(node as ts.ObjectLiteralExpression, metadataField);
+ const matchingProperties = getMetadataField(node, metadataField);
if (matchingProperties.length == 0) {
// We haven't found the field in the metadata declaration. Insert a new field.
- const expr = node as ts.ObjectLiteralExpression;
let position: number;
let toInsert: string;
- if (expr.properties.length == 0) {
- position = expr.getEnd() - 1;
+ if (node.properties.length == 0) {
+ position = node.getEnd() - 1;
toInsert = `\n ${metadataField}: [\n${tags.indentBy(4)`${symbolName}`}\n ]\n`;
} else {
- node = expr.properties[expr.properties.length - 1];
- position = node.getEnd();
+ const childNode = node.properties[node.properties.length - 1];
+ position = childNode.getEnd();
// Get the indentation of the last element, if any.
- const text = node.getFullText(source);
+ const text = childNode.getFullText(source);
const matches = text.match(/^(\r?\n)(\s*)/);
if (matches) {
toInsert =
@@ -408,40 +406,40 @@ export function addSymbolToNgModuleMetadata(
return [new InsertChange(ngModulePath, position, toInsert)];
}
}
- const assignment = matchingProperties[0] as ts.PropertyAssignment;
+ const assignment = matchingProperties[0];
// If it's not an array, nothing we can do really.
- if (assignment.initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression) {
+ if (
+ !ts.isPropertyAssignment(assignment) ||
+ !ts.isArrayLiteralExpression(assignment.initializer)
+ ) {
return [];
}
- const arrLiteral = assignment.initializer as ts.ArrayLiteralExpression;
- if (arrLiteral.elements.length == 0) {
- // Forward the property.
- node = arrLiteral;
- } else {
- node = arrLiteral.elements;
- }
+ let expresssion: ts.Expression | ts.ArrayLiteralExpression;
+ const assignmentInit = assignment.initializer;
+ const elements = assignmentInit.elements;
- if (Array.isArray(node)) {
- const nodeArray = (node as {}) as Array;
- const symbolsArray = nodeArray.map((node) => tags.oneLine`${node.getText()}`);
+ if (elements.length) {
+ const symbolsArray = elements.map((node) => tags.oneLine`${node.getText()}`);
if (symbolsArray.includes(tags.oneLine`${symbolName}`)) {
return [];
}
- node = node[node.length - 1];
+ expresssion = elements[elements.length - 1];
+ } else {
+ expresssion = assignmentInit;
}
let toInsert: string;
- let position = node.getEnd();
- if (node.kind == ts.SyntaxKind.ArrayLiteralExpression) {
+ let position = expresssion.getEnd();
+ if (ts.isArrayLiteralExpression(expresssion)) {
// We found the field but it's empty. Insert it just before the `]`.
position--;
toInsert = `\n${tags.indentBy(4)`${symbolName}`}\n `;
} else {
// Get the indentation of the last element, if any.
- const text = node.getFullText(source);
+ const text = expresssion.getFullText(source);
const matches = text.match(/^(\r?\n)(\s*)/);
if (matches) {
toInsert = `,${matches[1]}${tags.indentBy(matches[2].length)`${symbolName}`}`;
@@ -449,6 +447,7 @@ export function addSymbolToNgModuleMetadata(
toInsert = `, ${symbolName}`;
}
}
+
if (importPath !== null) {
return [
new InsertChange(ngModulePath, position, toInsert),
@@ -604,9 +603,12 @@ export function getEnvironmentExportName(source: ts.SourceFile): string | null {
*/
export function getRouterModuleDeclaration(source: ts.SourceFile): ts.Expression | undefined {
const result = getDecoratorMetadata(source, 'NgModule', '@angular/core');
- const node = result[0] as ts.ObjectLiteralExpression;
- const matchingProperties = getMetadataField(node, 'imports');
+ const node = result[0];
+ if (!node || !ts.isObjectLiteralExpression(node)) {
+ return undefined;
+ }
+ const matchingProperties = getMetadataField(node, 'imports');
if (!matchingProperties) {
return;
}
@@ -634,7 +636,10 @@ export function addRouteDeclarationToModule(
): Change {
const routerModuleExpr = getRouterModuleDeclaration(source);
if (!routerModuleExpr) {
- throw new Error(`Couldn't find a route declaration in ${fileToAdd}.`);
+ throw new Error(
+ `Couldn't find a route declaration in ${fileToAdd}.\n` +
+ `Use the '--module' option to specify a different routing module.`,
+ );
}
const scopeConfigMethodArgs = (routerModuleExpr as ts.CallExpression).arguments;
if (!scopeConfigMethodArgs.length) {
diff --git a/packages/schematics/angular/utility/ast-utils_spec.ts b/packages/schematics/angular/utility/ast-utils_spec.ts
index 54ed6d6d936d..fd305aa355b5 100644
--- a/packages/schematics/angular/utility/ast-utils_spec.ts
+++ b/packages/schematics/angular/utility/ast-utils_spec.ts
@@ -260,7 +260,7 @@ describe('ast utils', () => {
const elements = (arrayNode.pop() as ts.ArrayLiteralExpression).elements;
const change = insertAfterLastOccurrence(
- (elements as unknown) as ts.Node[],
+ elements as unknown as ts.Node[],
`, 'bar'`,
filePath,
elements.pos,
@@ -281,7 +281,7 @@ describe('ast utils', () => {
const elements = (arrayNode.pop() as ts.ArrayLiteralExpression).elements;
const change = insertAfterLastOccurrence(
- (elements as unknown) as ts.Node[],
+ elements as unknown as ts.Node[],
`'bar'`,
filePath,
elements.pos,
@@ -312,7 +312,7 @@ describe('ast utils', () => {
const source = getTsSource(modulePath, moduleContent);
const change = () => addRouteDeclarationToModule(source, './src/app', '');
- expect(change).toThrowError(`Couldn't find a route declaration in ./src/app.`);
+ expect(change).toThrowError(/Couldn't find a route declaration in \.\/src\/app/);
});
it(`should throw an error when router module doesn't have arguments`, () => {
@@ -632,6 +632,17 @@ describe('ast utils', () => {
/RouterModule\.forRoot\(\[\r?\n?\s*{ path: 'foo', component: FooComponent },\r?\n?\s*{ path: 'bar', component: BarComponent }\r?\n?\s*\]\)/,
);
});
+
+ it('should error if sourcefile is empty', () => {
+ const change = () =>
+ addRouteDeclarationToModule(
+ getTsSource(modulePath, ''),
+ './src/app',
+ `{ path: 'foo', component: FooComponent }`,
+ );
+
+ expect(change).toThrowError(/Couldn't find a route declaration in \.\/src\/app/);
+ });
});
describe('findNodes', () => {
diff --git a/packages/schematics/angular/utility/find-module.ts b/packages/schematics/angular/utility/find-module.ts
index 3c87085f5452..a420f07eb052 100644
--- a/packages/schematics/angular/utility/find-module.ts
+++ b/packages/schematics/angular/utility/find-module.ts
@@ -54,11 +54,9 @@ export function findModuleFromOptions(host: Tree, options: ModuleOptions): Path
const candidatesDirs = [...candidateSet].sort((a, b) => b.length - a.length);
for (const c of candidatesDirs) {
- const candidateFiles = [
- '',
- `${moduleBaseName}.ts`,
- `${moduleBaseName}${moduleExt}`,
- ].map((x) => join(c, x));
+ const candidateFiles = ['', `${moduleBaseName}.ts`, `${moduleBaseName}${moduleExt}`].map(
+ (x) => join(c, x),
+ );
for (const sc of candidateFiles) {
if (host.exists(sc)) {
@@ -96,7 +94,7 @@ export function findModule(
return join(dir.path, filteredMatches[0]);
} else if (filteredMatches.length > 1) {
throw new Error(
- 'More than one module matches. Use the skip-import option to skip importing ' +
+ `More than one module matches. Use the '--skip-import' option to skip importing ` +
'the component into the closest module or use the module option to specify a module.',
);
}
@@ -107,8 +105,8 @@ export function findModule(
const errorMsg = foundRoutingModule
? 'Could not find a non Routing NgModule.' +
`\nModules with suffix '${routingModuleExt}' are strictly reserved for routing.` +
- '\nUse the skip-import option to skip importing in NgModule.'
- : 'Could not find an NgModule. Use the skip-import option to skip importing in NgModule.';
+ `\nUse the '--skip-import' option to skip importing in NgModule.`
+ : `Could not find an NgModule. Use the '--skip-import' option to skip importing in NgModule.`;
throw new Error(errorMsg);
}
diff --git a/yarn.lock b/yarn.lock
index b002d256b4cb..2a87ca7597e9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7713,10 +7713,10 @@ minimalistic-assert@^1.0.0:
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
-"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
- integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+"minimatch@2 || 3", minimatch@3.0.5, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.5:
+ version "3.0.5"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.5.tgz#4da8f1290ee0f0f8e83d60ca69f8f134068604a3"
+ integrity sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==
dependencies:
brace-expansion "^1.1.7"