+
+
+ `,
+ 'project-a/app/index.js': js`
+ const className = "content-['project-a/app/index.js']"
+ export default { className }
+ `,
+ 'project-a/src/index.css': css`
+ @import 'tailwindcss' source('../app');
+ @source '../../project-b/src/**/*.html';
+ `,
+ 'project-b/src/index.html': html`
+
+ `,
+ 'project-b/src/index.js': js`
+ const className = "content-['project-b/src/index.js']"
+ module.exports = { className }
+ `,
+ },
+ },
+ async ({ root, fs, exec }) => {
+ await exec('pnpm vite build', { cwd: path.join(root, 'project-a') })
+
+ let files = await fs.glob('project-a/dist/**/*.css')
+ expect(files).toHaveLength(1)
+ let [filename] = files[0]
+
+ // `underline` and `m-2` are present in files in the module graph but
+ // we've filtered the module graph such that we only look in
+ // `./app/**/*` so they should not be present
+ await fs.expectFileNotToContain(filename, [
+ //
+ candidate`underline`,
+ candidate`m-2`,
+ candidate`content-['project-a/index.html']`,
+ ])
+
+ // We've filtered the module graph to only look in ./app/**/* so the
+ // candidates from that project should be present
+ await fs.expectFileToContain(filename, [
+ //
+ candidate`content-['project-a/app/index.js']`,
+ ])
+
+ // Even through we're filtering the module graph explicit sources are
+ // additive and as such files from `project-b` should be included
+ // because there is an explicit `@source` directive for it
+ await fs.expectFileToContain(filename, [
+ //
+ candidate`content-['project-b/src/index.html']`,
+ ])
+
+ // The explicit source directive only covers HTML files, so the JS file
+ // should not be included
+ await fs.expectFileNotToContain(filename, [
+ //
+ candidate`content-['project-b/src/index.js']`,
+ ])
+ },
+ )
})
}
diff --git a/packages/@tailwindcss-vite/src/index.ts b/packages/@tailwindcss-vite/src/index.ts
index e0874d58f..b1fffb9d1 100644
--- a/packages/@tailwindcss-vite/src/index.ts
+++ b/packages/@tailwindcss-vite/src/index.ts
@@ -439,9 +439,24 @@ class Root {
if (!this.compiler) return new Set()
if (this.compiler.root === 'none') return new Set()
+ let root = this.compiler.root
+ let basePath = root ? path.resolve(root.base, root.pattern) : null
+
+ function moduleIdIsAllowed(id: string) {
+ if (basePath === null) return true
+
+ // This a virtual module that's not on the file system
+ // TODO: What should we do here?
+ if (!id.startsWith('/')) return true
+
+ return id.startsWith(basePath)
+ }
+
let shared = new Set()
for (let [id, candidates] of this.getSharedCandidates()) {
+ if (!moduleIdIsAllowed(id)) continue
+
for (let candidate of candidates) {
shared.add(candidate)
}