Skip to content

Commit a217e08

Browse files
committed
refactor(@schematics/angular): add PostCSS configuration detection to tailwind schematic
The Tailwind schematic now intelligently handles existing PostCSS configurations. Instead of unconditionally creating a new `.postcssrc.json` file, the schematic now searches for `postcss.config.json` or `.postcssrc.json` in both the workspace and project roots. If an existing configuration file is found, it is updated with the required `@tailwindcss/postcss` plugin. A new configuration file is only created if one does not already exist. This prevents conflicts and makes the schematic safer to use in projects that already have a customized PostCSS setup.
1 parent 58acde9 commit a217e08

File tree

2 files changed

+54
-46
lines changed

2 files changed

+54
-46
lines changed

packages/schematics/angular/tailwind/index.ts

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ import {
2626
addDependency,
2727
updateWorkspace,
2828
} from '../utility';
29+
import { JSONFile } from '../utility/json-file';
2930
import { latestVersions } from '../utility/latest-versions';
3031
import { createProjectSchematic } from '../utility/project';
3132

3233
const TAILWIND_DEPENDENCIES = ['tailwindcss', '@tailwindcss/postcss', 'postcss'];
34+
const POSTCSS_CONFIG_FILES = ['.postcssrc.json', 'postcss.config.json'];
3335

3436
function addTailwindStyles(options: { project: string }, project: ProjectDefinition): Rule {
3537
return async (tree) => {
@@ -85,18 +87,43 @@ function addTailwindStyles(options: { project: string }, project: ProjectDefinit
8587
};
8688
}
8789

88-
export default createProjectSchematic((options, { project }) => {
89-
const templateSource = apply(url('./files'), [
90-
applyTemplates({
91-
...strings,
92-
...options,
93-
}),
94-
move(project.root),
95-
]);
90+
function managePostCssConfiguration(project: ProjectDefinition): Rule {
91+
return async (tree) => {
92+
const searchPaths = ['/', project.root]; // Workspace root and project root
93+
94+
for (const path of searchPaths) {
95+
for (const configFile of POSTCSS_CONFIG_FILES) {
96+
const fullPath = join(path, configFile);
97+
if (tree.exists(fullPath)) {
98+
const postcssConfig = new JSONFile(tree, fullPath);
99+
const tailwindPluginPath = ['plugins', '@tailwindcss/postcss'];
100+
101+
if (postcssConfig.get(tailwindPluginPath) === undefined) {
102+
postcssConfig.modify(tailwindPluginPath, {});
103+
}
104+
105+
// Config found and handled
106+
return;
107+
}
108+
}
109+
}
110+
111+
// No existing config found, so create one from the template
112+
const templateSource = apply(url('./files'), [
113+
applyTemplates({
114+
...strings,
115+
}),
116+
move(project.root),
117+
]);
96118

119+
return mergeWith(templateSource);
120+
};
121+
}
122+
123+
export default createProjectSchematic((options, { project }) => {
97124
return chain([
98125
addTailwindStyles(options, project),
99-
mergeWith(templateSource),
126+
managePostCssConfiguration(project),
100127
...TAILWIND_DEPENDENCIES.map((name) =>
101128
addDependency(name, latestVersions[name], {
102129
type: DependencyType.Dev,

packages/schematics/angular/tailwind/index_spec.ts

Lines changed: 18 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -105,49 +105,30 @@ describe('Tailwind Schematic', () => {
105105
});
106106
});
107107

108-
describe('with complex build configurations', () => {
109-
beforeEach(async () => {
110-
appTree = await schematicRunner.runSchematic('workspace', workspaceOptions);
111-
appTree = await schematicRunner.runSchematic(
112-
'application',
113-
{ ...appOptions, style: Style.Scss },
114-
appTree,
115-
);
116-
117-
const angularJson = JSON.parse(appTree.readContent('/angular.json'));
118-
angularJson.projects.bar.architect.build.configurations = {
119-
...angularJson.projects.bar.architect.build.configurations,
120-
staging: {
121-
styles: [],
122-
},
123-
production: {
124-
styles: ['projects/bar/src/styles.prod.scss'],
125-
},
126-
development: {
127-
// No styles property
128-
},
129-
};
130-
appTree.overwrite('/angular.json', JSON.stringify(angularJson, null, 2));
108+
describe('with postcss configuration', () => {
109+
it('should create a .postcssrc.json if one does not exist', async () => {
110+
const tree = await schematicRunner.runSchematic('tailwind', { project: 'bar' }, appTree);
111+
expect(tree.exists('/projects/bar/.postcssrc.json')).toBe(true);
131112
});
132113

133-
it('should add tailwind.css to all configurations with styles', async () => {
114+
it('should update an existing .postcssrc.json in the project root', async () => {
115+
appTree.create(
116+
'/projects/bar/.postcssrc.json',
117+
JSON.stringify({ plugins: { autoprefixer: {} } }),
118+
);
134119
const tree = await schematicRunner.runSchematic('tailwind', { project: 'bar' }, appTree);
135-
const angularJson = JSON.parse(tree.readContent('/angular.json'));
136-
const { configurations } = angularJson.projects.bar.architect.build;
137-
138-
expect(configurations.production.styles).toEqual([
139-
'projects/bar/src/tailwind.css',
140-
'projects/bar/src/styles.prod.scss',
141-
]);
142-
expect(configurations.staging.styles).toEqual(['projects/bar/src/tailwind.css']);
120+
const postCssConfig = JSON.parse(tree.readContent('/projects/bar/.postcssrc.json'));
121+
expect(postCssConfig.plugins['@tailwindcss/postcss']).toBeDefined();
122+
expect(postCssConfig.plugins['autoprefixer']).toBeDefined();
143123
});
144124

145-
it('should not modify configurations without a styles property', async () => {
125+
it('should update an existing postcss.config.json in the workspace root', async () => {
126+
appTree.create('/postcss.config.json', JSON.stringify({ plugins: { autoprefixer: {} } }));
146127
const tree = await schematicRunner.runSchematic('tailwind', { project: 'bar' }, appTree);
147-
const angularJson = JSON.parse(tree.readContent('/angular.json'));
148-
const { configurations } = angularJson.projects.bar.architect.build;
149-
150-
expect(configurations.development.styles).toBeUndefined();
128+
const postCssConfig = JSON.parse(tree.readContent('/postcss.config.json'));
129+
expect(postCssConfig.plugins['@tailwindcss/postcss']).toBeDefined();
130+
expect(postCssConfig.plugins['autoprefixer']).toBeDefined();
131+
expect(tree.exists('/projects/bar/.postcssrc.json')).toBe(false);
151132
});
152133
});
153134
});

0 commit comments

Comments
 (0)