Skip to content

feat(@angular-devkit/build-angular): partial custom postcss config support #30592

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/angular/build/src/private.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,8 @@ export { type BundleStats, generateBuildStatsTable } from './utils/stats-table';
export { getSupportedBrowsers } from './utils/supported-browsers';
export { assertCompatibleAngularVersion } from './utils/version';
export { findTests, getTestEntrypoints } from './builders/karma/find-tests';
export {
findTailwindConfiguration,
generateSearchDirectories,
loadPostcssConfiguration,
} from './utils/postcss-configuration';
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/

import { buildWebpackBrowser } from '../../index';
import { BASE_OPTIONS, BROWSER_BUILDER_INFO, describeBuilder } from '../setup';

describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => {
describe('Behavior: "Postcss config file"', () => {
it('applies plugins from config file if one is present', async () => {
// See:
// https://github.com/postcss/postcss-plugin-boilerplate/blob/main/template/index.t.js
await harness.writeFile(
'node_modules/my-postcss-plugin/index.cjs',
`module.exports = (opts = {}) => {
return {
postcssPlugin: 'my-postcss-plugin',
Root(root, postcss) {
const newRule = new postcss.Rule({
selector: '.my-postcss-plugin::before',
});
newRule.append({ text: 'content: "from applied plugin";' });
root.append(newRule);
},
};
};
module.exports.postcss = true;
`,
);

await harness.writeFile(
'postcss.config.json',
JSON.stringify({
plugins: {
'my-postcss-plugin/index.cjs': {},
},
}),
);

harness.useTarget('build', {
...BASE_OPTIONS,
styles: ['src/styles.css'],
});

const { result } = await harness.executeOnce();

expect(result?.success).toBe(true);
harness.expectFile('dist/styles.css').content.toMatch(/from applied plugin/);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@
* found in the LICENSE file at https://angular.dev/license
*/

import { SassWorkerImplementation } from '@angular/build/private';
import {
SassWorkerImplementation,
findTailwindConfiguration,
generateSearchDirectories,
loadPostcssConfiguration,
} from '@angular/build/private';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import * as path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import type { FileImporter } from 'sass';
import type { Configuration, LoaderContext, RuleSetUseItem } from 'webpack';
import { WebpackConfigOptions } from '../../../utils/build-options';
import { findTailwindConfigurationFile } from '../../../utils/tailwind';
import {
AnyComponentStyleBudgetChecker,
PostcssCliResources,
Expand Down Expand Up @@ -75,25 +79,40 @@ export async function getStylesConfig(wco: WebpackConfigOptions): Promise<Config

const extraPostcssPlugins: import('postcss').Plugin[] = [];

// Attempt to setup Tailwind CSS
// Only load Tailwind CSS plugin if configuration file was found.
// This acts as a guard to ensure the project actually wants to use Tailwind CSS.
// The package may be unknowningly present due to a third-party transitive package dependency.
const tailwindConfigPath = await findTailwindConfigurationFile(root, projectRoot);
if (tailwindConfigPath) {
let tailwindPackagePath;
try {
tailwindPackagePath = require.resolve('tailwindcss', { paths: [root] });
} catch {
const relativeTailwindConfigPath = path.relative(root, tailwindConfigPath);
logger.warn(
`Tailwind CSS configuration file found (${relativeTailwindConfigPath})` +
` but the 'tailwindcss' package is not installed.` +
` To enable Tailwind CSS, please install the 'tailwindcss' package.`,
);
const searchDirectories = await generateSearchDirectories([projectRoot, root]);
const postcssConfig = await loadPostcssConfiguration(searchDirectories);

if (postcssConfig) {
for (const [pluginName, pluginOptions] of postcssConfig.plugins) {
const resolvedPlugin = require.resolve(pluginName, { paths: [root] });
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This require.resolve was added to the original code to ensure the plugins are resolved relative to the project.

const { default: plugin } = await import(resolvedPlugin);
if (typeof plugin !== 'function' || plugin.postcss !== true) {
throw new Error(`Attempted to load invalid Postcss plugin: "${pluginName}"`);
}

extraPostcssPlugins.push(plugin(pluginOptions));
}
if (tailwindPackagePath) {
extraPostcssPlugins.push(require(tailwindPackagePath)({ config: tailwindConfigPath }));
} else {
// Attempt to setup Tailwind CSS
// Only load Tailwind CSS plugin if configuration file was found.
// This acts as a guard to ensure the project actually wants to use Tailwind CSS.
// The package may be unknowningly present due to a third-party transitive package dependency.
const tailwindConfigPath = findTailwindConfiguration(searchDirectories);
if (tailwindConfigPath) {
let tailwindPackagePath;
try {
tailwindPackagePath = require.resolve('tailwindcss', { paths: [root] });
} catch {
const relativeTailwindConfigPath = path.relative(root, tailwindConfigPath);
logger.warn(
`Tailwind CSS configuration file found (${relativeTailwindConfigPath})` +
` but the 'tailwindcss' package is not installed.` +
` To enable Tailwind CSS, please install the 'tailwindcss' package.`,
);
}
if (tailwindPackagePath) {
extraPostcssPlugins.push(require(tailwindPackagePath)({ config: tailwindConfigPath }));
}
}
}

Expand Down
40 changes: 0 additions & 40 deletions packages/angular_devkit/build_angular/src/utils/tailwind.ts

This file was deleted.

Loading