Cheap import scanner: regex-free, picks up `import X` and `from X import …` at the top level of a line.
(scripts: &[PathBuf])
| 124 | |
| 125 | /// Cheap import scanner: regex-free, picks up `import X` and `from X import …` at the top level of a line. |
| 126 | fn crawl_imports(scripts: &[PathBuf]) -> BTreeSet<String> { |
| 127 | let mut imports = BTreeSet::new(); |
| 128 | for path in scripts { |
| 129 | let Ok(text) = fs::read_to_string(path) else { continue }; |
| 130 | for line in text.lines() { |
| 131 | let line = line.trim(); |
| 132 | let rest = if let Some(r) = line.strip_prefix("from ") { |
| 133 | r |
| 134 | } else if let Some(r) = line.strip_prefix("import ") { |
| 135 | r |
| 136 | } else { |
| 137 | continue; |
| 138 | }; |
| 139 | for tok in rest.split(|c: char| c == ',' || c.is_whitespace()) { |
| 140 | if tok.is_empty() { continue; } |
| 141 | let name = tok.split('.').next().unwrap_or(tok); |
| 142 | if !name.is_empty() { imports.insert(name.to_string()); } |
| 143 | break; // first token after `import`/`from` is the module |
| 144 | } |
| 145 | } |
| 146 | } |
| 147 | imports |
| 148 | } |
| 149 | |
| 150 | /// For each imported name, resolve via the shared registry, fetch, and stash under dist/vendor/. |
| 151 | fn vendor_packages( |