tailwindcss/crates/node/src/lib.rs
Robin Malfait a902128640
Improve Oxide scanner API (#14187)
This PR updates the API for interacting with the Oxide API. Until now,
we used the name `scanDir(…)` which is fine, but we do way more work
right now.

We now have features such as:

1. Auto source detection (can be turned off, e.g.: `@tailwindcss/vite`
doesn't need it)
2. Scan based on `@source`s found in CSS files
3. Do "incremental" rebuilds (which means that the `scanDir(…)` result
was stateful).

To solve these issues, this PR introduces a new `Scanner` class where
you can pass in the `detectSources` and `sources` options. E.g.:

```ts
let scanner = new Scanner({
  // Optional, omitting `detectSources` field disables automatic source detection
  detectSources: { base: __dirname }, 

  // List of glob entries to scan. These come from `@source` directives in CSS.
  sources: [
    { base: __dirname, pattern: "src/**/*.css" },
    // …
  ],
});
```

The scanner object has the following API:

```ts
export interface ChangedContent {
  /** File path to the changed file */
  file?: string
  /** Contents of the changed file */
  content?: string
  /** File extension */
  extension: string
}
export interface DetectSources {
  /** Base path to start scanning from */
  base: string
}
export interface GlobEntry {
  /** Base path of the glob */
  base: string
  /** Glob pattern */
  pattern: string
}
export interface ScannerOptions {
  /** Automatically detect sources in the base path */
  detectSources?: DetectSources
  /** Glob sources */
  sources?: Array<GlobEntry>
}
export declare class Scanner {
  constructor(opts: ScannerOptions)
  scan(): Array<string>
  scanFiles(input: Array<ChangedContent>): Array<string>
  get files(): Array<string>
  get globs(): Array<GlobEntry>
}
```

The `scanFiles(…)` method is used for incremental rebuilds. It takes the
`ChangedContent` array for all the new/changes files. It returns whether
we scanned any new candidates or not.

Note that the `scanner` object is stateful, this means that we don't
have to track candidates in a `Set` anymore. We can just call
`getCandidates()` when we need it.

This PR also removed some unused code that we had in the `scanDir(…)`
function to allow for sequential or parallel `IO`, and sequential or
parallel `Parsing`. We only used the same `IO` and `Parsing` strategies
for all files, so I just got rid of it.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-08-16 15:05:42 +02:00

125 lines
2.5 KiB
Rust

#[macro_use]
extern crate napi_derive;
#[derive(Debug, Clone)]
#[napi(object)]
pub struct ChangedContent {
/// File path to the changed file
pub file: Option<String>,
/// Contents of the changed file
pub content: Option<String>,
/// File extension
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 {
/// Base path of the glob
pub base: String,
/// Glob pattern
pub pattern: String,
}
impl From<ChangedContent> for tailwindcss_oxide::ChangedContent {
fn from(changed_content: ChangedContent) -> Self {
Self {
file: changed_content.file.map(Into::into),
content: changed_content.content,
}
}
}
impl From<GlobEntry> for tailwindcss_oxide::GlobEntry {
fn from(glob: GlobEntry) -> Self {
Self {
base: glob.base,
pattern: glob.pattern,
}
}
}
impl From<tailwindcss_oxide::GlobEntry> for GlobEntry {
fn from(glob: tailwindcss_oxide::GlobEntry) -> Self {
Self {
base: glob.base,
pattern: glob.pattern,
}
}
}
impl From<DetectSources> 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<DetectSources>,
/// Glob sources
pub sources: Option<Vec<GlobEntry>>,
}
#[derive(Debug, Clone)]
#[napi]
pub struct Scanner {
scanner: tailwindcss_oxide::Scanner,
}
#[napi]
impl Scanner {
#[napi(constructor)]
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()),
),
}
}
#[napi]
pub fn scan(&mut self) -> Vec<String> {
self.scanner.scan()
}
#[napi]
pub fn scan_files(&mut self, input: Vec<ChangedContent>) -> Vec<String> {
self
.scanner
.scan_content(input.into_iter().map(Into::into).collect())
}
#[napi(getter)]
pub fn files(&mut self) -> Vec<String> {
self.scanner.get_files()
}
#[napi(getter)]
pub fn globs(&mut self) -> Vec<GlobEntry> {
self
.scanner
.get_globs()
.into_iter()
.map(Into::into)
.collect()
}
}