Skip to content

Commit

Permalink
Scan Next.js dynamic route segments with manual @source rules (#16457)
Browse files Browse the repository at this point in the history
Part of #16287

## Test plan

Added unit and integration tests

---------

Co-authored-by: Robin Malfait <[email protected]>
  • Loading branch information
philipp-spiess and RobinMalfait authored Feb 18, 2025
1 parent f014108 commit 08972f2
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Ensure drop shadow utilities don't inherit unexpectedly ([#16471](https://github.com/tailwindlabs/tailwindcss/pull/16471))
- Export backwards compatible config and plugin types from `tailwindcss/plugin` ([#16505](https://github.com/tailwindlabs/tailwindcss/pull/16505))
- Ensure JavaScript plugins that emit nested rules referencing to the utility name work as expected ([#16539](https://github.com/tailwindlabs/tailwindcss/pull/16539))
- Ensure that Next.js splat routes are automatically scanned for classes ([#16457](https://github.com/tailwindlabs/tailwindcss/pull/16457))
- Pin exact versions of `tailwindcss` and `@tailwindcss/*` ([#16623](https://github.com/tailwindlabs/tailwindcss/pull/16623))
- Upgrade: Report errors when updating dependencies ([#16504](https://github.com/tailwindlabs/tailwindcss/pull/16504))
- Upgrade: Ensure a `darkMode` JS config setting with block syntax converts to use `@slot` ([#16507](https://github.com/tailwindlabs/tailwindcss/pull/16507))
Expand Down
23 changes: 16 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/oxide/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ walkdir = "2.5.0"
ignore = "0.4.23"
dunce = "1.0.5"
bexpand = "1.2.0"
glob-match = "0.2.1"
fast-glob = "0.4.3"

[dev-dependencies]
tempfile = "3.13.0"
4 changes: 2 additions & 2 deletions crates/oxide/src/glob.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use fast_glob::glob_match;
use fxhash::{FxHashMap, FxHashSet};
use glob_match::glob_match;
use std::path::{Path, PathBuf};
use tracing::event;

Expand Down Expand Up @@ -173,7 +173,7 @@ pub fn path_matches_globs(path: &Path, globs: &[GlobEntry]) -> bool {

globs
.iter()
.any(|g| glob_match(&format!("{}/{}", g.base, g.pattern), &path))
.any(|g| glob_match(&format!("{}/{}", g.base, g.pattern), path.as_bytes()))
}

#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion crates/oxide/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use crate::scanner::allowed_paths::resolve_paths;
use crate::scanner::detect_sources::DetectSources;
use bexpand::Expression;
use bstr::ByteSlice;
use fast_glob::glob_match;
use fxhash::{FxHashMap, FxHashSet};
use glob::optimize_patterns;
use glob_match::glob_match;
use paths::Path;
use rayon::prelude::*;
use scanner::allowed_paths::read_dir;
Expand Down
26 changes: 26 additions & 0 deletions crates/oxide/tests/scanner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,32 @@ mod scanner {
assert_eq!(candidates, vec!["content-['foo.styl']"]);
}

#[test]
fn it_should_scan_next_dynamic_folders() {
let candidates = scan_with_globs(
&[
// We know that `.styl` extensions are ignored, so they are not covered by auto content
// detection.
("app/[slug]/page.styl", "content-['[slug]']"),
("app/[...slug]/page.styl", "content-['[...slug]']"),
("app/[[...slug]]/page.styl", "content-['[[...slug]]']"),
("app/(theme)/page.styl", "content-['(theme)']"),
],
vec!["./**/*.{styl}"],
)
.1;

assert_eq!(
candidates,
vec![
"content-['(theme)']",
"content-['[...slug]']",
"content-['[[...slug]]']",
"content-['[slug]']",
],
);
}

#[test]
fn it_should_scan_absolute_paths() {
// Create a temporary working directory
Expand Down
86 changes: 86 additions & 0 deletions integrations/postcss/next.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,89 @@ describe.each(['turbo', 'webpack'])('%s', (bundler) => {
},
)
})

test(
'should scan dynamic route segments',
{
fs: {
'package.json': json`
{
"dependencies": {
"react": "^18",
"react-dom": "^18",
"next": "^14"
},
"devDependencies": {
"@tailwindcss/postcss": "workspace:^",
"tailwindcss": "workspace:^"
}
}
`,
'postcss.config.mjs': js`
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
'@tailwindcss/postcss': {},
},
}
export default config
`,
'next.config.mjs': js`
/** @type {import('next').NextConfig} */
const nextConfig = {}
export default nextConfig
`,
'app/a/[slug]/page.js': js`
export default function Page() {
return <h1 className="content-['[slug]']">Hello, Next.js!</h1>
}
`,
'app/b/[...slug]/page.js': js`
export default function Page() {
return <h1 className="content-['[...slug]']">Hello, Next.js!</h1>
}
`,
'app/c/[[...slug]]/page.js': js`
export default function Page() {
return <h1 className="content-['[[...slug]]']">Hello, Next.js!</h1>
}
`,
'app/d/(theme)/page.js': js`
export default function Page() {
return <h1 className="content-['(theme)']">Hello, Next.js!</h1>
}
`,
'app/layout.js': js`
import './globals.css'
export default function RootLayout({ children }) {
return (
<html>
<body>{children}</body>
</html>
)
}
`,
'app/globals.css': css`
@import 'tailwindcss/utilities' source(none);
@source './**/*.{js,ts,jsx,tsx,mdx}';
`,
},
},
async ({ fs, exec, expect }) => {
await exec('pnpm next build')

let files = await fs.glob('.next/static/css/**/*.css')
expect(files).toHaveLength(1)
let [filename] = files[0]

await fs.expectFileToContain(filename, [
candidate`content-['[slug]']`,
candidate`content-['[...slug]']`,
candidate`content-['[[...slug]]']`,
candidate`content-['(theme)']`,
])
},
)

0 comments on commit 08972f2

Please sign in to comment.