Skip to content

Commit

Permalink
feat(@angular-devkit/build-angular): initial tailwindcss support for …
Browse files Browse the repository at this point in the history
…CSS in esbuild builder

When using the experimental esbuild-based browser application builder, CSS stylesheets will
now be processed by tailwindcss if a tailwind configuration file is present and the `tailwindcss`
package has been installed in the project. This provides equivalent behavior to the use of tailwind
with the current default Webpack-based build system. Currently, only CSS stylesheets are processed.
Preprocessor support including Sass and Less will be added in a future change.
  • Loading branch information
clydin authored and angular-robot[bot] committed Mar 22, 2023
1 parent 3b815e1 commit 52969db
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export interface CssPluginOptions {
* of the esbuild formatted target.
*/
browsers: string[];

tailwindConfiguration?: { file: string; package: string };
}

/**
Expand All @@ -61,12 +63,19 @@ export function createCssPlugin(options: CssPluginOptions): Plugin {
const autoprefixerInfo = autoprefixer.info({ from: build.initialOptions.absWorkingDir });
const skipAutoprefixer = autoprefixerInfo.includes('Awesome!');

if (skipAutoprefixer) {
if (skipAutoprefixer && !options.tailwindConfiguration) {
return;
}

postcss ??= (await import('postcss')).default;
const postcssProcessor = postcss([autoprefixer]);
const postcssProcessor = postcss();
if (options.tailwindConfiguration) {
const tailwind = await import(options.tailwindConfiguration.package);
postcssProcessor.use(tailwind({ config: options.tailwindConfiguration.file }));
}
if (!skipAutoprefixer) {
postcssProcessor.use(autoprefixer);
}

// Add a load callback to support inline Component styles
build.onLoad({ filter: /^css;/, namespace: 'angular:styles/component' }, async (args) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ function createCodeBundleOptions(
advancedOptimizations,
inlineStyleLanguage,
jit,
tailwindConfiguration,
} = options;

return {
Expand Down Expand Up @@ -386,6 +387,7 @@ function createCodeBundleOptions(
target,
inlineStyleLanguage,
browsers,
tailwindConfiguration,
},
),
],
Expand Down Expand Up @@ -464,6 +466,7 @@ function createGlobalStylesBundleOptions(
preserveSymlinks,
externalDependencies,
stylePreprocessorOptions,
tailwindConfiguration,
} = options;

const buildOptions = createStylesheetBundleOptions({
Expand All @@ -476,6 +479,7 @@ function createGlobalStylesBundleOptions(
outputNames,
includePaths: stylePreprocessorOptions?.includePaths,
browsers,
tailwindConfiguration,
});
buildOptions.legalComments = options.extractLicenses ? 'none' : 'eof';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
*/

import { BuilderContext } from '@angular-devkit/architect';
import * as path from 'path';
import fs from 'node:fs';
import { createRequire } from 'node:module';
import path from 'node:path';
import { normalizeAssetPatterns, normalizeOptimization, normalizeSourceMaps } from '../../utils';
import { normalizeCacheOptions } from '../../utils/normalize-cache';
import { normalizePolyfills } from '../../utils/normalize-polyfills';
Expand Down Expand Up @@ -100,6 +102,25 @@ export async function normalizeOptions(
}
}

let tailwindConfiguration: { file: string; package: string } | undefined;
const tailwindConfigurationPath = findTailwindConfigurationFile(workspaceRoot, projectRoot);
if (tailwindConfigurationPath) {
const resolver = createRequire(projectRoot);
try {
tailwindConfiguration = {
file: tailwindConfigurationPath,
package: resolver.resolve('tailwindcss'),
};
} catch {
const relativeTailwindConfigPath = path.relative(workspaceRoot, tailwindConfigurationPath);
context.logger.warn(
`Tailwind CSS configuration file found (${relativeTailwindConfigPath})` +
` but the 'tailwindcss' package is not installed.` +
` To enable Tailwind CSS, please install the 'tailwindcss' package.`,
);
}
}

let serviceWorkerOptions;
if (options.serviceWorker) {
// If ngswConfigPath is not specified, the default is 'ngsw-config.json' within the project root
Expand Down Expand Up @@ -181,5 +202,27 @@ export async function normalizeOptions(
globalStyles,
serviceWorkerOptions,
indexHtmlOptions,
tailwindConfiguration,
};
}

function findTailwindConfigurationFile(
workspaceRoot: string,
projectRoot: string,
): string | undefined {
// A configuration file can exist in the project or workspace root
// The list of valid config files can be found:
// https://github.com/tailwindlabs/tailwindcss/blob/8845d112fb62d79815b50b3bae80c317450b8b92/src/util/resolveConfigPath.js#L46-L52
const tailwindConfigFiles = ['tailwind.config.js', 'tailwind.config.cjs'];
for (const basePath of [projectRoot, workspaceRoot]) {
for (const configFile of tailwindConfigFiles) {
// Project level configuration should always take precedence.
const fullPath = path.join(basePath, configFile);
if (fs.existsSync(fullPath)) {
return fullPath;
}
}
}

return undefined;
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface BundleStylesheetOptions {
externalDependencies?: string[];
target: string[];
browsers: string[];
tailwindConfiguration?: { file: string; package: string };
}

export function createStylesheetBundleOptions(
Expand Down Expand Up @@ -72,6 +73,7 @@ export function createStylesheetBundleOptions(
sourcemap: !!options.sourcemap,
inlineComponentData,
browsers: options.browsers,
tailwindConfiguration: options.tailwindConfiguration,
}),
createCssResourcePlugin(),
],
Expand Down
10 changes: 9 additions & 1 deletion tests/legacy-cli/e2e.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,15 @@ TEST_TAGS = ["no-remote-exec", "requires-network"]
# Subset of tests for yarn/esbuild
BROWSER_TESTS = ["tests/misc/browsers.js"]
YARN_TESTS = ["tests/basic/**", "tests/update/**", "tests/commands/add/**"]
ESBUILD_TESTS = ["tests/basic/**", "tests/build/prod-build.js", "tests/build/relative-sourcemap.js", "tests/build/styles/scss.js", "tests/build/styles/include-paths.js", "tests/commands/add/add-pwa.js"]
ESBUILD_TESTS = [
"tests/basic/**",
"tests/build/prod-build.js",
"tests/build/relative-sourcemap.js",
"tests/build/styles/scss.js",
"tests/build/styles/include-paths.js",
"tests/build/styles/tailwind*.js",
"tests/commands/add/add-pwa.js",
]

# Tests excluded for esbuild
ESBUILD_IGNORE_TESTS = [
Expand Down

0 comments on commit 52969db

Please sign in to comment.