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"