Preserve explicit sources in Vite output

This commit is contained in:
OllieinCanada 2026-09-11 10:57:31 -04:00
parent 3bfba3bb8a
commit c9a445ee82
2 changed files with 51 additions and 31 deletions

View file

@ -1,5 +1,5 @@
import path from 'node:path'
import { candidate, css, js, json, test } from '../utils'
import { candidate, css, html, js, json, test } from '../utils'
test(
'build watch ignores a custom output directory',
@ -47,8 +47,12 @@ test(
document.body.innerHTML = '<div class="underline">Hello</div>'
`,
'resources/css/app.css': css` @import 'tailwindcss'; `,
'resources/css/app.css': css`
@import 'tailwindcss';
@source '../../assets/source.html';
`,
'assets/app.js': js` console.log('previous build') `,
'assets/source.html': html` <div class="font-black"></div> `,
'assets/styles.css': css`
.previous-build {
display: block;
@ -64,7 +68,7 @@ test(
await process.onStdout((message) => message.includes('built in'))
let output = path.join(root, 'assets/styles.css')
await fs.expectFileToContain(output, candidate`underline`)
await fs.expectFileToContain(output, [candidate`font-black`, candidate`underline`])
process.flush()
let result = await Promise.race([

View file

@ -339,6 +339,10 @@ class Root {
// The lazily-initialized Tailwind scanner.
private scanner?: Scanner
// A scanner for explicit `@source` paths that can opt back into paths which
// are excluded from automatic source detection.
private explicitScanner?: Scanner
// List of all candidates that were being returned by the root scanner during
// the lifetime of the root.
private candidates: Set<string> = new Set<string>()
@ -433,7 +437,7 @@ class Root {
return [{ ...this.compiler.root, negated: false }]
})().concat(this.compiler.sources)
if (this.buildOutputDirectory !== null) {
if (this.buildOutputDirectory !== null && this.compiler.root === null) {
// Watching files emitted by Vite would trigger another build every time
// the output is written.
sources.push({
@ -444,6 +448,10 @@ class Root {
}
this.scanner = new Scanner({ sources })
this.explicitScanner =
this.compiler.sources.length > 0
? new Scanner({ sources: this.compiler.sources })
: undefined
DEBUG && I.end('Setup scanner')
} else {
for (let buildDependency of this.buildDependencies.keys()) {
@ -469,47 +477,55 @@ class Root {
// setup a new scanner and compiler every time we request the CSS file
// (regardless whether it actually changed or not).
DEBUG && I.start('Scan for candidates')
for (let candidate of this.scanner.scan()) {
this.candidates.add(candidate)
for (let scanner of [this.scanner, this.explicitScanner]) {
if (!scanner) continue
for (let candidate of scanner.scan()) {
this.candidates.add(candidate)
}
}
DEBUG && I.end('Scan for candidates')
}
if (this.compiler.features & Features.Utilities) {
DEBUG && I.start('Register dependency messages')
// Watch individual files found via custom `@source` paths
for (let file of this.scanner.files) {
addWatchFile(file)
}
for (let scanner of [this.scanner, this.explicitScanner]) {
if (!scanner) continue
// Watch globs found via custom `@source` paths
for (let glob of this.scanner.globs) {
if (glob.pattern[0] === '!') continue
let relative = path.relative(this.base, glob.base)
if (relative[0] !== '.') {
relative = './' + relative
// Watch individual files found via custom `@source` paths
for (let file of scanner.files) {
addWatchFile(file)
}
// Ensure relative is a posix style path since we will merge it with the
// glob.
relative = normalizePath(relative)
addWatchFile(path.posix.join(relative, glob.pattern))
// Watch globs found via custom `@source` paths
for (let glob of scanner.globs) {
if (glob.pattern[0] === '!') continue
let root = this.compiler.root
let relative = path.relative(this.base, glob.base)
if (relative[0] !== '.') {
relative = './' + relative
}
// Ensure relative is a posix style path since we will merge it with the
// glob.
relative = normalizePath(relative)
if (root !== 'none' && root !== null) {
let basePath = normalizePath(path.resolve(root.base, root.pattern))
addWatchFile(path.posix.join(relative, glob.pattern))
let isDir = await fs.stat(basePath).then(
(stats) => stats.isDirectory(),
() => false,
)
let root = this.compiler.root
if (!isDir) {
throw new Error(
`The path given to \`source(…)\` must be a directory but got \`source(${basePath})\` instead.`,
if (root !== 'none' && root !== null) {
let basePath = normalizePath(path.resolve(root.base, root.pattern))
let isDir = await fs.stat(basePath).then(
(stats) => stats.isDirectory(),
() => false,
)
if (!isDir) {
throw new Error(
`The path given to \`source(…)\` must be a directory but got \`source(${basePath})\` instead.`,
)
}
}
}
}