8000 Apply general feedback for `@sources` by RobinMalfait · Pull Request #14135 · tailwindlabs/tailwindcss · GitHub
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Add support for `inline` option when defining `@theme` values ([#14095](https://github.com/tailwindlabs/tailwindcss/pull/14095))
- Add `inert` variant ([#14129](https://github.com/tailwindlabs/tailwindcss/pull/14129))
- Add `@source` support ([#14078](https://github.com/tailwindlabs/tailwindcss/pull/14078), [14063](https://github.com/tailwindlabs/tailwindcss/pull/14063), [14085](https://github.com/tailwindlabs/tailwindcss/pull/14085), [14079](https://github.com/tailwindlabs/tailwindcss/pull/14079), [14067](https://github.com/tailwindlabs/tailwindcss/pull/14067), [14076](https://github.com/tailwindlabs/tailwindcss/pull/14076), [14080](https://github.com/tailwindlabs/tailwindcss/pull/14080), [14127](https://github.com/tailwindlabs/tailwindcss/pull/14127))
- Add `@source` support ([#14078](https://github.com/tailwindlabs/tailwindcss/pull/14078), [14063](https://github.com/tailwindlabs/tailwindcss/pull/14063), [14085](https://github.com/tailwindlabs/tailwindcss/pull/14085), [14079](https://github.com/tailwindlabs/tailwindcss/pull/14079), [14067](https://github.com/tailwindlabs/tailwindcss/pull/14067), [14076](https://github.com/tailwindlabs/tailwindcss/pull/14076), [14080](https://github.com/tailwindlabs/tailwindcss/pull/14080), [14127](https://github.com/tailwindlabs/tailwindcss/pull/14127), [14135](https://github.com/tailwindlabs/tailwindcss/pull/14135))

## [4.0.0-alpha.18] - 2024-07-25

Expand Down
59 changes: 42 additions & 17 deletions crates/node/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use napi::bindgen_prelude::{FromNapiValue, ToNapiValue};
use std::path::PathBuf;
use std::{collections::HashSet, path::PathBuf};

#[macro_use]
extern crate napi_derive;
Expand All @@ -24,6 +24,12 @@ impl From<ChangedContent> for tailwindcss_oxide::ChangedContent {
#[derive(Debug, Clone)]
#[napi]
pub struct ScanResult {
// Private information necessary for incremental rebuilds. Note: these fields are not exposed
// to JS
base: Option<String>,
sources: Vec<GlobEntry>,

// Public API:
pub globs: Vec<GlobEntry>,
pub files: Vec<String>,
pub candidates: Vec<String>,
Expand All @@ -33,34 +39,47 @@ pub struct ScanResult {
impl ScanResult {
#[napi]
pub fn scan_files(&self, input: Vec<ChangedContent>) -> Vec<String> {
tailwindcss_oxide::scan_files_with_globs(
let result = tailwindcss_oxide::scan_dir(tailwindcss_oxide::ScanOptions {
base: self.base.clone(),
sources: self.sources.clone().into_iter().map(Into::into).collect(),
});

let mut unique_candidates: HashSet<String> = HashSet::from_iter(result.candidates);
let candidates_from_files: HashSet<String> = HashSet::from_iter(tailwindcss_oxide::scan_files(
input.into_iter().map(Into::into).collect(),
self.globs.clone().into_iter().map(Into::into).collect(),
)
IO::Parallel as u8 | Parsing::Parallel as u8,
));

unique_candidates.extend(candidates_from_files);

unique_candidates
.into_iter()
.map(|x| x.to_string())
.collect()
}
}

#[derive(Debug, Clone)]
#[napi(object)]
pub struct GlobEntry {
pub base: String,
pub glob: String,
pub pattern: String,
}

impl From<GlobEntry> for tailwindcss_oxide::GlobEntry {
fn from(globs: GlobEntry) -> Self {
fn from(glob: GlobEntry) -> Self {
tailwindcss_oxide::GlobEntry {
base: globs.base,
glob: globs.glob,
base: glob.base,
pattern: glob.pattern,
}
}
}

impl From<tailwindcss_oxide::GlobEntry> for GlobEntry {
fn from(globs: tailwindcss_oxide::GlobEntry) -> Self {
fn from(glob: tailwindcss_oxide::GlobEntry) -> Self {
GlobEntry {
base: globs.base,
glob: globs.glob,
base: glob.base,
pattern: glob.pattern,
}
}
}
Expand All @@ -69,9 +88,9 @@ impl From<tailwindcss_oxide::GlobEntry> for GlobEntry {
#[napi(object)]
pub struct ScanOptions {
/// Base path to start scanning from
pub base: String,
/// Glob content paths
pub content_paths: Option<Vec<GlobEntry>>,
pub base: Option<String>,
/// Glob sources
pub sources: Option<Vec<GlobEntry>>,
}

#[napi]
Expand All @@ -82,16 +101,22 @@ pub fn clear_cache() {
#[napi]
pub fn scan_dir(args: ScanOptions) -> ScanResult {
let result = tailwindcss_oxide::scan_dir(tailwindcss_oxide::ScanOptions {
base: args.base,
content_paths: args
.content_paths
base: args.base.clone(),
sources: args
.sources
.clone()
.unwrap_or_default()
.into_iter()
.map(Into::into)
.collect(),
});

ScanResult {
// Private
base: args.base,
sources: args.sources.unwrap_or_default(),

// Public
files: result.files,
candidates: result.candidates,
globs: result.globs.into_iter().map(Into::into).collect(),
Expand Down
24 changes: 12 additions & 12 deletions crates/oxide/src/glob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub fn get_fast_patterns(patterns: &Vec<GlobEntry>) -> Vec<(PathBuf, Vec<String>

for pattern in patterns {
let base_path = PathBuf::from(&pattern.base);
let pattern = &pattern.glob;
let pattern = &pattern.pattern;

let is_negated = pattern.starts_with('!');
let mut pattern = pattern.clone();
Expand Down Expand Up @@ -141,7 +141,7 @@ pub fn path_matches_globs(path: &Path, globs: &[GlobEntry]) -> bool {

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

/// Given this input: a-{b,c}-d-{e,f}
Expand Down Expand Up @@ -248,7 +248,7 @@ mod tests {
fn it_should_keep_globs_that_start_with_file_wildcards_as_is() {
let actual = get_fast_patterns(&vec![GlobEntry {
base: "/projects".to_string(),
glob: "*.html".to_string(),
pattern: "*.html".to_string(),
}]);
let expected = vec![(PathBuf::from("/projects"), vec!["*.html".to_string()])];

Expand All @@ -259,7 +259,7 @@ mod tests {
fn it_should_keep_globs_that_start_with_folder_wildcards_as_is() {
let actual = get_fast_patterns(&vec![GlobEntry {
base: "/projects".to_string(),
glob: "**/*.html".to_string(),
pattern: "**/*.html".to_string(),
}]);

let expected = vec![(PathBuf::from("/projects"), vec!["**/*.html".to_string()])];
Expand All @@ -271,7 +271,7 @@ mod tests {
fn it_should_move_the_starting_folder_to_the_path() {
let actual = get_fast_patterns(&vec![GlobEntry {
base: "/projects".to_string(),
glob: "example/*.html".to_string(),
pattern: "example/*.html".to_string(),
}]);
let expected = vec![(
PathBuf::from("/projects/example"),
Expand All @@ -285,7 +285,7 @@ mod tests {
fn it_should_move_the_starting_folders_to_the_path() {
let actual = get_fast_patterns(&vec![GlobEntry {
base: "/projects".to_string(),
glob: "example/other/*.html".to_string(),
pattern: "example/other/*.html".to_string(),
}]);
let expected = vec![(
PathBuf::from("/projects/example/other"),
Expand All @@ -299,7 +299,7 @@ mod tests {
fn it_should_branch_expandable_folders() {
let actual = get_fast_patterns(&vec![GlobEntry {
base: "/projects".to_string(),
glob: "{foo,bar}/*.html".to_string(),
pattern: "{foo,bar}/*.html".to_string(),
}]);

let expected = vec![
Expand All @@ -314,7 +314,7 @@ mod tests {
fn it_should_expand_multiple_expansions_in_the_same_folder() {
let actual = get_fast_patterns(&vec![GlobEntry {
base: "/projects".to_string(),
glob: "a-{b,c}-d-{e,f}-g/*.html".to_string(),
pattern: "a-{b,c}-d-{e,f}-g/*.html".to_string(),
}]);
let expected = vec![
(
Expand Down Expand Up @@ -342,7 +342,7 @@ mod tests {
fn multiple_expansions_per_folder_starting_at_the_root() {
let actual = get_fast_patterns(&vec![GlobEntry {
base: "/projects".to_string(),
glob: "{a,b}-c-{d,e}-f/{b,c}-d-{e,f}-g/*.html".to_string(),
pattern: "{a,b}-c-{d,e}-f/{b,c}-d-{e,f}-g/*.html".to_string(),
}]);
let expected = vec![
(
Expand Down Expand Up @@ -418,7 +418,7 @@ mod tests {
fn it_should_stop_expanding_once_we_hit_a_wildcard() {
let actual = get_fast_patterns(&vec![GlobEntry {
base: "/projects".to_string(),
glob: "{foo,bar}/example/**/{baz,qux}/*.html".to_string(),
pattern: "{foo,bar}/example/**/{baz,qux}/*.html".to_string(),
}]);

let expected = vec![
Expand All @@ -439,7 +439,7 @@ mod tests {
fn it_should_keep_the_negation_symbol_for_all_new_patterns() {
let actual = get_fast_patterns(&vec![GlobEntry {
base: "/projects".to_string(),
glob: "!{foo,bar}/*.html".to_string(),
pattern: "!{foo,bar}/*.html".to_string(),
}]);
let expected = vec![
(PathBuf::from("/projects/foo"), vec!["!*.html".to_string()]),
Expand All @@ -453,7 +453,7 @@ mod tests {
fn it_should_expand_a_complex_example() {
let actual = get_fast_patterns(&vec![GlobEntry {
base: "/projects".to_string(),
glob: "a/{b,c}/d/{e,f}/g/*.html".to_string(),
pattern: "a/{b,c}/d/{e,f}/g/*.html".to_string(),
}]);
let expected = vec![
(
Expand Down
60 changes: 24 additions & 36 deletions crates/oxide/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::glob::path_matches_globs;
use crate::parser::Extractor;
use bstr::ByteSlice;
use cache::Cache;
Expand Down Expand Up @@ -43,9 +42,9 @@ pub struct ChangedContent {
#[derive(Debug, Clone)]
pub struct ScanOptions {
/// Base path to start scanning from
pub base: String,
/// Glob content paths
pub content_paths: Vec<GlobEntry>,
pub base: Option<String>,
/// Glob sources
pub sources: Vec<GlobEntry>,
}

#[derive(Debug, Clone)]
Expand All @@ -58,7 +57,7 @@ pub struct ScanResult {
#[derive(Debug, Clone)]
pub struct GlobEntry {
pub base: String,
pub glob: String,
pub pattern: String,
}

pub fn clear_cache() {
Expand All @@ -69,15 +68,21 @@ pub fn clear_cache() {
pub fn scan_dir(opts: ScanOptions) -> ScanResult {
init_tracing();

let base = Path::new(&opts.base);

let (mut files, dirs) = resolve_files(base);
let (mut files, mut globs) = match opts.base {
Some(base) => {
// Only enable auto content detection when `base` is provided.
let base = Path::new(&base);
let (files, dirs) = resolve_files(base);
let globs = resolve_globs(base, dirs);

let mut globs = resolve_globs(base, dirs);
(files, globs)
}
None => (vec![], vec![]),
};

// If we have additional content paths, then we have to resolve them as well.
if !opts.content_paths.is_empty() {
let resolved_files: Vec<_> = match fast_glob(&opts.content_paths) {
// If we have additional sources, then we have to resolve them as well.
if !opts.sources.is_empty() {
let resolved_files: Vec<_> = match fast_glob(&opts.sources) {
Ok(matches) => matches
.filter_map(|x| dunce::canonicalize(&x).ok())
.collect(),
Expand All @@ -89,7 +94,7 @@ pub fn scan_dir(opts: ScanOptions) -> ScanResult {

files.extend(resolved_files);

let optimized_incoming_globs = get_fast_patterns(&opts.content_paths)
let optimized_incoming_globs = get_fast_patterns(&opts.sources)
.iter()
.flat_map(|(root, globs)| {
globs.iter().filter_map(|glob| {
Expand All @@ -107,7 +112,10 @@ pub fn scan_dir(opts: ScanOptions) -> ScanResult {

let base = root.display().to_string();
let glob = glob.to_string();
Some(GlobEntry { base, glob })
Some(GlobEntry {
base,
pattern: glob,
})
})
})
.collect::<Vec<GlobEntry>>();
Expand Down Expand Up @@ -300,12 +308,12 @@ fn resolve_globs(root: &Path, dirs: Vec<PathBuf>) -> Vec<GlobEntry> {
// Build the globs for all globable directories.
let shallow_globs = shallow_globable_directories.iter().map(|path| GlobEntry {
base: path.display().to_string(),
glob: format!("*/*.{{{}}}", extension_list),
pattern: format!("*/*.{{{}}}", extension_list),
});

let deep_globs = deep_globable_directories.iter().map(|path| GlobEntry {
base: path.display().to_string(),
glob: format!("**/*.{{{}}}", extension_list),
pattern: format!("**/*.{{{}}}", extension_list),
});

shallow_globs.chain(deep_globs).collect::<Vec<_>>()
Expand Down Expand Up @@ -441,26 +449,6 @@ pub fn scan_files(input: Vec<ChangedContent>, options: u8) -> Vec<String> {
}
}

#[tracing::instrument(skip(input, globs))]
pub fn scan_files_with_globs(input: Vec<ChangedContent>, globs: Vec<GlobEntry>) -> Vec<String> {
parse_all_blobs_sync(read_all_files_sync(
input
.into_iter()
.flat_map(|c| {
// Verify that the file is actually allowed by the globs
if let Some(ref file) = c.file {
if !path_matches_globs(file, &globs) {
return None;
}
}

// ChangedContent is allowed
Some(c)
})
.collect(),
))
}

fn read_changed_content(c: ChangedContent) -> Option<Vec<u8>> {
if let Some(content) = c.content {
return Some(content.into_bytes());
Expand Down
8 changes: 4 additions & 4 deletions crates/oxide/tests/scan_dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ mod scan_dir {

// Resolve all content paths for the (temporary) current working directory
let result = scan_dir(ScanOptions {
base: base.clone(),
content_paths: globs
base: Some(base.clone()),
sources: globs
.iter()
.map(|x| GlobEntry {
base: base.clone(),
glob: x.to_string(),
pattern: x.to_string(),
})
.collect(),
});
Expand All @@ -60,7 +60,7 @@ mod scan_dir {
"{}{}{}",
glob.base,
path::MAIN_SEPARATOR,
glob.glob
glob.pattern
));
}

Expand Down
Loading