Skip to content
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
7 changes: 4 additions & 3 deletions integrations/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down Expand Up @@ -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<void> {
Expand Down
53 changes: 40 additions & 13 deletions packages/@tailwindcss-cli/src/commands/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,6 @@ export async function handle(args: Result<ReturnType<typeof options>>) {

// 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'] === '-'
Expand All @@ -226,7 +223,12 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
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')
Expand Down Expand Up @@ -271,8 +273,10 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
// 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),
)
})
}

Expand Down Expand Up @@ -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
Expand All @@ -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(() => {
Expand Down Expand Up @@ -365,17 +392,17 @@ 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.
watchers.add(unsubscribe)
}

// Cleanup
return () => {
watchers.dispose()
debounceQueue.dispose()
return async () => {
await watchers.dispose()
await debounceQueue.dispose()
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/@tailwindcss-cli/src/utils/disposables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down