Home / Function/ create_walker() — tailwindcss Function Reference

create_walker() — tailwindcss Function Reference

Architecture documentation for the create_walker() function in mod.rs from the tailwindcss codebase.

Entity Profile

Dependency Diagram

graph TD
  f8682733_9224_5872_cde2_2e9fbb1e34a0["create_walker()"]
  4db59ffb_dcea_b3bb_bddb_1c20bbe5b635["new()"]
  4db59ffb_dcea_b3bb_bddb_1c20bbe5b635 -->|calls| f8682733_9224_5872_cde2_2e9fbb1e34a0
  style f8682733_9224_5872_cde2_2e9fbb1e34a0 fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

crates/oxide/src/scanner/mod.rs lines 571–792

fn create_walker(sources: Sources) -> Option<WalkBuilder> {
    let mtimes: Arc<Mutex<FxHashMap<PathBuf, SystemTime>>> = Default::default();
    let mut other_roots: FxHashSet<&PathBuf> = FxHashSet::default();
    let mut first_root: Option<&PathBuf> = None;
    let mut ignores: BTreeMap<&PathBuf, BTreeSet<String>> = Default::default();

    for source in sources.iter() {
        match source {
            SourceEntry::Auto { base } => {
                if first_root.is_none() {
                    first_root = Some(base);
                } else {
                    other_roots.insert(base);
                }
            }
            SourceEntry::Pattern { base, pattern } => {
                let mut pattern = pattern.to_string();

                if first_root.is_none() {
                    first_root = Some(base);
                } else {
                    other_roots.insert(base);
                }

                if !pattern.contains("**") {
                    // Ensure that the pattern is pinned to the base path.
                    if !pattern.starts_with("/") {
                        pattern = format!("/{pattern}");
                    }

                    // Specific patterns should take precedence even over git-ignored files:
                    ignores
                        .entry(base)
                        .or_default()
                        .insert(format!("!{}", pattern));
                } else {
                    // Assumption: the pattern we receive will already be brace expanded. So
                    // `*.{html,jsx}` will result in two separate patterns: `*.html` and `*.jsx`.
                    if let Some(extension) = Path::new(&pattern).extension() {
                        // Extend auto source detection to include the extension
                        ignores
                            .entry(base)
                            .or_default()
                            .insert(format!("!*.{}", extension.to_string_lossy()));
                    }
                }
            }
            SourceEntry::Ignored { base, pattern } => {
                let mut pattern = pattern.to_string();
                // Ensure that the pattern is pinned to the base path.
                if !pattern.starts_with("/") {
                    pattern = format!("/{pattern}");
                }
                ignores.entry(base).or_default().insert(pattern);
            }
            SourceEntry::External { base } => {
                if first_root.is_none() {
                    first_root = Some(base);
                } else {
                    other_roots.insert(base);
                }

                // External sources should take precedence even over git-ignored files:
                ignores
                    .entry(base)
                    .or_default()
                    .insert(format!("!{}", "/**/*"));

                // External sources should still disallow binary extensions:
                ignores
                    .entry(base)
                    .or_default()
                    .insert(BINARY_EXTENSIONS_GLOB.clone());
            }
        }
    }

    let mut builder = WalkBuilder::new(first_root?);

    // We have to follow symlinks
    builder.follow_links(true);

    // Scan hidden files / directories
    builder.hidden(false);

    // Don't respect global gitignore files
    builder.git_global(false);

    // By default, allow .gitignore files to be used regardless of whether or not
    // a .git directory is present. This is an optimization for when projects
    // are first created and may not be in a git repo yet.
    builder.require_git(false);

    // If we are in a git repo then require it to ensure that only rules within
    // the repo are used. For example, we don't want to consider a .gitignore file
    // in the user's home folder if we're in a git repo.
    //
    // The alternative is using a call like `.parents(false)` but that will
    // prevent looking at parent directories for .gitignore files from within
    // the repo and that's not what we want.
    //
    // For example, in a project with this structure:
    //
    // home
    // .gitignore
    //  my-project
    //   .gitignore
    //   apps
    //     .gitignore
    //     web
    //       {root}
    //
    // We do want to consider all .gitignore files listed:
    // - home/.gitignore
    // - my-project/.gitignore
    // - my-project/apps/.gitignore
    //
    // However, if a repo is initialized inside my-project then only the following
    // make sense for consideration:
    // - my-project/.gitignore
    // - my-project/apps/.gitignore
    //
    // Setting the require_git(true) flag conditionally allows us to do this.
    for parent in first_root?.ancestors() {
        if parent.join(".git").exists() {
            builder.require_git(true);
            break;
        }
    }

    for root in other_roots {
        builder.add(root);
    }

    // Setup auto source detection rules
    for ignore in auto_source_detection::RULES.iter() {
        builder.add_gitignore(ignore.clone());
    }

    // Setup ignores based on `@source` definitions
    for (base, patterns) in ignores {
        let mut ignore_builder = GitignoreBuilder::new(base);
        for pattern in patterns {
            ignore_builder.add_line(None, &pattern).unwrap();
        }
        let ignore = ignore_builder.build().unwrap();
        builder.add_gitignore(ignore);
    }

    builder.filter_entry({
        move |entry| {
            let path = entry.path();

            // Ensure the entries are matching any of the provided source patterns (this is
            // necessary for manual-patterns that can filter the file extension)
            if path.is_file() {
                let mut matches = false;
                for source in sources.iter() {
                    match source {
                        SourceEntry::Auto { base } | SourceEntry::External { base } => {
                            if path.starts_with(base) {
                                matches = true;
                                break;
                            }
                        }
                        SourceEntry::Pattern { base, pattern } => {
                            let mut pattern = pattern.to_string();
                            // Ensure that the pattern is pinned to the base path.
                            if !pattern.starts_with("/") {
                                pattern = format!("/{pattern}");
                            }

                            // Check if path starts with base, if so, remove the prefix and check the remainder against the pattern
                            let remainder = path.strip_prefix(base);
                            if remainder.is_ok_and(|remainder| {
                                let mut path_str = remainder.to_string_lossy().to_string();
                                if !path_str.starts_with("/") {
                                    path_str = format!("/{path_str}");
                                }
                                glob_match(pattern, path_str.as_bytes())
                            }) {
                                matches = true;
                                break;
                            }
                        }
                        _ => {}
                    }
                }

                if !matches {
                    return false;
                }
            }

            let mut mtimes = mtimes.lock().unwrap();
            let current_time = match entry.metadata() {
                Ok(metadata) if metadata.is_file() => {
                    if let Ok(time) = metadata.modified() {
                        Some(time)
                    } else {
                        None
                    }
                }
                _ => None,
            };

            let previous_time =
                current_time.and_then(|time| mtimes.insert(entry.clone().into_path(), time));

            match (current_time, previous_time) {
                (Some(current), Some(prev)) if prev == current => false,
                _ => {
                    event!(tracing::Level::INFO, "Discovering {:?}", path);

                    true
                }
            }
        }
    });

    Some(builder)
}

Domain

Subdomains

Called By

Frequently Asked Questions

What does create_walker() do?
create_walker() is a function in the tailwindcss codebase.
What calls create_walker()?
create_walker() is called by 1 function(s): new.

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free