diff --git a/integrations/utils.ts b/integrations/utils.ts index 70b97c111d06..277b3bda4b29 100644 --- a/integrations/utils.ts +++ b/integrations/utils.ts @@ -214,8 +214,9 @@ export function test( }) options.onTestFailed(() => { - // In debug mode, messages are logged to the console immediately - if (debug) return + // In only or debug mode, messages are logged to the console + // immediately. + if (only || debug) return for (let [type, message] of combined) { if (type === 'stdout') { @@ -293,7 +294,7 @@ export function test( let dir = path.dirname(full) await fs.mkdir(dir, { recursive: true }) - await fs.writeFile(full, content) + await fs.writeFile(full, content, 'utf-8') }, async create(filenames: string[]): Promise { diff --git a/packages/@tailwindcss-cli/src/commands/build/index.ts b/packages/@tailwindcss-cli/src/commands/build/index.ts index b61f3dd49b86..3a5fd120b81a 100644 --- a/packages/@tailwindcss-cli/src/commands/build/index.ts +++ b/packages/@tailwindcss-cli/src/commands/build/index.ts @@ -203,9 +203,6 @@ export async function handle(args: Result>) { // Scan the entire `base` directory for full rebuilds. if (rebuildStrategy === 'full') { - // Clear all watchers - cleanupWatchers() - // Read the new `input`. let input = args['--input'] ? args['--input'] === '-' @@ -226,7 +223,12 @@ export async function handle(args: Result>) { env.DEBUG && console.timeEnd('[@tailwindcss/cli] Scan for candidates') // Setup new watchers - cleanupWatchers = await createWatchers(watchDirectories(scanner), handle) + let newCleanupWatchers = await createWatchers(watchDirectories(scanner), handle) + + // Clear old watchers + await cleanupWatchers() + + cleanupWatchers = newCleanupWatchers // Re-compile the CSS env.DEBUG && console.time('[@tailwindcss/cli] Build CSS') @@ -271,8 +273,10 @@ export async function handle(args: Result>) { // disable this behavior with `--watch=always`. if (args['--watch'] !== 'always') { process.stdin.on('end', () => { - cleanupWatchers() - process.exit(0) + cleanupWatchers().then( + () => process.exit(0), + () => process.exit(1), + ) }) } @@ -307,6 +311,29 @@ function watchDirectories(scanner: Scanner) { } async function createWatchers(dirs: string[], cb: (files: string[]) => void) { + // Remove any directories that are children of an already watched directory. + // If we don't we may not get notified of certain filesystem events regardless + // of whether or not they are for the directory that is duplicated. + + // 1. Sort in asc by length + dirs = dirs.sort((a, z) => a.length - z.length) + + // 2. Remove any directories that are children of another directory + let toRemove = [] + + // /project-a 0 + // /project-a/src 1 + + for (let i = 0; i < dirs.length; ++i) { + for (let j = 0; j < i; ++j) { + if (!dirs[i].startsWith(`${dirs[j]}/`)) continue + + toRemove.push(dirs[i]) + } + } + + dirs = dirs.filter((dir) => !toRemove.includes(dir)) + // Track all Parcel watchers for each glob. // // When we encounter a change in a CSS file, we need to setup new watchers and @@ -322,9 +349,9 @@ async function createWatchers(dirs: string[], cb: (files: string[]) => void) { // A changed file can be watched by multiple watchers, but we only want to // handle the file once. We debounce the handle function with the collected // files to handle them in a single batch and to avoid multiple rebuilds. - function enqueueCallback() { - // Dispose all existing macrotask. - debounceQueue.dispose() + async function enqueueCallback() { + // Dispose all existing macrotasks. + await debounceQueue.dispose() // Setup a new macrotask to handle the files in batch. debounceQueue.queueMacrotask(() => { @@ -365,7 +392,7 @@ async function createWatchers(dirs: string[], cb: (files: string[]) => void) { ) // Handle the tracked files at some point in the future. - enqueueCallback() + await enqueueCallback() }) // Ensure we cleanup the watcher when we're done. @@ -373,9 +400,9 @@ async function createWatchers(dirs: string[], cb: (files: string[]) => void) { } // Cleanup - return () => { - watchers.dispose() - debounceQueue.dispose() + return async () => { + await watchers.dispose() + await debounceQueue.dispose() } } diff --git a/packages/@tailwindcss-cli/src/utils/disposables.ts b/packages/@tailwindcss-cli/src/utils/disposables.ts index cb0d982c1a78..737f3ed1e0a1 100644 --- a/packages/@tailwindcss-cli/src/utils/disposables.ts +++ b/packages/@tailwindcss-cli/src/utils/disposables.ts @@ -35,9 +35,9 @@ export class Disposables { /** * Dispose all disposables at once. */ - dispose() { + async dispose() { for (let dispose of this.#disposables) { - dispose() + await dispose() } this.#disposables.clear()