use utf16::IndexConverter; #[macro_use] extern crate napi_derive; mod utf16; #[derive(Debug, Clone)] #[napi(object)] pub struct ChangedContent { /// File path to the changed file pub file: Option, /// Contents of the changed file pub content: Option, /// File extension pub extension: String, } #[derive(Debug, Clone)] #[napi(object)] pub struct GlobEntry { /// Base path of the glob pub base: String, /// Glob pattern pub pattern: String, } #[derive(Debug, Clone)] #[napi(object)] pub struct SourceEntry { /// Base path of the glob pub base: String, /// Glob pattern pub pattern: String, /// Negated flag pub negated: bool, } impl From for tailwindcss_oxide::ChangedContent { fn from(changed_content: ChangedContent) -> Self { if let Some(file) = changed_content.file { return tailwindcss_oxide::ChangedContent::File(file.into(), changed_content.extension); } if let Some(contents) = changed_content.content { return tailwindcss_oxide::ChangedContent::Content(contents, changed_content.extension); } unreachable!() } } impl From for tailwindcss_oxide::GlobEntry { fn from(glob: GlobEntry) -> Self { Self { base: glob.base, pattern: glob.pattern, } } } impl From for GlobEntry { fn from(glob: tailwindcss_oxide::GlobEntry) -> Self { Self { base: glob.base, pattern: glob.pattern, } } } impl From for tailwindcss_oxide::PublicSourceEntry { fn from(source: SourceEntry) -> Self { Self { base: source.base, pattern: source.pattern, negated: source.negated, } } } // --- #[derive(Debug, Clone)] #[napi(object)] pub struct ScannerOptions { /// Glob sources pub sources: Option>, } #[derive(Debug, Clone)] #[napi] pub struct Scanner { scanner: tailwindcss_oxide::Scanner, } #[derive(Debug, Clone)] #[napi(object)] pub struct CandidateWithPosition { /// The candidate string pub candidate: String, /// The position of the candidate inside the content file pub position: i64, } #[napi] impl Scanner { #[napi(constructor)] pub fn new(opts: ScannerOptions) -> Self { Self { scanner: tailwindcss_oxide::Scanner::new(match opts.sources { Some(sources) => sources.into_iter().map(Into::into).collect(), None => vec![], }), } } #[napi] pub fn scan(&mut self) -> Vec { self.scanner.scan() } #[napi] pub fn scan_files(&mut self, input: Vec) -> Vec { self .scanner .scan_content(input.into_iter().map(Into::into).collect()) } #[napi] pub fn get_candidates_with_positions( &mut self, input: ChangedContent, ) -> Vec { let content = input.content.unwrap_or_else(|| { std::fs::read_to_string(input.file.unwrap()).expect("Failed to read file") }); let input = ChangedContent { file: None, content: Some(content.clone()), extension: input.extension, }; let mut utf16_idx = IndexConverter::new(&content[..]); self .scanner .get_candidates_with_positions(input.into()) .into_iter() .map(|(candidate, position)| CandidateWithPosition { candidate, position: utf16_idx.get(position), }) .collect() } #[napi(getter)] pub fn files(&mut self) -> Vec { self.scanner.get_files() } #[napi(getter)] pub fn globs(&mut self) -> Vec { self .scanner .get_globs() .into_iter() .map(Into::into) .collect() } #[napi(getter)] pub fn normalized_sources(&mut self) -> Vec { self .scanner .get_normalized_sources() .into_iter() .map(Into::into) .collect() } }