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>
125 lines
2.5 KiB
Rust
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()
|
|
}
|
|
}
|