|
1 | 1 | use std::{ffi::OsStr, path::PathBuf, sync::Arc, sync::mpsc};
|
2 | 2 |
|
3 |
| -use ignore::{DirEntry, overrides::Override}; |
4 |
| -use oxc_span::VALID_EXTENSIONS; |
5 |
| - |
6 |
| -// use crate::cli::IgnoreOptions; |
7 |
| - |
8 |
| -#[derive(Debug, Clone)] |
9 |
| -pub struct Extensions(pub Vec<&'static str>); |
10 |
| - |
11 |
| -impl Default for Extensions { |
12 |
| - fn default() -> Self { |
13 |
| - Self(VALID_EXTENSIONS.to_vec()) |
| 3 | +use ignore::overrides::Override; |
| 4 | + |
| 5 | +use oxc_span::SourceType; |
| 6 | + |
| 7 | +// Additional extensions from linguist-languages, which Prettier also supports |
| 8 | +// - https://github.com/ikatyang-collab/linguist-languages/blob/d1dc347c7ced0f5b42dd66c7d1c4274f64a3eb6b/data/JavaScript.js |
| 9 | +// No special extensions for TypeScript |
| 10 | +// - https://github.com/ikatyang-collab/linguist-languages/blob/d1dc347c7ced0f5b42dd66c7d1c4274f64a3eb6b/data/TypeScript.js |
| 11 | +const ADDITIONAL_JS_EXTENSIONS: &[&str] = &[ |
| 12 | + "_js", |
| 13 | + "bones", |
| 14 | + "es", |
| 15 | + "es6", |
| 16 | + "frag", |
| 17 | + "gs", |
| 18 | + "jake", |
| 19 | + "javascript", |
| 20 | + "jsb", |
| 21 | + "jscad", |
| 22 | + "jsfl", |
| 23 | + "jslib", |
| 24 | + "jsm", |
| 25 | + "jspre", |
| 26 | + "jss", |
| 27 | + "njs", |
| 28 | + "pac", |
| 29 | + "sjs", |
| 30 | + "ssjs", |
| 31 | + "xsjs", |
| 32 | + "xsjslib", |
| 33 | +]; |
| 34 | + |
| 35 | +fn is_supported_source_type(path: &std::path::Path) -> Option<SourceType> { |
| 36 | + let extension = path.extension()?.to_string_lossy(); |
| 37 | + |
| 38 | + // Standard extensions, also supported by `oxc_span::VALID_EXTENSIONS` |
| 39 | + if let Ok(source_type) = SourceType::from_extension(&extension) { |
| 40 | + return Some(source_type); |
14 | 41 | }
|
| 42 | + // Additional extensions from linguist-languages, which Prettier also supports |
| 43 | + if ADDITIONAL_JS_EXTENSIONS.contains(&extension.as_ref()) { |
| 44 | + return Some(SourceType::default()); |
| 45 | + } |
| 46 | + // `Jakefile` has no extension but is a valid JS file defined by linguist-languages |
| 47 | + if path.file_name() == Some(OsStr::new("Jakefile")) { |
| 48 | + return Some(SourceType::default()); |
| 49 | + } |
| 50 | + |
| 51 | + None |
15 | 52 | }
|
16 | 53 |
|
| 54 | +// --- |
| 55 | + |
17 | 56 | pub struct Walk {
|
18 | 57 | inner: ignore::WalkParallel,
|
19 |
| - /// The file extensions to include during the traversal. |
20 |
| - extensions: Extensions, |
| 58 | +} |
| 59 | + |
| 60 | +pub struct WalkEntry { |
| 61 | + pub path: Arc<OsStr>, |
| 62 | + pub source_type: SourceType, |
21 | 63 | }
|
22 | 64 |
|
23 | 65 | struct WalkBuilder {
|
24 |
| - sender: mpsc::Sender<Vec<Arc<OsStr>>>, |
25 |
| - extensions: Extensions, |
| 66 | + sender: mpsc::Sender<Vec<WalkEntry>>, |
26 | 67 | }
|
27 | 68 |
|
28 | 69 | impl<'s> ignore::ParallelVisitorBuilder<'s> for WalkBuilder {
|
29 | 70 | fn build(&mut self) -> Box<dyn ignore::ParallelVisitor + 's> {
|
30 |
| - Box::new(WalkCollector { |
31 |
| - paths: vec![], |
32 |
| - sender: self.sender.clone(), |
33 |
| - extensions: self.extensions.clone(), |
34 |
| - }) |
| 71 | + Box::new(WalkCollector { entries: vec![], sender: self.sender.clone() }) |
35 | 72 | }
|
36 | 73 | }
|
37 | 74 |
|
38 | 75 | struct WalkCollector {
|
39 |
| - paths: Vec<Arc<OsStr>>, |
40 |
| - sender: mpsc::Sender<Vec<Arc<OsStr>>>, |
41 |
| - extensions: Extensions, |
| 76 | + entries: Vec<WalkEntry>, |
| 77 | + sender: mpsc::Sender<Vec<WalkEntry>>, |
42 | 78 | }
|
43 | 79 |
|
44 | 80 | impl Drop for WalkCollector {
|
45 | 81 | fn drop(&mut self) {
|
46 |
| - let paths = std::mem::take(&mut self.paths); |
47 |
| - self.sender.send(paths).unwrap(); |
| 82 | + let entries = std::mem::take(&mut self.entries); |
| 83 | + self.sender.send(entries).unwrap(); |
48 | 84 | }
|
49 | 85 | }
|
50 | 86 |
|
51 | 87 | impl ignore::ParallelVisitor for WalkCollector {
|
52 | 88 | fn visit(&mut self, entry: Result<ignore::DirEntry, ignore::Error>) -> ignore::WalkState {
|
53 | 89 | match entry {
|
54 | 90 | Ok(entry) => {
|
55 |
| - if Walk::is_wanted_entry(&entry, &self.extensions) { |
56 |
| - self.paths.push(entry.path().as_os_str().into()); |
| 91 | + // Skip if we can't get file type or if it's a directory |
| 92 | + if let Some(file_type) = entry.file_type() { |
| 93 | + if !file_type.is_dir() { |
| 94 | + if let Some(source_type) = is_supported_source_type(entry.path()) { |
| 95 | + self.entries.push(WalkEntry { |
| 96 | + path: entry.path().as_os_str().into(), |
| 97 | + source_type, |
| 98 | + }); |
| 99 | + } |
| 100 | + } |
57 | 101 | }
|
58 | 102 | ignore::WalkState::Continue
|
59 | 103 | }
|
@@ -87,24 +131,14 @@ impl Walk {
|
87 | 131 | // Do not follow symlinks like Prettier does.
|
88 | 132 | // See https://github.com/prettier/prettier/pull/14627
|
89 | 133 | let inner = inner.hidden(false).ignore(false).git_global(false).build_parallel();
|
90 |
| - Self { inner, extensions: Extensions::default() } |
| 134 | + Self { inner } |
91 | 135 | }
|
92 | 136 |
|
93 |
| - pub fn paths(self) -> Vec<Arc<OsStr>> { |
94 |
| - let (sender, receiver) = mpsc::channel::<Vec<Arc<OsStr>>>(); |
95 |
| - let mut builder = WalkBuilder { sender, extensions: self.extensions }; |
| 137 | + pub fn entries(self) -> Vec<WalkEntry> { |
| 138 | + let (sender, receiver) = mpsc::channel::<Vec<WalkEntry>>(); |
| 139 | + let mut builder = WalkBuilder { sender }; |
96 | 140 | self.inner.visit(&mut builder);
|
97 | 141 | drop(builder);
|
98 | 142 | receiver.into_iter().flatten().collect()
|
99 | 143 | }
|
100 |
| - |
101 |
| - fn is_wanted_entry(dir_entry: &DirEntry, extensions: &Extensions) -> bool { |
102 |
| - let Some(file_type) = dir_entry.file_type() else { return false }; |
103 |
| - if file_type.is_dir() { |
104 |
| - return false; |
105 |
| - } |
106 |
| - let Some(extension) = dir_entry.path().extension() else { return false }; |
107 |
| - let extension = extension.to_string_lossy(); |
108 |
| - extensions.0.contains(&extension.as_ref()) |
109 |
| - } |
110 | 144 | }
|
0 commit comments