From 23147ffacc342773b8324a6ced27dda80ffa1a4e Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Wed, 23 Oct 2024 13:27:57 +0200 Subject: [PATCH 01/28] =?UTF-8?q?handle=20`source(=E2=80=A6)`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Forward it from `@media source(…)` to `@tailwind utilities source(…)`. Next, handle `@tailwind utilities source(…)` and retrieve the path. --- packages/tailwindcss/src/at-import.ts | 8 ++- packages/tailwindcss/src/index.ts | 94 ++++++++++++++++++++------- 2 files changed, 77 insertions(+), 25 deletions(-) diff --git a/packages/tailwindcss/src/at-import.ts b/packages/tailwindcss/src/at-import.ts index 29c0ce2c5b8f..9b26c89f4f18 100644 --- a/packages/tailwindcss/src/at-import.ts +++ b/packages/tailwindcss/src/at-import.ts @@ -44,8 +44,12 @@ export async function substituteAtImports( let ast = CSS.parse(loaded.content) await substituteAtImports(ast, loaded.base, loadStylesheet, recurseCount + 1) - contextNode.nodes = buildImportNodes(ast, layer, media, supports) - contextNode.context.base = loaded.base + contextNode.nodes = buildImportNodes( + [context({ base: loaded.base }, ast)], + layer, + media, + supports, + ) })(), ) diff --git a/packages/tailwindcss/src/index.ts b/packages/tailwindcss/src/index.ts index 743e713f23f5..171b4e5a7648 100644 --- a/packages/tailwindcss/src/index.ts +++ b/packages/tailwindcss/src/index.ts @@ -3,7 +3,7 @@ import { substituteAtApply } from './apply' import { atRoot, comment, - context, + context as contextNode, decl, rule, toCss, @@ -73,22 +73,64 @@ async function parseCss( loadStylesheet = throwOnLoadStylesheet, }: CompileOptions = {}, ) { - let ast = [context({ base }, CSS.parse(css))] as AstNode[] + let ast = [contextNode({ base }, CSS.parse(css))] as AstNode[] await substituteAtImports(ast, base, loadStylesheet) - let important: boolean | null = null + let important = null as boolean | null let theme = new Theme() let customVariants: ((designSystem: DesignSystem) => void)[] = [] let customUtilities: ((designSystem: DesignSystem) => void)[] = [] - let firstThemeRule: Rule | null = null + let firstThemeRule = null as Rule | null + let utilitiesNode = null as Rule | null let globs: { base: string; pattern: string }[] = [] + let root: + | null // Unknown root + | 'none' // Explicitly no root specified via `source(none)` + // Specified via `source(…)`, relative to the `base` + | { base: string; pattern: string } = null // Handle at-rules walk(ast, (node, { parent, replaceWith, context }) => { if (node.kind !== 'rule') return if (node.selector[0] !== '@') return + // Find `@tailwind utilities` so that we can later replace it with the + // actual generated utility class CSS. + if ( + utilitiesNode === null && + (node.selector === '@tailwind utilities' || node.selector.startsWith('@tailwind utilities ')) + ) { + let params = segment(node.selector.slice(20).trim(), ' ') + for (let param of params) { + if (param.startsWith('source(')) { + let path = param.slice(7, -1) + + // Keyword: `source(none)` + if (path === 'none') { + root = path + continue + } + + // Explicit path: `source('…')` + if ( + (path[0] === '"' && path[path.length - 1] !== '"') || + (path[0] === "'" && path[path.length - 1] !== "'") || + (path[0] !== "'" && path[0] !== '"') + ) { + throw new Error('`source(…)` paths must be quoted.') + } + + root = { + base: context.sourceBase ?? context.base, + pattern: path.slice(1, -1), + } + } + } + + utilitiesNode = node + } + // Collect custom `@utility` at-rules if (node.selector.startsWith('@utility ')) { if (parent !== null) { @@ -232,12 +274,26 @@ async function parseCss( let unknownParams: string[] = [] for (let param of params) { + // Handle `@media source(…)` + if (param.startsWith('source(')) { + let path = param.slice(7, -1) + + walk(node.nodes, (child, { replaceWith }) => { + if (child.kind !== 'rule') return + if (child.selector === '@tailwind utilities') { + child.selector += ` source(${path})` + replaceWith([contextNode({ sourceBase: context.base }, [child])]) + return WalkAction.Stop + } + }) + } + // Handle `@media theme(…)` // // We support `@import "tailwindcss/theme" theme(reference)` as a way to // import an external theme file as a reference, which becomes `@media // theme(reference) { … }` when the `@import` is processed. - if (param.startsWith('theme(')) { + else if (param.startsWith('theme(')) { let themeParams = param.slice(6, -1) walk(node.nodes, (child) => { @@ -419,6 +475,8 @@ async function parseCss( designSystem, ast, globs, + root, + utilitiesNode, } } @@ -427,24 +485,13 @@ export async function compile( opts: CompileOptions = {}, ): Promise<{ globs: { base: string; pattern: string }[] + root: + | null // Unknown root + | 'none' // Explicitly no root specified via `source(none)` + | { base: string; pattern: string } // Specified via `source(…)`, relative to the `base` build(candidates: string[]): string }> { - let { designSystem, ast, globs } = await parseCss(css, opts) - - let tailwindUtilitiesNode: Rule | null = null - - // Find `@tailwind utilities` so that we can later replace it with the actual - // generated utility class CSS. - walk(ast, (node) => { - if (node.kind === 'rule' && node.selector === '@tailwind utilities') { - tailwindUtilitiesNode = node - - // Stop walking after finding `@tailwind utilities` to avoid walking all - // of the generated CSS. This means `@tailwind utilities` can only appear - // once per file but that's the intended usage at this point in time. - return WalkAction.Stop - } - }) + let { designSystem, ast, globs, root, utilitiesNode } = await parseCss(css, opts) if (process.env.NODE_ENV !== 'test') { ast.unshift(comment(`! tailwindcss v${version} | MIT License | https://tailwindcss.com `)) @@ -464,6 +511,7 @@ export async function compile( return { globs, + root, build(newRawCandidates: string[]) { let didChange = false @@ -482,7 +530,7 @@ export async function compile( return compiledCss } - if (tailwindUtilitiesNode) { + if (utilitiesNode) { let newNodes = compileCandidates(allValidCandidates, designSystem, { onInvalidCandidate, }).astNodes @@ -496,7 +544,7 @@ export async function compile( previousAstNodeCount = newNodes.length - tailwindUtilitiesNode.nodes = newNodes + utilitiesNode.nodes = newNodes compiledCss = toCss(ast) } From a62b010c74538dcfab7b26780f154ac29e267ab7 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Wed, 23 Oct 2024 17:38:11 +0200 Subject: [PATCH 02/28] =?UTF-8?q?add=20integration=20test=20for=20`source(?= =?UTF-8?q?=E2=80=A6)`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This integration test itself might change over time as we work through this PR. --- integrations/cli/index.test.ts | 109 ++++++++++++++++++++++++++++++++- integrations/utils.ts | 6 +- 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/integrations/cli/index.test.ts b/integrations/cli/index.test.ts index f392f754486a..f17ca0e6a53b 100644 --- a/integrations/cli/index.test.ts +++ b/integrations/cli/index.test.ts @@ -1,6 +1,6 @@ import os from 'node:os' import path from 'node:path' -import { describe } from 'vitest' +import { describe, expect } from 'vitest' import { candidate, css, html, js, json, test, yaml } from '../utils' const STANDALONE_BINARY = (() => { @@ -255,3 +255,110 @@ describe.each([ }, ) }) + +describe.only('@source', () => { + test( + 'it works', + { + fs: { + 'package.json': json`{}`, + 'pnpm-workspace.yaml': yaml` + # + packages: + - project-a + `, + 'project-a/package.json': json` + { + "dependencies": { + "tailwindcss": "workspace:^", + "@tailwindcss/cli": "workspace:^" + } + } + `, + 'project-a/src/index.css': css` + @import 'tailwindcss/theme' theme(reference); + + /* Run auto-content detection in ../../project-b */ + @import 'tailwindcss/utilities' source('../../project-b'); + + /* Additive: */ + /* {my-lib-1,my-lib-2}: expand */ + /* *.html: only look for .html */ + @source '../node_modules/{my-lib-1,my-lib-2}/src/**/*.html'; + @source './logo.{jpg,png}'; /* Don't worry about it */ + `, + 'project-a/src/index.html': html` +
+ `, + 'project-a/src/logo.jpg': html` +
+ `, + 'project-a/node_modules/my-lib-1/src/index.html': html` +
+ `, + 'project-a/node_modules/my-lib-2/src/index.html': html` +
+ `, + 'project-b/src/index.html': html` +
+ `, + 'project-b/node_modules/my-lib-3/src/index.html': html` +
+ `, + }, + }, + async ({ fs, exec, root }) => { + await exec('pnpm tailwindcss --input src/index.css --output dist/out.css', { + cwd: path.join(root, 'project-a'), + }) + + expect(await fs.dumpFiles('./project-a/dist/*.css')).toMatchInlineSnapshot(` + " + --- ./project-a/dist/out.css --- + @tailwind utilities source('../../project-b') { + .content-\\[\\'project-a\\/node_modules\\/my-lib-1\\/src\\/index\\.html\\'\\] { + --tw-content: 'project-a/node modules/my-lib-1/src/index.html'; + content: var(--tw-content); + } + .content-\\[\\'project-a\\/node_modules\\/my-lib-2\\/src\\/index\\.html\\'\\] { + --tw-content: 'project-a/node modules/my-lib-2/src/index.html'; + content: var(--tw-content); + } + .content-\\[\\'project-a\\/src\\/logo\\.jpg\\'\\] { + --tw-content: 'project-a/src/logo.jpg'; + content: var(--tw-content); + } + .content-\\[\\'project-b\\/src\\/index\\.html\\'\\] { + --tw-content: 'project-b/src/index.html'; + content: var(--tw-content); + } + } + @supports (-moz-orient: inline) { + @layer base { + *, ::before, ::after, ::backdrop { + --tw-content: ""; + } + } + } + @property --tw-content { + syntax: "*"; + inherits: false; + initial-value: ""; + } + " + `) + }, + ) +}) diff --git a/integrations/utils.ts b/integrations/utils.ts index 5eed468a077e..9e11c5a7358a 100644 --- a/integrations/utils.ts +++ b/integrations/utils.ts @@ -303,7 +303,11 @@ export function test( return Promise.all( files.map(async (file) => { let content = await fs.readFile(path.join(root, file), 'utf8') - return [file, content] + return [ + file, + // Drop license comment + content.replace(/[\s\n]*\/\*! tailwindcss .*? \*\/[\s\n]*/g, ''), + ] }), ) }, From 7b45b77bb17256f70e3c81a48e7dc310fad7264f Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Wed, 23 Oct 2024 17:38:39 +0200 Subject: [PATCH 03/28] =?UTF-8?q?forward=20correct=20`source(=E2=80=A6)`?= =?UTF-8?q?=20to=20the=20oxide=20scanner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/commands/build/index.ts | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/packages/@tailwindcss-cli/src/commands/build/index.ts b/packages/@tailwindcss-cli/src/commands/build/index.ts index dc8ba2a90178..41650e07bd3f 100644 --- a/packages/@tailwindcss-cli/src/commands/build/index.ts +++ b/packages/@tailwindcss-cli/src/commands/build/index.ts @@ -137,16 +137,29 @@ export async function handle(args: Result>) { fullRebuildPaths.push(path) }, }) + + let detectSources = (() => { + // Disable auto source detection + if (compiler.root === 'none') { + return undefined + } + + // No root specified, use the base directory + if (compiler.root === null) { + return { base } + } + + // Use the specified root + return { base: path.resolve(compiler.root.base, compiler.root.pattern) } + })() + + let scanner = new Scanner({ detectSources, sources: compiler.globs }) env.DEBUG && console.timeEnd('[@tailwindcss/cli] Setup compiler') - return compiler + + return [compiler, scanner] as const } - // Compile the input - let compiler = await createCompiler(input) - let scanner = new Scanner({ - detectSources: { base }, - sources: compiler.globs, - }) + let [compiler, scanner] = await createCompiler(input) // Watch for changes if (args['--watch']) { @@ -205,13 +218,7 @@ export async function handle(args: Result>) { fullRebuildPaths = inputFilePath ? [inputFilePath] : [] // Create a new compiler, given the new `input` - compiler = await createCompiler(input) - - // Re-scan the directory to get the new `candidates` - scanner = new Scanner({ - detectSources: { base }, - sources: compiler.globs, - }) + ;[compiler, scanner] = await createCompiler(input) // Scan the directory for candidates env.DEBUG && console.time('[@tailwindcss/cli] Scan for candidates') From 165718abb4dd23f1c7c0e8fe2c441f6dbfe8d286 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 12:52:08 +0200 Subject: [PATCH 04/28] update test --- integrations/cli/index.test.ts | 60 +++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/integrations/cli/index.test.ts b/integrations/cli/index.test.ts index f17ca0e6a53b..5b27558d1fbb 100644 --- a/integrations/cli/index.test.ts +++ b/integrations/cli/index.test.ts @@ -285,18 +285,33 @@ describe.only('@source', () => { /* {my-lib-1,my-lib-2}: expand */ /* *.html: only look for .html */ @source '../node_modules/{my-lib-1,my-lib-2}/src/**/*.html'; - @source './logo.{jpg,png}'; /* Don't worry about it */ + + /* We typically ignore these extensions, but now include them explicitly */ + @source './logo.{jpg,png}'; + + /* Project C should apply auto source detection */ + @source '../../project-c'; `, + + // Project A is the current folder, but we explicitly configured + // `source(project-b)`, therefore project-a should not be included in + // the output. 'project-a/src/index.html': html`
`, + + // Project A explicitly includes an extension we usually ignore, + // therefore it should be included in the output. 'project-a/src/logo.jpg': html`
`, + + // Project A explicitly includes node_modules/{my-lib-1,my-lib-2}, + // therefore these files should be included in the output. 'project-a/node_modules/my-lib-1/src/index.html': html`
{ class="content-['project-a/node_modules/my-lib-2/src/index.html']" >
`, + + // Project B is the configured `source(…)`, therefore auto source + // detection should include known extensions and folders in the output. 'project-b/src/index.html': html`
`, + + // Project B is the configured `source(…)`, therefore auto source + // detection should apply and node_modules should not be included in the + // output. 'project-b/node_modules/my-lib-3/src/index.html': html`
`, + + // Project C should apply auto source detection, therefore known + // extensions and folders should be included in the output. + 'project-c/src/index.html': html` +
+ `, + + // Project C should apply auto source detection, therefore known ignored + // extensions should not be included in the output. + 'project-c/src/logo.jpg': html` +
+ `, + + // Project C should apply auto source detection, therefore node_modules + // should not be included in the output. + 'project-c/node_modules/my-lib-1/src/index.html': html` +
+ `, }, }, async ({ fs, exec, root }) => { - await exec('pnpm tailwindcss --input src/index.css --output dist/out.css', { - cwd: path.join(root, 'project-a'), - }) + console.log( + await exec('pnpm tailwindcss --input src/index.css --output dist/out.css', { + cwd: path.join(root, 'project-a'), + }), + ) expect(await fs.dumpFiles('./project-a/dist/*.css')).toMatchInlineSnapshot(` " @@ -344,6 +392,10 @@ describe.only('@source', () => { --tw-content: 'project-b/src/index.html'; content: var(--tw-content); } + .content-\\[\\'project-c\\/src\\/index\\.html\\'\\] { + --tw-content: 'project-c/src/index.html'; + content: var(--tw-content); + } } @supports (-moz-orient: inline) { @layer base { From d0731d169297cb3eebca2f72da65e642abf3cf53 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 12:52:21 +0200 Subject: [PATCH 05/28] fix turbo input paths We used to list `core` instead of `oxide`, this should make sure that building Oxide works if we make changes in `oxide/`. --- turbo.json | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/turbo.json b/turbo.json index 9dbd528909cb..a7f1dc2642b7 100644 --- a/turbo.json +++ b/turbo.json @@ -3,29 +3,41 @@ "ui": "tui", "tasks": { "@tailwindcss/oxide#build": { - "dependsOn": ["^build"], - "outputs": ["./index.d.ts", "./index.js", "./*.node"], + "dependsOn": [ + "^build" + ], + "outputs": [ + "./index.d.ts", + "./index.js", + "./*.node" + ], "inputs": [ "./src/**/*", "./build.rs", "./package.json", "./Cargo.toml", - "../core/src/**/*", - "../core/Cargo.toml", + "../oxide/src/**/*", + "../oxide/Cargo.toml", "../Cargo.toml", "../package.json" ] }, "@tailwindcss/oxide#dev": { - "dependsOn": ["^dev"], - "outputs": ["./index.d.ts", "./index.js", "./*.node"], + "dependsOn": [ + "^dev" + ], + "outputs": [ + "./index.d.ts", + "./index.js", + "./*.node" + ], "inputs": [ "./src/**/*", "./build.rs", "./package.json", "./Cargo.toml", - "../core/src/**/*", - "../core/Cargo.toml", + "../oxide/src/**/*", + "../oxide/Cargo.toml", "../Cargo.toml", "../package.json" ], @@ -33,8 +45,12 @@ "persistent": true }, "build": { - "dependsOn": ["^build"], - "outputs": ["dist/**"] + "dependsOn": [ + "^build" + ], + "outputs": [ + "dist/**" + ] }, "lint": {}, "dev": { @@ -44,5 +60,7 @@ }, // If rustup is installed outside of the default ~/.rustup directory, we need // to pass the path through to the individual rust tasks. - "globalPassThroughEnv": ["RUSTUP_HOME"] + "globalPassThroughEnv": [ + "RUSTUP_HOME" + ] } From 9b25bf88d51e8917a81600fd337737c250f0a6b3 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 12:53:05 +0200 Subject: [PATCH 06/28] fix some clippy warnings --- crates/node/src/lib.rs | 2 +- crates/node/src/utf16.rs | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/crates/node/src/lib.rs b/crates/node/src/lib.rs index 10d8f99d7008..0c8d049398a2 100644 --- a/crates/node/src/lib.rs +++ b/crates/node/src/lib.rs @@ -128,7 +128,7 @@ impl Scanner { input: ChangedContent, ) -> Vec { let content = input.content.unwrap_or_else(|| { - std::fs::read_to_string(&input.file.unwrap()).expect("Failed to read file") + std::fs::read_to_string(input.file.unwrap()).expect("Failed to read file") }); let input = ChangedContent { diff --git a/crates/node/src/utf16.rs b/crates/node/src/utf16.rs index 7cdebd9fd1b2..a7e7018cb28f 100644 --- a/crates/node/src/utf16.rs +++ b/crates/node/src/utf16.rs @@ -31,20 +31,18 @@ impl<'a> IndexConverter<'a> { // will only ever be incremented up to the length of the input string. // // This eliminates a "potential" panic that cannot actually happen - let slice = unsafe { - self.input.get_unchecked(self.curr_utf8..) - }; + let slice = unsafe { self.input.get_unchecked(self.curr_utf8..) }; for c in slice.chars() { if self.curr_utf8 >= pos { - break + break; } self.curr_utf8 += c.len_utf8(); self.curr_utf16 += c.len_utf16(); } - return self.curr_utf16 as i64; + self.curr_utf16 as i64 } } @@ -66,19 +64,16 @@ mod test { (4, 4), (5, 5), (6, 6), - // inside the 🔥 (7, 8), (8, 8), (9, 8), (10, 8), - // inside the 🥳 (11, 10), (12, 10), (13, 10), (14, 10), - // world! (15, 11), (16, 12), @@ -87,7 +82,6 @@ mod test { (19, 15), (20, 16), (21, 17), - // Past the end should return the last utf-16 character index (22, 17), (100, 17), From fcf0a9df9dd297416883494694d6675dafe44448 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 12:53:23 +0200 Subject: [PATCH 07/28] promote sources to auto source detection --- crates/oxide/src/lib.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/crates/oxide/src/lib.rs b/crates/oxide/src/lib.rs index 79c2bcb9e59a..9a11a3f8c79c 100644 --- a/crates/oxide/src/lib.rs +++ b/crates/oxide/src/lib.rs @@ -219,6 +219,34 @@ impl Scanner { self.files.extend(files); self.globs.extend(globs); } + + // Find all `@source` globs that point to a directory. If so, promote the source to auto + // source detection instead. + if let Some(sources) = &mut self.sources { + for source in sources { + // If a glob ends with `**/*`, then we just want to register the base path as a new + // base. + if source.pattern.ends_with("**/*") { + source.pattern = source.pattern.trim_end_matches("**/*").to_owned(); + } + + let path = PathBuf::from(&source.base).join(&source.pattern); + if let Some(folder_name) = path.file_name() { + // Contains a file extension, e.g.: `foo.html`, therefore we don't want to + // detect sources here. + if folder_name.to_str().unwrap().contains(".") { + continue; + } + + // Promote to auto source detection + let detect_sources = DetectSources::new(path.clone()); + + let (files, globs) = detect_sources.detect(); + self.files.extend(files); + self.globs.extend(globs); + } + } + } } #[tracing::instrument(skip_all)] From dea08ba4e2da83a25984557869889f101041751f Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 13:14:23 +0200 Subject: [PATCH 08/28] refactor, remove a level of nesting --- crates/oxide/src/lib.rs | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/crates/oxide/src/lib.rs b/crates/oxide/src/lib.rs index 9a11a3f8c79c..fbfb79de48e6 100644 --- a/crates/oxide/src/lib.rs +++ b/crates/oxide/src/lib.rs @@ -231,20 +231,22 @@ impl Scanner { } let path = PathBuf::from(&source.base).join(&source.pattern); - if let Some(folder_name) = path.file_name() { - // Contains a file extension, e.g.: `foo.html`, therefore we don't want to - // detect sources here. - if folder_name.to_str().unwrap().contains(".") { - continue; - } - - // Promote to auto source detection - let detect_sources = DetectSources::new(path.clone()); + let Some(folder_name) = path.file_name() else { + continue; + }; - let (files, globs) = detect_sources.detect(); - self.files.extend(files); - self.globs.extend(globs); + // Contains a file extension, e.g.: `foo.html`, therefore we don't want to + // detect sources here. + if folder_name.to_str().unwrap().contains(".") { + continue; } + + // Promote to auto source detection + let detect_sources = DetectSources::new(path.clone()); + + let (files, globs) = detect_sources.detect(); + self.files.extend(files); + self.globs.extend(globs); } } } From 77e8bf9182b76ee32fb7039cbdb60e06b3f28e42 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 16:19:39 +0200 Subject: [PATCH 09/28] merge `globs` and `detectSources` Now that we can promote `@source "../path/to/folder"` to enable auto source detection, it means that we can just merge the globs together and add the base folder as if it was `@source "../path/to/folder"` --- crates/node/src/lib.rs | 17 ---- crates/oxide/src/lib.rs | 81 ++++++++----------- .../src/commands/build/index.ts | 12 +-- packages/@tailwindcss-postcss/src/index.ts | 3 +- 4 files changed, 41 insertions(+), 72 deletions(-) diff --git a/crates/node/src/lib.rs b/crates/node/src/lib.rs index 0c8d049398a2..2eff57be308d 100644 --- a/crates/node/src/lib.rs +++ b/crates/node/src/lib.rs @@ -18,13 +18,6 @@ pub struct ChangedContent { pub extension: String, } -#[derive(Debug, Clone)] -#[napi(object)] -pub struct DetectSources { - /// Base path to start scanning from - pub base: String, -} - #[derive(Debug, Clone)] #[napi(object)] pub struct GlobEntry { @@ -62,20 +55,11 @@ impl From for GlobEntry { } } -impl From for tailwindcss_oxide::scanner::detect_sources::DetectSources { - fn from(detect_sources: DetectSources) -> Self { - Self::new(detect_sources.base.into()) - } -} - // --- #[derive(Debug, Clone)] #[napi(object)] pub struct ScannerOptions { - /// Automatically detect sources in the base path - pub detect_sources: Option, - /// Glob sources pub sources: Option>, } @@ -102,7 +86,6 @@ impl Scanner { pub fn new(opts: ScannerOptions) -> Self { Self { scanner: tailwindcss_oxide::Scanner::new( - opts.detect_sources.map(Into::into), opts .sources .map(|x| x.into_iter().map(Into::into).collect()), diff --git a/crates/oxide/src/lib.rs b/crates/oxide/src/lib.rs index fbfb79de48e6..b45767c0b171 100644 --- a/crates/oxide/src/lib.rs +++ b/crates/oxide/src/lib.rs @@ -62,9 +62,6 @@ pub struct GlobEntry { #[derive(Debug, Clone, Default)] pub struct Scanner { - /// Auto content configuration - detect_sources: Option, - /// Glob sources sources: Option>, @@ -86,9 +83,8 @@ pub struct Scanner { } impl Scanner { - pub fn new(detect_sources: Option, sources: Option>) -> Self { + pub fn new(sources: Option>) -> Self { Self { - detect_sources, sources, ..Default::default() } @@ -206,51 +202,11 @@ impl Scanner { return; } - self.detect_sources(); self.scan_sources(); self.ready = true; } - #[tracing::instrument(skip_all)] - fn detect_sources(&mut self) { - if let Some(detect_sources) = &self.detect_sources { - let (files, globs) = detect_sources.detect(); - self.files.extend(files); - self.globs.extend(globs); - } - - // Find all `@source` globs that point to a directory. If so, promote the source to auto - // source detection instead. - if let Some(sources) = &mut self.sources { - for source in sources { - // If a glob ends with `**/*`, then we just want to register the base path as a new - // base. - if source.pattern.ends_with("**/*") { - source.pattern = source.pattern.trim_end_matches("**/*").to_owned(); - } - - let path = PathBuf::from(&source.base).join(&source.pattern); - let Some(folder_name) = path.file_name() else { - continue; - }; - - // Contains a file extension, e.g.: `foo.html`, therefore we don't want to - // detect sources here. - if folder_name.to_str().unwrap().contains(".") { - continue; - } - - // Promote to auto source detection - let detect_sources = DetectSources::new(path.clone()); - - let (files, globs) = detect_sources.detect(); - self.files.extend(files); - self.globs.extend(globs); - } - } - } - #[tracing::instrument(skip_all)] fn scan_sources(&mut self) { let Some(sources) = &self.sources else { @@ -261,7 +217,38 @@ impl Scanner { return; } - let resolved_files: Vec<_> = match fast_glob(sources) { + // Partition sources into sources that should be promoted to auto source detection and + // sources that should be resolved as globs. + let (auto_sources, glob_sources): (Vec<_>, Vec<_>) = sources.iter().partition(|source| { + // If a glob ends with `/**/*`, then we just want to register the base path as a new + // base. Essentially converting it to use auto source detection. + if source.pattern.ends_with("**/*") { + return true; + } + + // Directories should be promoted to auto source detection + if PathBuf::from(&source.base).join(&source.pattern).is_dir() { + return true; + } + + false + }); + + // Turn `Vec<&GlobEntry>` in `Vec` + let glob_sources: Vec<_> = glob_sources.into_iter().cloned().collect(); + + for path in auto_sources + .iter() + .map(|source| PathBuf::from(&source.base).join(source.pattern.trim_end_matches("**/*"))) + { + let detect_sources = DetectSources::new(path); + + let (files, globs) = detect_sources.detect(); + self.files.extend(files); + self.globs.extend(globs); + } + + let resolved_files: Vec<_> = match fast_glob(&glob_sources) { Ok(matches) => matches .filter_map(|x| dunce::canonicalize(&x).ok()) .collect(), @@ -272,7 +259,7 @@ impl Scanner { }; self.files.extend(resolved_files); - self.globs.extend(sources.clone()); + self.globs.extend(glob_sources); // Re-optimize the globs to reduce the number of patterns we have to scan. self.globs = get_fast_patterns(&self.globs) diff --git a/packages/@tailwindcss-cli/src/commands/build/index.ts b/packages/@tailwindcss-cli/src/commands/build/index.ts index 41650e07bd3f..f7f15b006e9c 100644 --- a/packages/@tailwindcss-cli/src/commands/build/index.ts +++ b/packages/@tailwindcss-cli/src/commands/build/index.ts @@ -138,22 +138,22 @@ export async function handle(args: Result>) { }, }) - let detectSources = (() => { + let sources = (() => { // Disable auto source detection if (compiler.root === 'none') { - return undefined + return [] } // No root specified, use the base directory if (compiler.root === null) { - return { base } + return [{ base, pattern: '**/*' }] } // Use the specified root - return { base: path.resolve(compiler.root.base, compiler.root.pattern) } - })() + return [{ base: path.resolve(compiler.root.base, compiler.root.pattern), pattern: '**/*' }] + })().concat(compiler.globs) - let scanner = new Scanner({ detectSources, sources: compiler.globs }) + let scanner = new Scanner({ sources }) env.DEBUG && console.timeEnd('[@tailwindcss/cli] Setup compiler') return [compiler, scanner] as const diff --git a/packages/@tailwindcss-postcss/src/index.ts b/packages/@tailwindcss-postcss/src/index.ts index 69a693971607..4dfde5103d84 100644 --- a/packages/@tailwindcss-postcss/src/index.ts +++ b/packages/@tailwindcss-postcss/src/index.ts @@ -136,8 +136,7 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin { if (context.scanner === null || rebuildStrategy === 'full') { // Look for candidates used to generate the CSS context.scanner = new Scanner({ - detectSources: { base }, - sources: context.compiler.globs, + sources: [{ base, pattern: '**/*' }].concat(context.compiler.globs), }) } From c3c82eb7c3eb85983888f5cf22c8603ccc33b25e Mon Sep 17 00:00:00 2001 From: Philipp Spiess Date: Fri, 25 Oct 2024 16:35:12 +0200 Subject: [PATCH 10/28] Remove @tailwind utility nodes with parameters --- integrations/cli/index.test.ts | 40 ++++++++++++++++----------------- packages/tailwindcss/src/ast.ts | 5 ++++- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/integrations/cli/index.test.ts b/integrations/cli/index.test.ts index 5b27558d1fbb..8def82337785 100644 --- a/integrations/cli/index.test.ts +++ b/integrations/cli/index.test.ts @@ -375,27 +375,25 @@ describe.only('@source', () => { expect(await fs.dumpFiles('./project-a/dist/*.css')).toMatchInlineSnapshot(` " --- ./project-a/dist/out.css --- - @tailwind utilities source('../../project-b') { - .content-\\[\\'project-a\\/node_modules\\/my-lib-1\\/src\\/index\\.html\\'\\] { - --tw-content: 'project-a/node modules/my-lib-1/src/index.html'; - content: var(--tw-content); - } - .content-\\[\\'project-a\\/node_modules\\/my-lib-2\\/src\\/index\\.html\\'\\] { - --tw-content: 'project-a/node modules/my-lib-2/src/index.html'; - content: var(--tw-content); - } - .content-\\[\\'project-a\\/src\\/logo\\.jpg\\'\\] { - --tw-content: 'project-a/src/logo.jpg'; - content: var(--tw-content); - } - .content-\\[\\'project-b\\/src\\/index\\.html\\'\\] { - --tw-content: 'project-b/src/index.html'; - content: var(--tw-content); - } - .content-\\[\\'project-c\\/src\\/index\\.html\\'\\] { - --tw-content: 'project-c/src/index.html'; - content: var(--tw-content); - } + .content-\\[\\'project-a\\/node_modules\\/my-lib-1\\/src\\/index\\.html\\'\\] { + --tw-content: 'project-a/node modules/my-lib-1/src/index.html'; + content: var(--tw-content); + } + .content-\\[\\'project-a\\/node_modules\\/my-lib-2\\/src\\/index\\.html\\'\\] { + --tw-content: 'project-a/node modules/my-lib-2/src/index.html'; + content: var(--tw-content); + } + .content-\\[\\'project-a\\/src\\/logo\\.jpg\\'\\] { + --tw-content: 'project-a/src/logo.jpg'; + content: var(--tw-content); + } + .content-\\[\\'project-b\\/src\\/index\\.html\\'\\] { + --tw-content: 'project-b/src/index.html'; + content: var(--tw-content); + } + .content-\\[\\'project-c\\/src\\/index\\.html\\'\\] { + --tw-content: 'project-c/src/index.html'; + content: var(--tw-content); } @supports (-moz-orient: inline) { @layer base { diff --git a/packages/tailwindcss/src/ast.ts b/packages/tailwindcss/src/ast.ts index 2ab5a0046e26..6b12e1079f93 100644 --- a/packages/tailwindcss/src/ast.ts +++ b/packages/tailwindcss/src/ast.ts @@ -185,7 +185,10 @@ export function toCss(ast: AstNode[]) { // Rule if (node.kind === 'rule') { - if (node.selector === '@tailwind utilities') { + if ( + node.selector === '@tailwind utilities' || + node.selector.startsWith('@tailwind utilities ') + ) { for (let child of node.nodes) { css += stringify(child, depth) } From 98f74f485d761dd89b2f223460df7f7055a078b1 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 17:20:41 +0200 Subject: [PATCH 11/28] =?UTF-8?q?validate=20existence=20`source(=E2=80=A6)?= =?UTF-8?q?`=20base=20path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/@tailwindcss-node/src/compile.ts | 28 +++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/packages/@tailwindcss-node/src/compile.ts b/packages/@tailwindcss-node/src/compile.ts index 9f3f58fddae4..40bb9ef1fea4 100644 --- a/packages/@tailwindcss-node/src/compile.ts +++ b/packages/@tailwindcss-node/src/compile.ts @@ -10,11 +10,11 @@ import { } from 'tailwindcss' import { getModuleDependencies } from './get-module-dependencies' -export function compile( +export async function compile( css: string, { base, onDependency }: { base: string; onDependency: (path: string) => void }, ) { - return _compile(css, { + let compiler = await _compile(css, { base, async loadModule(id, base) { return loadModule(id, base, onDependency) @@ -23,6 +23,30 @@ export function compile( return loadStylesheet(id, base, onDependency) }, }) + + // Verify if the `source(…)` path exists (until the glob pattern starts) + if (compiler.root && compiler.root !== 'none') { + let globSymbols = /[*{]/ + let basePath = [] + for (let segment of compiler.root.pattern.split('/')) { + if (globSymbols.test(segment)) { + break + } + + basePath.push(segment) + } + + let exists = await fsPromises + .stat(path.resolve(base, basePath.join('/'))) + .then((stat) => stat.isDirectory()) + .catch(() => false) + + if (!exists) { + throw new Error(`The \`source(${compiler.root.pattern})\` does not exist`) + } + } + + return compiler } export async function __unstable__loadDesignSystem(css: string, { base }: { base: string }) { From 193e7bc14450acf73e89b120baf6e3cde5692aee Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 17:21:23 +0200 Subject: [PATCH 12/28] provide base and pattern separately --- packages/@tailwindcss-cli/src/commands/build/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@tailwindcss-cli/src/commands/build/index.ts b/packages/@tailwindcss-cli/src/commands/build/index.ts index f7f15b006e9c..815480f40d7d 100644 --- a/packages/@tailwindcss-cli/src/commands/build/index.ts +++ b/packages/@tailwindcss-cli/src/commands/build/index.ts @@ -150,7 +150,7 @@ export async function handle(args: Result>) { } // Use the specified root - return [{ base: path.resolve(compiler.root.base, compiler.root.pattern), pattern: '**/*' }] + return [{ base: compiler.root.base, pattern: compiler.root.pattern }] })().concat(compiler.globs) let scanner = new Scanner({ sources }) From 2cc2a3335d2214f7eaa9cd1ed11660a313c283b3 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 17:40:51 +0200 Subject: [PATCH 13/28] add `bexpand` for expanding glob expressions --- Cargo.lock | 113 ++++++++++++++++------------------------ crates/oxide/Cargo.toml | 1 + 2 files changed, 47 insertions(+), 67 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f3ecb7a58e2e..eb9602407af0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "bexpand" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "045d7d9db8390cf2c59f39f3bd138f1962ef616b096d1b9f5651c7acba19e5a7" +dependencies = [ + "itertools", + "nom", +] + [[package]] name = "bitflags" version = "2.6.0" @@ -183,6 +193,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -202,7 +221,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.0", + "windows-targets", ] [[package]] @@ -232,6 +251,12 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "napi" version = "2.16.11" @@ -289,6 +314,16 @@ dependencies = [ "libloading", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -485,6 +520,7 @@ dependencies = [ name = "tailwindcss-oxide" version = "0.1.0" dependencies = [ + "bexpand", "bstr", "crossbeam", "dunce", @@ -649,7 +685,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -658,22 +694,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows-targets", ] [[package]] @@ -682,46 +703,28 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -734,48 +737,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/crates/oxide/Cargo.toml b/crates/oxide/Cargo.toml index 315fcf4f5375..763b0c291f69 100644 --- a/crates/oxide/Cargo.toml +++ b/crates/oxide/Cargo.toml @@ -16,6 +16,7 @@ walkdir = "2.5.0" ignore = "0.4.23" glob-match = "0.2.1" dunce = "1.0.5" +bexpand = "1.2.0" [dev-dependencies] tempfile = "3.13.0" From d612e667b81697e0c919e161ae10574c937036d9 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 17:41:43 +0200 Subject: [PATCH 14/28] expand patterns in `GlobEntry` --- crates/oxide/src/lib.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/crates/oxide/src/lib.rs b/crates/oxide/src/lib.rs index b45767c0b171..32ea52fba823 100644 --- a/crates/oxide/src/lib.rs +++ b/crates/oxide/src/lib.rs @@ -1,5 +1,6 @@ use crate::parser::Extractor; use crate::scanner::detect_sources::DetectSources; +use bexpand::Expression; use bstr::ByteSlice; use fxhash::{FxHashMap, FxHashSet}; use glob::fast_glob; @@ -217,6 +218,26 @@ impl Scanner { return; } + // Expand glob patterns and create new `GlobEntry` instances for each expanded pattern. + let sources = sources + .iter() + .flat_map(|source| { + let expression: Result = source.pattern[..].try_into(); + let Ok(expression) = expression else { + return vec![source.clone()]; + }; + + expression + .into_iter() + .filter_map(|expanded_pattern| expanded_pattern.map(|x| x.into()).ok()) + .map(move |pattern| GlobEntry { + base: source.base.clone(), + pattern, + }) + .collect::>() + }) + .collect::>(); + // Partition sources into sources that should be promoted to auto source detection and // sources that should be resolved as globs. let (auto_sources, glob_sources): (Vec<_>, Vec<_>) = sources.iter().partition(|source| { From 9e469928a88be58fc08dc3a678629e78a6585b31 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 17:42:19 +0200 Subject: [PATCH 15/28] run prettier --- packages/@tailwindcss-upgrade/tsconfig.json | 4 +-- turbo.json | 32 +++++---------------- 2 files changed, 9 insertions(+), 27 deletions(-) diff --git a/packages/@tailwindcss-upgrade/tsconfig.json b/packages/@tailwindcss-upgrade/tsconfig.json index b7ac105af223..c4a3e047afe1 100644 --- a/packages/@tailwindcss-upgrade/tsconfig.json +++ b/packages/@tailwindcss-upgrade/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "../tsconfig.base.json", "compilerOptions": { - "allowSyntheticDefaultImports":true - } + "allowSyntheticDefaultImports": true, + }, } diff --git a/turbo.json b/turbo.json index a7f1dc2642b7..35a3e63a7ce7 100644 --- a/turbo.json +++ b/turbo.json @@ -3,14 +3,8 @@ "ui": "tui", "tasks": { "@tailwindcss/oxide#build": { - "dependsOn": [ - "^build" - ], - "outputs": [ - "./index.d.ts", - "./index.js", - "./*.node" - ], + "dependsOn": ["^build"], + "outputs": ["./index.d.ts", "./index.js", "./*.node"], "inputs": [ "./src/**/*", "./build.rs", @@ -23,14 +17,8 @@ ] }, "@tailwindcss/oxide#dev": { - "dependsOn": [ - "^dev" - ], - "outputs": [ - "./index.d.ts", - "./index.js", - "./*.node" - ], + "dependsOn": ["^dev"], + "outputs": ["./index.d.ts", "./index.js", "./*.node"], "inputs": [ "./src/**/*", "./build.rs", @@ -45,12 +33,8 @@ "persistent": true }, "build": { - "dependsOn": [ - "^build" - ], - "outputs": [ - "dist/**" - ] + "dependsOn": ["^build"], + "outputs": ["dist/**"] }, "lint": {}, "dev": { @@ -60,7 +44,5 @@ }, // If rustup is installed outside of the default ~/.rustup directory, we need // to pass the path through to the individual rust tasks. - "globalPassThroughEnv": [ - "RUSTUP_HOME" - ] + "globalPassThroughEnv": ["RUSTUP_HOME"] } From 810d98af6af91970f282438d067b7f328b1fe575 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 17:45:20 +0200 Subject: [PATCH 16/28] update test name --- integrations/cli/index.test.ts | 276 ++++++++++++++++----------------- 1 file changed, 137 insertions(+), 139 deletions(-) diff --git a/integrations/cli/index.test.ts b/integrations/cli/index.test.ts index 8def82337785..1180e901324a 100644 --- a/integrations/cli/index.test.ts +++ b/integrations/cli/index.test.ts @@ -256,159 +256,157 @@ describe.each([ ) }) -describe.only('@source', () => { - test( - 'it works', - { - fs: { - 'package.json': json`{}`, - 'pnpm-workspace.yaml': yaml` - # - packages: - - project-a - `, - 'project-a/package.json': json` - { - "dependencies": { - "tailwindcss": "workspace:^", - "@tailwindcss/cli": "workspace:^" - } +test( + 'source(…) and `@source` can be configured to use auto source detection', + { + fs: { + 'package.json': json`{}`, + 'pnpm-workspace.yaml': yaml` + # + packages: + - project-a + `, + 'project-a/package.json': json` + { + "dependencies": { + "tailwindcss": "workspace:^", + "@tailwindcss/cli": "workspace:^" } - `, - 'project-a/src/index.css': css` - @import 'tailwindcss/theme' theme(reference); + } + `, + 'project-a/src/index.css': css` + @import 'tailwindcss/theme' theme(reference); - /* Run auto-content detection in ../../project-b */ - @import 'tailwindcss/utilities' source('../../project-b'); + /* Run auto-content detection in ../../project-b */ + @import 'tailwindcss/utilities' source('../../project-b'); - /* Additive: */ - /* {my-lib-1,my-lib-2}: expand */ - /* *.html: only look for .html */ - @source '../node_modules/{my-lib-1,my-lib-2}/src/**/*.html'; + /* Additive: */ + /* {my-lib-1,my-lib-2}: expand */ + /* *.html: only look for .html */ + @source '../node_modules/{my-lib-1,my-lib-2}/src/**/*.html'; - /* We typically ignore these extensions, but now include them explicitly */ - @source './logo.{jpg,png}'; + /* We typically ignore these extensions, but now include them explicitly */ + @source './logo.{jpg,png}'; - /* Project C should apply auto source detection */ - @source '../../project-c'; - `, + /* Project C should apply auto source detection */ + @source '../../project-c'; + `, - // Project A is the current folder, but we explicitly configured - // `source(project-b)`, therefore project-a should not be included in - // the output. - 'project-a/src/index.html': html` -
- `, + // Project A is the current folder, but we explicitly configured + // `source(project-b)`, therefore project-a should not be included in + // the output. + 'project-a/src/index.html': html` +
+ `, - // Project A explicitly includes an extension we usually ignore, - // therefore it should be included in the output. - 'project-a/src/logo.jpg': html` -
- `, + // Project A explicitly includes an extension we usually ignore, + // therefore it should be included in the output. + 'project-a/src/logo.jpg': html` +
+ `, - // Project A explicitly includes node_modules/{my-lib-1,my-lib-2}, - // therefore these files should be included in the output. - 'project-a/node_modules/my-lib-1/src/index.html': html` -
- `, - 'project-a/node_modules/my-lib-2/src/index.html': html` -
- `, + // Project A explicitly includes node_modules/{my-lib-1,my-lib-2}, + // therefore these files should be included in the output. + 'project-a/node_modules/my-lib-1/src/index.html': html` +
+ `, + 'project-a/node_modules/my-lib-2/src/index.html': html` +
+ `, - // Project B is the configured `source(…)`, therefore auto source - // detection should include known extensions and folders in the output. - 'project-b/src/index.html': html` -
- `, + // Project B is the configured `source(…)`, therefore auto source + // detection should include known extensions and folders in the output. + 'project-b/src/index.html': html` +
+ `, - // Project B is the configured `source(…)`, therefore auto source - // detection should apply and node_modules should not be included in the - // output. - 'project-b/node_modules/my-lib-3/src/index.html': html` -
- `, + // Project B is the configured `source(…)`, therefore auto source + // detection should apply and node_modules should not be included in the + // output. + 'project-b/node_modules/my-lib-3/src/index.html': html` +
+ `, - // Project C should apply auto source detection, therefore known - // extensions and folders should be included in the output. - 'project-c/src/index.html': html` -
- `, + // Project C should apply auto source detection, therefore known + // extensions and folders should be included in the output. + 'project-c/src/index.html': html` +
+ `, - // Project C should apply auto source detection, therefore known ignored - // extensions should not be included in the output. - 'project-c/src/logo.jpg': html` -
- `, + // Project C should apply auto source detection, therefore known ignored + // extensions should not be included in the output. + 'project-c/src/logo.jpg': html` +
+ `, - // Project C should apply auto source detection, therefore node_modules - // should not be included in the output. - 'project-c/node_modules/my-lib-1/src/index.html': html` -
- `, - }, + // Project C should apply auto source detection, therefore node_modules + // should not be included in the output. + 'project-c/node_modules/my-lib-1/src/index.html': html` +
+ `, }, - async ({ fs, exec, root }) => { - console.log( - await exec('pnpm tailwindcss --input src/index.css --output dist/out.css', { - cwd: path.join(root, 'project-a'), - }), - ) + }, + async ({ fs, exec, root }) => { + console.log( + await exec('pnpm tailwindcss --input src/index.css --output dist/out.css', { + cwd: path.join(root, 'project-a'), + }), + ) - expect(await fs.dumpFiles('./project-a/dist/*.css')).toMatchInlineSnapshot(` - " - --- ./project-a/dist/out.css --- - .content-\\[\\'project-a\\/node_modules\\/my-lib-1\\/src\\/index\\.html\\'\\] { - --tw-content: 'project-a/node modules/my-lib-1/src/index.html'; - content: var(--tw-content); - } - .content-\\[\\'project-a\\/node_modules\\/my-lib-2\\/src\\/index\\.html\\'\\] { - --tw-content: 'project-a/node modules/my-lib-2/src/index.html'; - content: var(--tw-content); - } - .content-\\[\\'project-a\\/src\\/logo\\.jpg\\'\\] { - --tw-content: 'project-a/src/logo.jpg'; - content: var(--tw-content); - } - .content-\\[\\'project-b\\/src\\/index\\.html\\'\\] { - --tw-content: 'project-b/src/index.html'; - content: var(--tw-content); - } - .content-\\[\\'project-c\\/src\\/index\\.html\\'\\] { - --tw-content: 'project-c/src/index.html'; - content: var(--tw-content); - } - @supports (-moz-orient: inline) { - @layer base { - *, ::before, ::after, ::backdrop { - --tw-content: ""; - } + expect(await fs.dumpFiles('./project-a/dist/*.css')).toMatchInlineSnapshot(` + " + --- ./project-a/dist/out.css --- + .content-\\[\\'project-a\\/node_modules\\/my-lib-1\\/src\\/index\\.html\\'\\] { + --tw-content: 'project-a/node modules/my-lib-1/src/index.html'; + content: var(--tw-content); + } + .content-\\[\\'project-a\\/node_modules\\/my-lib-2\\/src\\/index\\.html\\'\\] { + --tw-content: 'project-a/node modules/my-lib-2/src/index.html'; + content: var(--tw-content); + } + .content-\\[\\'project-a\\/src\\/logo\\.jpg\\'\\] { + --tw-content: 'project-a/src/logo.jpg'; + content: var(--tw-content); + } + .content-\\[\\'project-b\\/src\\/index\\.html\\'\\] { + --tw-content: 'project-b/src/index.html'; + content: var(--tw-content); + } + .content-\\[\\'project-c\\/src\\/index\\.html\\'\\] { + --tw-content: 'project-c/src/index.html'; + content: var(--tw-content); + } + @supports (-moz-orient: inline) { + @layer base { + *, ::before, ::after, ::backdrop { + --tw-content: ""; } } - @property --tw-content { - syntax: "*"; - inherits: false; - initial-value: ""; - } - " - `) - }, - ) -}) + } + @property --tw-content { + syntax: "*"; + inherits: false; + initial-value: ""; + } + " + `) + }, +) From ec70c4c7ffc6e5db7d75744ae299a1f757f32332 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 17:56:12 +0200 Subject: [PATCH 17/28] refactor: use variable directly --- packages/@tailwindcss-cli/src/commands/build/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@tailwindcss-cli/src/commands/build/index.ts b/packages/@tailwindcss-cli/src/commands/build/index.ts index 815480f40d7d..b5a97367f279 100644 --- a/packages/@tailwindcss-cli/src/commands/build/index.ts +++ b/packages/@tailwindcss-cli/src/commands/build/index.ts @@ -150,7 +150,7 @@ export async function handle(args: Result>) { } // Use the specified root - return [{ base: compiler.root.base, pattern: compiler.root.pattern }] + return [compiler.root] })().concat(compiler.globs) let scanner = new Scanner({ sources }) From 1c140a8d6ae2850c24a3e5d69575b3d0def42662 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 18:11:16 +0200 Subject: [PATCH 18/28] =?UTF-8?q?update=20`@tailwindcss/postcss`=20with=20?= =?UTF-8?q?new=20`source(=E2=80=A6)`=20setup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/@tailwindcss-postcss/src/index.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/@tailwindcss-postcss/src/index.ts b/packages/@tailwindcss-postcss/src/index.ts index 4dfde5103d84..aeb428176ad3 100644 --- a/packages/@tailwindcss-postcss/src/index.ts +++ b/packages/@tailwindcss-postcss/src/index.ts @@ -134,10 +134,23 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin { } if (context.scanner === null || rebuildStrategy === 'full') { + let sources = (() => { + // Disable auto source detection + if (context.compiler.root === 'none') { + return [] + } + + // No root specified, use the base directory + if (context.compiler.root === null) { + return [{ base, pattern: '**/*' }] + } + + // Use the specified root + return [context.compiler.root] + })().concat(context.compiler.globs) + // Look for candidates used to generate the CSS - context.scanner = new Scanner({ - sources: [{ base, pattern: '**/*' }].concat(context.compiler.globs), - }) + context.scanner = new Scanner({ sources }) } env.DEBUG && console.time('[@tailwindcss/postcss] Scan for candidates') From 894290ae53e1bd4ea001e1102724d0a909ebbdec Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 18:11:35 +0200 Subject: [PATCH 19/28] =?UTF-8?q?update=20`@tailwindcss/vite`=20with=20new?= =?UTF-8?q?=20`source(=E2=80=A6)`=20setup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/@tailwindcss-vite/src/index.ts | 31 +++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/@tailwindcss-vite/src/index.ts b/packages/@tailwindcss-vite/src/index.ts index 9571342cdbda..6fca22743967 100644 --- a/packages/@tailwindcss-vite/src/index.ts +++ b/packages/@tailwindcss-vite/src/index.ts @@ -348,6 +348,11 @@ class Root { // root. private dependencies = new Set() + // Whether to include candidates from the module graph. This is disabled when + // the user provides `source(none)` to essentially disable auto source + // detection. + private includeCandidatesFromModuleGraph = true + constructor( private id: string, private getSharedCandidates: () => Set, @@ -379,9 +384,23 @@ class Root { }) env.DEBUG && console.timeEnd('[@tailwindcss/vite] Setup compiler') - this.scanner = new Scanner({ - sources: this.compiler.globs, - }) + let sources = (() => { + // Disable auto source detection + if (this.compiler.root === 'none') { + this.includeCandidatesFromModuleGraph = false + return [] + } + + // No root specified, use the module graph + if (this.compiler.root === null) { + return [] + } + + // Use the specified root + return [this.compiler.root] + })().concat(this.compiler.globs) + + this.scanner = new Scanner({ sources }) } // This should not be here, but right now the Vite plugin is setup where we @@ -416,7 +435,11 @@ class Root { this.requiresRebuild = true env.DEBUG && console.time('[@tailwindcss/vite] Build CSS') - let result = this.compiler.build([...this.getSharedCandidates(), ...this.candidates]) + let result = this.compiler.build( + this.includeCandidatesFromModuleGraph + ? [...this.getSharedCandidates(), ...this.candidates] + : Array.from(this.candidates), + ) env.DEBUG && console.timeEnd('[@tailwindcss/vite] Build CSS') return result From 6a3159e8e1aa6218297986e81de73c27ad8b35c7 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 18:11:43 +0200 Subject: [PATCH 20/28] cleanup `console.log` --- integrations/cli/index.test.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/integrations/cli/index.test.ts b/integrations/cli/index.test.ts index 1180e901324a..59a7604d8f8c 100644 --- a/integrations/cli/index.test.ts +++ b/integrations/cli/index.test.ts @@ -365,11 +365,9 @@ test( }, }, async ({ fs, exec, root }) => { - console.log( - await exec('pnpm tailwindcss --input src/index.css --output dist/out.css', { - cwd: path.join(root, 'project-a'), - }), - ) + await exec('pnpm tailwindcss --input src/index.css --output dist/out.css', { + cwd: path.join(root, 'project-a'), + }) expect(await fs.dumpFiles('./project-a/dist/*.css')).toMatchInlineSnapshot(` " From 9f6ab1a80729124477562e7e4ee0ae33ede01f3f Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 18:15:26 +0200 Subject: [PATCH 21/28] add todo for follow up PR --- packages/@tailwindcss-vite/src/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/@tailwindcss-vite/src/index.ts b/packages/@tailwindcss-vite/src/index.ts index 6fca22743967..a8e94561bae6 100644 --- a/packages/@tailwindcss-vite/src/index.ts +++ b/packages/@tailwindcss-vite/src/index.ts @@ -396,6 +396,9 @@ class Root { return [] } + // TODO: In a follow up PR we want this filter this against the module graph. + this.includeCandidatesFromModuleGraph = false + // Use the specified root return [this.compiler.root] })().concat(this.compiler.globs) From 86e9e4dbeb5b06ddd952ffbec87d153d9a99efa4 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 18:35:24 +0200 Subject: [PATCH 22/28] =?UTF-8?q?add=20CLI=20watch=20mode=20tests=20for=20?= =?UTF-8?q?`@source`=20and=20`source(=E2=80=A6)`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- integrations/cli/index.test.ts | 102 ++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 2 deletions(-) diff --git a/integrations/cli/index.test.ts b/integrations/cli/index.test.ts index 59a7604d8f8c..b36ca4c07688 100644 --- a/integrations/cli/index.test.ts +++ b/integrations/cli/index.test.ts @@ -257,7 +257,7 @@ describe.each([ }) test( - 'source(…) and `@source` can be configured to use auto source detection', + 'source(…) and `@source` can be configured to use auto source detection (build + watch mode)', { fs: { 'package.json': json`{}`, @@ -364,7 +364,7 @@ test( `, }, }, - async ({ fs, exec, root }) => { + async ({ fs, exec, spawn, root }) => { await exec('pnpm tailwindcss --input src/index.css --output dist/out.css', { cwd: path.join(root, 'project-a'), }) @@ -406,5 +406,103 @@ test( } " `) + + // Watch mode tests + await spawn('pnpm tailwindcss --input src/index.css --output dist/out.css --watch', { + cwd: path.join(root, 'project-a'), + }) + + // Changes to project-a should not be included in the output, we changed the + // base folder to project-b. + await fs.write( + 'project-a/src/index.html', + html`
`, + ) + await fs.expectFileNotToContain('./project-a/dist/out.css', [ + candidate`[.changed_&]:content-['project-a/src/index.html']`, + ]) + + // Changes to this file should be included, because we explicitly listed + // them using `@source`. + await fs.write( + 'project-a/src/logo.jpg', + html`
`, + ) + await fs.expectFileToContain('./project-a/dist/out.css', [ + candidate`[.changed_&]:content-['project-a/src/logo.jpg']`, + ]) + + // Changes to these files should be included, because we explicitly listed + // them using `@source`. + await fs.write( + 'project-a/node_modules/my-lib-1/src/index.html', + html`
`, + ) + await fs.expectFileToContain('./project-a/dist/out.css', [ + candidate`[.changed_&]:content-['project-a/node_modules/my-lib-1/src/index.html']`, + ]) + await fs.write( + 'project-a/node_modules/my-lib-2/src/index.html', + html`
`, + ) + await fs.expectFileToContain('./project-a/dist/out.css', [ + candidate`[.changed_&]:content-['project-a/node_modules/my-lib-2/src/index.html']`, + ]) + + // Changes to this file should be included, because we changed the base to + // `project-b`. + await fs.write( + 'project-b/src/index.html', + html`
`, + ) + await fs.expectFileToContain('./project-a/dist/out.css', [ + candidate`[.changed_&]:content-['project-b/src/index.html']`, + ]) + + // Changes to this file should not be included. We did change the base to + // `project-b`, but we still apply the auto source detection rules which + // ignore `node_modules`. + await fs.write( + 'project-b/node_modules/my-lib-3/src/index.html', + html`
`, + ) + await fs.expectFileNotToContain('./project-a/dist/out.css', [ + candidate`[.changed_&]:content-['project-b/node_modules/my-lib-3/src/index.html']`, + ]) + + // Project C was added explicitly via `@source`, therefore changes to these + // files should be included. + await fs.write( + 'project-c/src/index.html', + html`
`, + ) + await fs.expectFileToContain('./project-a/dist/out.css', [ + candidate`[.changed_&]:content-['project-c/src/index.html']`, + ]) + + // Except for these files, since they are ignored by the default auto source + // detection rules. + await fs.write( + 'project-c/src/logo.jpg', + html`
`, + ) + await fs.expectFileNotToContain('./project-a/dist/out.css', [ + candidate`[.changed_&]:content-['project-c/src/logo.jpg']`, + ]) + await fs.write( + 'project-c/node_modules/my-lib-1/src/index.html', + html`
`, + ) + await fs.expectFileNotToContain('./project-a/dist/out.css', [ + candidate`[.changed_&]:content-['project-c/node_modules/my-lib-1/src/index.html']`, + ]) }, ) From 5747d1c8eb3bc1d65dc3efb2e3c90e7d954a34d3 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 18:41:07 +0200 Subject: [PATCH 23/28] migrate detect sources to sources --- crates/oxide/tests/scanner.rs | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/crates/oxide/tests/scanner.rs b/crates/oxide/tests/scanner.rs index 2eb128168fff..6e7f4bb1da81 100644 --- a/crates/oxide/tests/scanner.rs +++ b/crates/oxide/tests/scanner.rs @@ -1,6 +1,5 @@ #[cfg(test)] mod scanner { - use scanner::detect_sources::DetectSources; use std::process::Command; use std::{fs, path}; @@ -35,18 +34,20 @@ mod scanner { let base = format!("{}", dir.display()); // Resolve all content paths for the (temporary) current working directory - let mut scanner = Scanner::new( - Some(DetectSources::new(base.clone().into())), - Some( - globs - .iter() - .map(|x| GlobEntry { - base: base.clone(), - pattern: x.to_string(), - }) - .collect(), - ), - ); + let mut sources: Vec = globs + .iter() + .map(|x| GlobEntry { + base: base.clone(), + pattern: x.to_string(), + }) + .collect(); + + sources.push(GlobEntry { + base: base.clone(), + pattern: "**/*".to_string(), + }); + + let mut scanner = Scanner::new(Some(sources)); let candidates = scanner.scan(); From f3fe3ce2899e414aab9c9413ad8f6d7942265619 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 18:53:24 +0200 Subject: [PATCH 24/28] resolves base path in tests Ensure symlinks are handled because `/var/` points to `/private/var/`. --- crates/oxide/tests/scanner.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/oxide/tests/scanner.rs b/crates/oxide/tests/scanner.rs index 6e7f4bb1da81..fe15be414fb2 100644 --- a/crates/oxide/tests/scanner.rs +++ b/crates/oxide/tests/scanner.rs @@ -69,7 +69,11 @@ mod scanner { paths = paths .into_iter() .map(|x| { - let parent_dir = format!("{}{}", &base.to_string(), path::MAIN_SEPARATOR); + let parent_dir = format!( + "{}{}", + fs::canonicalize(&base).unwrap().display(), + path::MAIN_SEPARATOR + ); x.replace(&parent_dir, "") // Normalize paths to use unix style separators .replace('\\', "/") From d600964abeeccd0cc9a085fe8eaa7d395dec1275 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 18:59:19 +0200 Subject: [PATCH 25/28] =?UTF-8?q?update=20all=20instances=20of=20`new=20Sc?= =?UTF-8?q?anner(=E2=80=A6)`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This now makes use of just `sources` instead of auto content detection. --- packages/@tailwindcss-upgrade/src/migrate-js-config.ts | 2 +- packages/tailwindcss/src/candidate.bench.ts | 2 +- packages/tailwindcss/src/index.bench.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@tailwindcss-upgrade/src/migrate-js-config.ts b/packages/@tailwindcss-upgrade/src/migrate-js-config.ts index f8a6ad6f8dc2..60bcbcefd7cb 100644 --- a/packages/@tailwindcss-upgrade/src/migrate-js-config.ts +++ b/packages/@tailwindcss-upgrade/src/migrate-js-config.ts @@ -285,7 +285,7 @@ function keyframesToCss(keyframes: Record): string { } function autodetectedSourceFiles(base: string) { - let scanner = new Scanner({ detectSources: { base } }) + let scanner = new Scanner({ sources: [{ base, pattern: '**/*' }] }) scanner.scan() return scanner.files } diff --git a/packages/tailwindcss/src/candidate.bench.ts b/packages/tailwindcss/src/candidate.bench.ts index ef7c97114dfd..5b7171432e17 100644 --- a/packages/tailwindcss/src/candidate.bench.ts +++ b/packages/tailwindcss/src/candidate.bench.ts @@ -8,7 +8,7 @@ import { Theme } from './theme' const root = process.env.FOLDER || process.cwd() // Auto content detection -const scanner = new Scanner({ detectSources: { base: root } }) +const scanner = new Scanner({ sources: [{ base: root, pattern: '**/*' }] }) const candidates = scanner.scan() const designSystem = buildDesignSystem(new Theme()) diff --git a/packages/tailwindcss/src/index.bench.ts b/packages/tailwindcss/src/index.bench.ts index dcffb522e3f2..fb9cd0e71084 100644 --- a/packages/tailwindcss/src/index.bench.ts +++ b/packages/tailwindcss/src/index.bench.ts @@ -7,7 +7,7 @@ const root = process.env.FOLDER || process.cwd() const css = String.raw bench('compile', async () => { - let scanner = new Scanner({ detectSources: { base: root } }) + let scanner = new Scanner({ sources: [{ base: root, pattern: '**/*' }] }) let candidates = scanner.scan() let { build } = await compile(css` From bf39acd37f4063d84ee47945a5ebd79fedb4e9d2 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 25 Oct 2024 21:45:23 +0200 Subject: [PATCH 26/28] Update crates/oxide/src/lib.rs Co-authored-by: Jordan Pittman --- crates/oxide/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/oxide/src/lib.rs b/crates/oxide/src/lib.rs index 32ea52fba823..24eecca8bc0c 100644 --- a/crates/oxide/src/lib.rs +++ b/crates/oxide/src/lib.rs @@ -229,10 +229,10 @@ impl Scanner { expression .into_iter() - .filter_map(|expanded_pattern| expanded_pattern.map(|x| x.into()).ok()) + .filter_map(Result::ok) .map(move |pattern| GlobEntry { base: source.base.clone(), - pattern, + pattern: pattern.into(), }) .collect::>() }) From d549337c6c5ddc369b80e36bc4333638e5c0d9c8 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Sat, 26 Oct 2024 01:30:07 +0200 Subject: [PATCH 27/28] Update packages/@tailwindcss-vite/src/index.ts Co-authored-by: Jordan Pittman --- packages/@tailwindcss-vite/src/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/@tailwindcss-vite/src/index.ts b/packages/@tailwindcss-vite/src/index.ts index a8e94561bae6..e85daecf2542 100644 --- a/packages/@tailwindcss-vite/src/index.ts +++ b/packages/@tailwindcss-vite/src/index.ts @@ -393,6 +393,8 @@ class Root { // No root specified, use the module graph if (this.compiler.root === null) { + this.includeCandidatesFromModuleGraph = true + return [] } From d5b724cff9e55b8bcb9d600f8173ef13fd71ee08 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Sat, 26 Oct 2024 01:30:13 +0200 Subject: [PATCH 28/28] Update packages/@tailwindcss-vite/src/index.ts Co-authored-by: Jordan Pittman --- packages/@tailwindcss-vite/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@tailwindcss-vite/src/index.ts b/packages/@tailwindcss-vite/src/index.ts index e85daecf2542..b045ab3505ae 100644 --- a/packages/@tailwindcss-vite/src/index.ts +++ b/packages/@tailwindcss-vite/src/index.ts @@ -399,7 +399,7 @@ class Root { } // TODO: In a follow up PR we want this filter this against the module graph. - this.includeCandidatesFromModuleGraph = false + this.includeCandidatesFromModuleGraph = true // Use the specified root return [this.compiler.root]