Skip to content

fix(@angular-devkit/build-angular): update list of known tailwind configuration files #25022

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
Apr 18, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import path from 'node:path';
import { normalizeAssetPatterns, normalizeOptimization, normalizeSourceMaps } from '../../utils';
import { normalizeCacheOptions } from '../../utils/normalize-cache';
import { generateEntryPoints } from '../../utils/package-chunk-sort';
import { findTailwindConfigurationFile } from '../../utils/tailwind';
import { getIndexInputFile, getIndexOutputFile } from '../../utils/webpack-browser-config';
import { globalScriptsByBundleName, normalizeGlobalStyles } from '../../webpack/utils/helpers';
import { Schema as BrowserBuilderOptions, OutputHashing } from './schema';
Expand Down Expand Up @@ -97,7 +98,7 @@ export async function normalizeOptions(
}

let tailwindConfiguration: { file: string; package: string } | undefined;
const tailwindConfigurationPath = findTailwindConfigurationFile(workspaceRoot, projectRoot);
const tailwindConfigurationPath = await findTailwindConfigurationFile(workspaceRoot, projectRoot);
if (tailwindConfigurationPath) {
// Create a node resolver at the project root as a directory
const resolver = createRequire(projectRoot + '/');
Expand Down Expand Up @@ -203,27 +204,6 @@ export async function normalizeOptions(
};
}

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;
}

/**
* Normalize a directory path string.
* Currently only removes a trailing slash if present.
Expand Down
40 changes: 40 additions & 0 deletions packages/angular_devkit/build_angular/src/utils/tailwind.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* @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.io/license
*/

import { readdir } from 'node:fs/promises';
import { join } from 'node:path';

const tailwindConfigFiles: string[] = [
'tailwind.config.js',
'tailwind.config.cjs',
'tailwind.config.mjs',
'tailwind.config.ts',
];

export async function findTailwindConfigurationFile(
workspaceRoot: string,
projectRoot: string,
): Promise<string | undefined> {
const dirEntries = [projectRoot, workspaceRoot].map((root) =>
readdir(root, { withFileTypes: false }).then((entries) => ({
root,
files: new Set(entries),
})),
);

// A configuration file can exist in the project or workspace root
for await (const { root, files } of dirEntries) {
for (const potentialConfig of tailwindConfigFiles) {
if (files.has(potentialConfig)) {
return join(root, potentialConfig);
}
}
}

return undefined;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
*/

import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import * as fs from 'node:fs';
import * as path from 'node:path';
import { pathToFileURL } from 'node:url';
import type { FileImporter } from 'sass';
Expand All @@ -19,6 +18,7 @@ import {
import { SassLegacyWorkerImplementation } from '../../sass/sass-service-legacy';
import { WebpackConfigOptions } from '../../utils/build-options';
import { useLegacySass } from '../../utils/environment-options';
import { findTailwindConfigurationFile } from '../../utils/tailwind';
import {
AnyComponentStyleBudgetChecker,
PostcssCliResources,
Expand All @@ -34,8 +34,8 @@ import {
} from '../utils/helpers';

// eslint-disable-next-line max-lines-per-function
export function getStylesConfig(wco: WebpackConfigOptions): Configuration {
const { root, buildOptions, logger } = wco;
export async function getStylesConfig(wco: WebpackConfigOptions): Promise<Configuration> {
const { root, buildOptions, logger, projectRoot } = wco;
const extraPlugins: Configuration['plugins'] = [];

extraPlugins.push(new AnyComponentStyleBudgetChecker(buildOptions.budgets));
Expand Down Expand Up @@ -86,13 +86,13 @@ export function getStylesConfig(wco: WebpackConfigOptions): Configuration {
// 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 = getTailwindConfigPath(wco);
const tailwindConfigPath = await findTailwindConfigurationFile(root, projectRoot);
if (tailwindConfigPath) {
let tailwindPackagePath;
try {
tailwindPackagePath = require.resolve('tailwindcss', { paths: [wco.root] });
tailwindPackagePath = require.resolve('tailwindcss', { paths: [root] });
} catch {
const relativeTailwindConfigPath = path.relative(wco.root, tailwindConfigPath);
const relativeTailwindConfigPath = path.relative(root, tailwindConfigPath);
logger.warn(
`Tailwind CSS configuration file found (${relativeTailwindConfigPath})` +
` but the 'tailwindcss' package is not installed.` +
Expand Down Expand Up @@ -315,24 +315,6 @@ export function getStylesConfig(wco: WebpackConfigOptions): Configuration {
};
}

function getTailwindConfigPath({ projectRoot, root }: WebpackConfigOptions): 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, root]) {
for (const configFile of tailwindConfigFiles) {
// Irrespective of the name project level configuration should always take precedence.
const fullPath = path.join(basePath, configFile);
if (fs.existsSync(fullPath)) {
return fullPath;
}
}
}

return undefined;
}

function getSassLoaderOptions(
root: string,
implementation: SassWorkerImplementation | SassLegacyWorkerImplementation,
Expand Down Expand Up @@ -399,7 +381,7 @@ function getSassResolutionImporter(
root: string,
preserveSymlinks: boolean,
): FileImporter<'async'> {
const commonResolverOptions: Parameters<typeof loaderContext['getResolve']>[0] = {
const commonResolverOptions: Parameters<(typeof loaderContext)['getResolve']>[0] = {
conditionNames: ['sass', 'style'],
mainFields: ['sass', 'style', 'main', '...'],
extensions: ['.scss', '.sass', '.css'],
Expand Down