From a48725f5f6a1ae2ace952ea00efb44cd3dcb9721 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Thu, 5 Jun 2025 15:10:21 +0000 Subject: [PATCH] refactor: fix schematics not respecting new `-module.ts` suffix Angular CLI generates `-module.ts` suffix when using `--standalone: false`. Our schematic tests generate the same, and fail currently because some files weren't picked up properly by the rule. This commit fixes this, and adds supports for the new suffix in a naive way until https://github.com/angular/angular-cli/pull/30471 is available. --- .../ng-generate/drag-drop/index.spec.ts | 11 ++++--- src/cdk/schematics/utils/build-component.ts | 3 +- .../ng-generate/address-form/index.spec.ts | 11 ++++--- .../ng-generate/dashboard/index.spec.ts | 11 ++++--- .../ng-generate/navigation/index.spec.ts | 11 ++++--- .../ng-generate/table/index.spec.ts | 11 ++++--- .../schematics/ng-generate/tree/index.spec.ts | 11 ++++--- tools/defaults.bzl | 31 ++++++++++--------- 8 files changed, 54 insertions(+), 46 deletions(-) diff --git a/src/cdk/schematics/ng-generate/drag-drop/index.spec.ts b/src/cdk/schematics/ng-generate/drag-drop/index.spec.ts index d20459057d12..2c962db32dff 100644 --- a/src/cdk/schematics/ng-generate/drag-drop/index.spec.ts +++ b/src/cdk/schematics/ng-generate/drag-drop/index.spec.ts @@ -12,6 +12,7 @@ describe('CDK drag-drop schematic', () => { // updating the other tests as well because `createTestApp` is responsible for creating // the project. project: 'material', + module: 'app-module.ts', }; beforeEach(() => { @@ -21,7 +22,7 @@ describe('CDK drag-drop schematic', () => { it('should create drag-drop files and add them to module', async () => { const app = await createTestApp(runner, {standalone: false}); const tree = await runner.runSchematic('drag-drop', baseOptions, app); - const moduleContent = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const moduleContent = getFileContent(tree, '/projects/material/src/app/app-module.ts'); const files = tree.files; expect(files).toContain('/projects/material/src/app/foo/foo.component.css'); @@ -36,7 +37,7 @@ describe('CDK drag-drop schematic', () => { it('should add drag-drop module', async () => { const app = await createTestApp(runner, {standalone: false}); const tree = await runner.runSchematic('drag-drop', baseOptions, app); - const moduleContent = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const moduleContent = getFileContent(tree, '/projects/material/src/app/app-module.ts'); expect(moduleContent).toContain('DragDropModule'); }); @@ -45,7 +46,7 @@ describe('CDK drag-drop schematic', () => { it('should generate a standalone component', async () => { const app = await createTestApp(runner, {standalone: false}); const tree = await runner.runSchematic('drag-drop', {...baseOptions, standalone: true}, app); - const module = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const module = getFileContent(tree, '/projects/material/src/app/app-module.ts'); const component = getFileContent(tree, '/projects/material/src/app/foo/foo.component.ts'); expect(module).not.toContain('DragDropModule'); @@ -58,7 +59,7 @@ describe('CDK drag-drop schematic', () => { it('should generate a component with no imports and standalone false', async () => { const app = await createTestApp(runner, {standalone: false}); const tree = await runner.runSchematic('drag-drop', {...baseOptions, standalone: false}, app); - const module = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const module = getFileContent(tree, '/projects/material/src/app/app-module.ts'); const component = getFileContent(tree, '/projects/material/src/app/foo/foo.component.ts'); expect(module).toContain('DragDropModule'); @@ -74,7 +75,7 @@ describe('CDK drag-drop schematic', () => { const component = getFileContent(tree, '/projects/material/src/app/foo/foo.component.ts'); const test = getFileContent(tree, '/projects/material/src/app/foo/foo.component.spec.ts'); - expect(tree.exists('/projects/material/src/app/app.module.ts')).toBe(false); + expect(tree.exists('/projects/material/src/app/app-module.ts')).toBe(false); expect(component).toContain('imports: ['); diff --git a/src/cdk/schematics/utils/build-component.ts b/src/cdk/schematics/utils/build-component.ts index a3dba2f64f9c..1e1433266e16 100644 --- a/src/cdk/schematics/utils/build-component.ts +++ b/src/cdk/schematics/utils/build-component.ts @@ -194,7 +194,8 @@ export function buildComponent( options.standalone = await isStandaloneSchematic(host, options); if (!options.standalone) { - options.module = findModuleFromOptions(host, options); + // TODO: Remove ext option when the Angular CLI looks for both candidate locations. + options.module = findModuleFromOptions(host, {...options, moduleExt: 'module.ts'}); } const parsedPath = parseName(options.path!, options.name); diff --git a/src/material/schematics/ng-generate/address-form/index.spec.ts b/src/material/schematics/ng-generate/address-form/index.spec.ts index f0c5e08b723d..3df760bb565a 100644 --- a/src/material/schematics/ng-generate/address-form/index.spec.ts +++ b/src/material/schematics/ng-generate/address-form/index.spec.ts @@ -9,6 +9,7 @@ describe('Material address-form schematic', () => { const baseOptions: Schema = { name: 'foo', project: 'material', + module: 'app-module.ts', }; beforeEach(() => { @@ -25,7 +26,7 @@ describe('Material address-form schematic', () => { expect(files).toContain('/projects/material/src/app/foo/foo.component.spec.ts'); expect(files).toContain('/projects/material/src/app/foo/foo.component.ts'); - const moduleContent = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const moduleContent = getFileContent(tree, '/projects/material/src/app/app-module.ts'); expect(moduleContent).toMatch(/import.*Foo.*from '.\/foo\/foo.component'/); expect(moduleContent).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+FooComponent\r?\n/m); }); @@ -33,7 +34,7 @@ describe('Material address-form schematic', () => { it('should add address-form imports to module', async () => { const app = await createTestApp(runner, {standalone: false}); const tree = await runner.runSchematic('address-form', baseOptions, app); - const moduleContent = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const moduleContent = getFileContent(tree, '/projects/material/src/app/app-module.ts'); expect(moduleContent).toContain('MatInputModule'); expect(moduleContent).toContain('MatButtonModule'); @@ -58,7 +59,7 @@ describe('Material address-form schematic', () => { {...baseOptions, standalone: true}, app, ); - const module = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const module = getFileContent(tree, '/projects/material/src/app/app-module.ts'); const content = getFileContent(tree, '/projects/material/src/app/foo/foo.component.ts'); const requiredModules = [ 'MatInputModule', @@ -84,7 +85,7 @@ describe('Material address-form schematic', () => { {...baseOptions, standalone: false}, app, ); - const module = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const module = getFileContent(tree, '/projects/material/src/app/app-module.ts'); const content = getFileContent(tree, '/projects/material/src/app/foo/foo.component.ts'); const requiredModules = [ 'MatInputModule', @@ -108,7 +109,7 @@ describe('Material address-form schematic', () => { const tree = await runner.runSchematic('address-form', baseOptions, app); const component = getFileContent(tree, '/projects/material/src/app/foo/foo.component.ts'); - expect(tree.exists('/projects/material/src/app/app.module.ts')).toBe(false); + expect(tree.exists('/projects/material/src/app/app-module.ts')).toBe(false); expect(component).toContain('imports: ['); }); }); diff --git a/src/material/schematics/ng-generate/dashboard/index.spec.ts b/src/material/schematics/ng-generate/dashboard/index.spec.ts index 1d867252f67f..81f1e2ca3109 100644 --- a/src/material/schematics/ng-generate/dashboard/index.spec.ts +++ b/src/material/schematics/ng-generate/dashboard/index.spec.ts @@ -9,6 +9,7 @@ describe('material-dashboard-schematic', () => { const baseOptions: Schema = { name: 'foo', project: 'material', + module: './app-module.ts', }; beforeEach(() => { @@ -25,7 +26,7 @@ describe('material-dashboard-schematic', () => { expect(files).toContain('/projects/material/src/app/foo/foo.component.spec.ts'); expect(files).toContain('/projects/material/src/app/foo/foo.component.ts'); - const moduleContent = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const moduleContent = getFileContent(tree, '/projects/material/src/app/app-module.ts'); expect(moduleContent).toMatch(/import.*Foo.*from '.\/foo\/foo.component'/); expect(moduleContent).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+FooComponent\r?\n/m); }); @@ -33,7 +34,7 @@ describe('material-dashboard-schematic', () => { it('should add dashboard imports to module', async () => { const app = await createTestApp(runner, {standalone: false}); const tree = await runner.runSchematic('dashboard', baseOptions, app); - const moduleContent = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const moduleContent = getFileContent(tree, '/projects/material/src/app/app-module.ts'); expect(moduleContent).toContain('MatGridListModule'); expect(moduleContent).toContain('MatCardModule'); @@ -62,7 +63,7 @@ describe('material-dashboard-schematic', () => { it('should generate a standalone component', async () => { const app = await createTestApp(runner, {standalone: false}); const tree = await runner.runSchematic('dashboard', {...baseOptions, standalone: true}, app); - const module = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const module = getFileContent(tree, '/projects/material/src/app/app-module.ts'); const component = getFileContent(tree, '/projects/material/src/app/foo/foo.component.ts'); const requiredModules = [ 'MatButtonModule', @@ -84,7 +85,7 @@ describe('material-dashboard-schematic', () => { it('should generate a component with no imports and standalone false', async () => { const app = await createTestApp(runner, {standalone: false}); const tree = await runner.runSchematic('dashboard', {...baseOptions, standalone: false}, app); - const module = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const module = getFileContent(tree, '/projects/material/src/app/app-module.ts'); const component = getFileContent(tree, '/projects/material/src/app/foo/foo.component.ts'); const requiredModules = [ 'MatButtonModule', @@ -113,7 +114,7 @@ describe('material-dashboard-schematic', () => { '/projects/material/src/app/foo/foo.component.ts', ); - expect(tree.exists('/projects/material/src/app/app.module.ts')).toBe(false); + expect(tree.exists('/projects/material/src/app/app-module.ts')).toBe(false); expect(componentContent).toContain('imports: ['); }); }); diff --git a/src/material/schematics/ng-generate/navigation/index.spec.ts b/src/material/schematics/ng-generate/navigation/index.spec.ts index 266db2943c87..b7416967e677 100644 --- a/src/material/schematics/ng-generate/navigation/index.spec.ts +++ b/src/material/schematics/ng-generate/navigation/index.spec.ts @@ -10,6 +10,7 @@ describe('material-navigation-schematic', () => { const baseOptions: Schema = { name: 'foo', project: 'material', + module: './app-module.ts', }; beforeEach(() => { @@ -17,7 +18,7 @@ describe('material-navigation-schematic', () => { }); function expectNavigationSchematicModuleImports(tree: UnitTestTree) { - const moduleContent = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const moduleContent = getFileContent(tree, '/projects/material/src/app/app-module.ts'); expect(moduleContent).toMatch(/MatToolbarModule,\s+/); expect(moduleContent).toMatch(/MatButtonModule,\s+/); expect(moduleContent).toMatch(/MatSidenavModule,\s+/); @@ -44,7 +45,7 @@ describe('material-navigation-schematic', () => { expect(files).toContain('/projects/material/src/app/foo/foo.component.spec.ts'); expect(files).toContain('/projects/material/src/app/foo/foo.component.ts'); - const moduleContent = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const moduleContent = getFileContent(tree, '/projects/material/src/app/app-module.ts'); expect(moduleContent).toMatch(/import.*Foo.*from '.\/foo\/foo.component'/); expect(moduleContent).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+FooComponent\r?\n/m); }); @@ -73,7 +74,7 @@ describe('material-navigation-schematic', () => { it('should generate a standalone component', async () => { const app = await createTestApp(runner, {standalone: false}); const tree = await runner.runSchematic('navigation', {...baseOptions, standalone: true}, app); - const module = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const module = getFileContent(tree, '/projects/material/src/app/app-module.ts'); const component = getFileContent(tree, '/projects/material/src/app/foo/foo.component.ts'); const requiredModules = [ 'MatToolbarModule', @@ -99,7 +100,7 @@ describe('material-navigation-schematic', () => { {...baseOptions, standalone: false}, app, ); - const module = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const module = getFileContent(tree, '/projects/material/src/app/app-module.ts'); const component = getFileContent(tree, '/projects/material/src/app/foo/foo.component.ts'); const requiredModules = [ 'MatToolbarModule', @@ -128,7 +129,7 @@ describe('material-navigation-schematic', () => { '/projects/material/src/app/foo/foo.component.ts', ); - expect(tree.exists('/projects/material/src/app/app.module.ts')).toBe(false); + expect(tree.exists('/projects/material/src/app/app-module.ts')).toBe(false); expect(componentContent).toContain('imports: ['); }); }); diff --git a/src/material/schematics/ng-generate/table/index.spec.ts b/src/material/schematics/ng-generate/table/index.spec.ts index 45887d312fd4..d8723a7869a8 100644 --- a/src/material/schematics/ng-generate/table/index.spec.ts +++ b/src/material/schematics/ng-generate/table/index.spec.ts @@ -9,6 +9,7 @@ describe('material-table-schematic', () => { const baseOptions: Schema = { name: 'foo', project: 'material', + module: './app-module.ts', }; beforeEach(() => { @@ -26,7 +27,7 @@ describe('material-table-schematic', () => { expect(files).toContain('/projects/material/src/app/foo/foo.component.ts'); expect(files).toContain('/projects/material/src/app/foo/foo-datasource.ts'); - const moduleContent = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const moduleContent = getFileContent(tree, '/projects/material/src/app/app-module.ts'); expect(moduleContent).toMatch(/import.*Foo.*from '.\/foo\/foo.component'/); expect(moduleContent).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+FooComponent\r?\n/m); @@ -49,7 +50,7 @@ describe('material-table-schematic', () => { it('should add table imports to module', async () => { const app = await createTestApp(runner, {standalone: false}); const tree = await runner.runSchematic('table', baseOptions, app); - const moduleContent = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const moduleContent = getFileContent(tree, '/projects/material/src/app/app-module.ts'); expect(moduleContent).toContain('MatTableModule'); expect(moduleContent).toContain('MatPaginatorModule'); @@ -74,7 +75,7 @@ describe('material-table-schematic', () => { it('should generate a standalone component', async () => { const app = await createTestApp(runner, {standalone: false}); const tree = await runner.runSchematic('table', {...baseOptions, standalone: true}, app); - const module = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const module = getFileContent(tree, '/projects/material/src/app/app-module.ts'); const component = getFileContent(tree, '/projects/material/src/app/foo/foo.component.ts'); const requiredModules = ['MatTableModule', 'MatPaginatorModule', 'MatSortModule']; @@ -90,7 +91,7 @@ describe('material-table-schematic', () => { it('should generate a component with no imports and standalone false', async () => { const app = await createTestApp(runner, {standalone: false}); const tree = await runner.runSchematic('table', {...baseOptions, standalone: false}, app); - const module = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const module = getFileContent(tree, '/projects/material/src/app/app-module.ts'); const component = getFileContent(tree, '/projects/material/src/app/foo/foo.component.ts'); const requiredModules = ['MatTableModule', 'MatPaginatorModule', 'MatSortModule']; @@ -113,7 +114,7 @@ describe('material-table-schematic', () => { '/projects/material/src/app/foo/foo.component.ts', ); - expect(tree.exists('/projects/material/src/app/app.module.ts')).toBe(false); + expect(tree.exists('/projects/material/src/app/app-module.ts')).toBe(false); expect(componentContent).toContain('imports: ['); }); }); diff --git a/src/material/schematics/ng-generate/tree/index.spec.ts b/src/material/schematics/ng-generate/tree/index.spec.ts index 65f1affbf815..d95582d6f498 100644 --- a/src/material/schematics/ng-generate/tree/index.spec.ts +++ b/src/material/schematics/ng-generate/tree/index.spec.ts @@ -9,6 +9,7 @@ describe('Material tree schematic', () => { const baseOptions: Schema = { name: 'foo', project: 'material', + module: './app-module.ts', }; beforeEach(() => { @@ -25,7 +26,7 @@ describe('Material tree schematic', () => { expect(files).toContain('/projects/material/src/app/foo/foo.component.spec.ts'); expect(files).toContain('/projects/material/src/app/foo/foo.component.ts'); - const moduleContent = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const moduleContent = getFileContent(tree, '/projects/material/src/app/app-module.ts'); expect(moduleContent).toMatch(/import.*Foo.*from '.\/foo\/foo.component'/); expect(moduleContent).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+FooComponent\r?\n/m); }); @@ -33,7 +34,7 @@ describe('Material tree schematic', () => { it('should add tree imports to module', async () => { const app = await createTestApp(runner, {standalone: false}); const tree = await runner.runSchematic('tree', baseOptions, app); - const moduleContent = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const moduleContent = getFileContent(tree, '/projects/material/src/app/app-module.ts'); expect(moduleContent).toContain('MatTreeModule'); expect(moduleContent).toContain('MatIconModule'); @@ -52,7 +53,7 @@ describe('Material tree schematic', () => { it('should generate a standalone component', async () => { const app = await createTestApp(runner, {standalone: false}); const tree = await runner.runSchematic('tree', {...baseOptions, standalone: true}, app); - const module = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const module = getFileContent(tree, '/projects/material/src/app/app-module.ts'); const component = getFileContent(tree, '/projects/material/src/app/foo/foo.component.ts'); const requiredModules = ['MatTreeModule', 'MatButtonModule', 'MatIconModule']; @@ -68,7 +69,7 @@ describe('Material tree schematic', () => { it('should generate a component with no imports and standalone false', async () => { const app = await createTestApp(runner, {standalone: false}); const tree = await runner.runSchematic('tree', {...baseOptions, standalone: false}, app); - const module = getFileContent(tree, '/projects/material/src/app/app.module.ts'); + const module = getFileContent(tree, '/projects/material/src/app/app-module.ts'); const component = getFileContent(tree, '/projects/material/src/app/foo/foo.component.ts'); const requiredModules = ['MatTreeModule', 'MatButtonModule', 'MatIconModule']; @@ -91,7 +92,7 @@ describe('Material tree schematic', () => { '/projects/material/src/app/foo/foo.component.ts', ); - expect(tree.exists('/projects/material/src/app/app.module.ts')).toBe(false); + expect(tree.exists('/projects/material/src/app/app-module.ts')).toBe(false); expect(componentContent).toContain('imports: ['); }); }); diff --git a/tools/defaults.bzl b/tools/defaults.bzl index 94536aa7e108..7d033f2935ce 100644 --- a/tools/defaults.bzl +++ b/tools/defaults.bzl @@ -1,23 +1,23 @@ # Re-export of Bazel rules with repository-wide defaults -load("@rules_pkg//:pkg.bzl", "pkg_tar") -load("@rules_sass//src:index.bzl", _sass_binary = "sass_binary", _sass_library = "sass_library") -load("@rules_angular//src/ng_package:index.bzl", _ng_package = "ng_package") -load("//:packages.bzl", "NO_STAMP_NPM_PACKAGE_SUBSTITUTIONS", "NPM_PACKAGE_SUBSTITUTIONS") -load("//:pkg-externals.bzl", "PKG_EXTERNALS") -load("//tools/markdown-to-html:index.bzl", _markdown_to_html = "markdown_to_html") -load("//tools/extract-tokens:index.bzl", _extract_tokens = "extract_tokens") -load("//tools/bazel:ng_package_interop.bzl", "ng_package_interop") +load("@aspect_rules_jasmine//jasmine:defs.bzl", _jasmine_test = "jasmine_test") +load("@aspect_rules_js//npm:defs.bzl", _npm_package = "npm_package") load("@devinfra//bazel/http-server:index.bzl", _http_server = "http_server") load("@devinfra//bazel/spec-bundling:index_rjs.bzl", _spec_bundle = "spec_bundle") -load("//tools/bazel:web_test_suite.bzl", _ng_web_test_suite = "ng_web_test_suite") -load("@aspect_rules_js//npm:defs.bzl", _npm_package = "npm_package") +load("@devinfra//bazel/ts_project:index.bzl", "strict_deps_test") +load("@rules_angular//src/ng_package:index.bzl", _ng_package = "ng_package") load("@rules_angular//src/ng_package/text_replace:index.bzl", _text_replace = "text_replace") load("@rules_angular//src/ng_project:index.bzl", _ng_project = "ng_project") load("@rules_angular//src/ts_project:index.bzl", _ts_project = "ts_project") -load("@devinfra//bazel/ts_project:index.bzl", "strict_deps_test") -load("@aspect_rules_jasmine//jasmine:defs.bzl", _jasmine_test = "jasmine_test") load("@rules_browsers//src/protractor_test:index.bzl", "protractor_test") +load("@rules_pkg//:pkg.bzl", "pkg_tar") +load("@rules_sass//src:index.bzl", _sass_binary = "sass_binary", _sass_library = "sass_library") +load("//:packages.bzl", "NO_STAMP_NPM_PACKAGE_SUBSTITUTIONS", "NPM_PACKAGE_SUBSTITUTIONS") +load("//:pkg-externals.bzl", "PKG_EXTERNALS") +load("//tools/bazel:ng_package_interop.bzl", "ng_package_interop") +load("//tools/bazel:web_test_suite.bzl", _ng_web_test_suite = "ng_web_test_suite") +load("//tools/extract-tokens:index.bzl", _extract_tokens = "extract_tokens") +load("//tools/markdown-to-html:index.bzl", _markdown_to_html = "markdown_to_html") # Re-exports to simplify build file load statements markdown_to_html = _markdown_to_html @@ -208,9 +208,10 @@ def jasmine_test(name, data = [], args = [], **kwargs): chdir = native.package_name(), fixed_args = [ "--require=%s/node_modules/source-map-support/register.js" % relative_to_root, - "**/*spec.js", - "**/*spec.mjs", - "**/*spec.cjs", + # Escape so that the `js_binary` launcher triggers Bash expansion. + "'**/*+(.|_)spec.js'", + "'**/*+(.|_)spec.mjs'", + "'**/*+(.|_)spec.cjs'", ] + args, data = data + [ "//:node_modules/source-map-support",