diff --git a/packages/schematics/angular/tailwind/index.ts b/packages/schematics/angular/tailwind/index.ts index 018f0f3b9f95..540b38310178 100644 --- a/packages/schematics/angular/tailwind/index.ts +++ b/packages/schematics/angular/tailwind/index.ts @@ -48,9 +48,11 @@ function addTailwindStyles(options: { project: string }, project: ProjectDefinit let stylesheetPath: string | undefined; if (styles) { + // Only consider workspace source files. Entries such as package specifiers + // (`@angular/material/prebuilt-themes/...`) or files within `node_modules` must not be modified. stylesheetPath = styles .map((s) => (typeof s === 'string' ? s : s.input)) - .find((p) => p.endsWith('.css')); + .find((p) => p.endsWith('.css') && !/(?:^|\/)node_modules\//.test(p) && tree.exists(p)); } if (!stylesheetPath) { diff --git a/packages/schematics/angular/tailwind/index_spec.ts b/packages/schematics/angular/tailwind/index_spec.ts index a70c3b3eef8d..b55d76fff385 100644 --- a/packages/schematics/angular/tailwind/index_spec.ts +++ b/packages/schematics/angular/tailwind/index_spec.ts @@ -129,4 +129,40 @@ describe('Tailwind Schematic', () => { expect(tree.exists('/projects/bar/.postcssrc.json')).toBe(false); }); }); + + describe('with non-workspace stylesheets', () => { + function setStyles(tree: UnitTestTree, styles: string[]): void { + const workspace = getWorkspace(tree); + workspace.projects.bar.architect.build.options.styles = styles; + tree.overwrite('/angular.json', JSON.stringify(workspace, null, 2)); + } + + it('should create a tailwind stylesheet when the only CSS entry is a package theme', async () => { + setStyles(appTree, [ + '@angular/material/prebuilt-themes/azure-blue.css', + 'projects/bar/src/styles.scss', + ]); + + const tree = await schematicRunner.runSchematic('tailwind', { project: 'bar' }, appTree); + + expect(tree.exists('/projects/bar/src/tailwind.css')).toBe(true); + const workspace = getWorkspace(tree); + expect(workspace.projects.bar.architect.build.options.styles).toEqual([ + 'projects/bar/src/tailwind.css', + '@angular/material/prebuilt-themes/azure-blue.css', + 'projects/bar/src/styles.scss', + ]); + }); + + it('should not modify a stylesheet inside node_modules', async () => { + const vendorStylesheet = 'node_modules/bootstrap/dist/css/bootstrap.min.css'; + appTree.create(`/${vendorStylesheet}`, 'body { margin: 0; }'); + setStyles(appTree, [vendorStylesheet, 'projects/bar/src/styles.css']); + + const tree = await schematicRunner.runSchematic('tailwind', { project: 'bar' }, appTree); + + expect(tree.readContent(`/${vendorStylesheet}`)).not.toContain('tailwindcss'); + expect(tree.readContent('/projects/bar/src/styles.css')).toContain(`@import 'tailwindcss';`); + }); + }); });