dmc_transform/builtin/
npm_command.rs1use crate::pipeline::Transformer;
2use crate::visit::{NodeAction, Visitor, walk_root};
3use dmc_diagnostic::Code;
4use dmc_diagnostic::metadata::SourceMeta;
5use dmc_parser::ast::*;
6
7#[derive(Default)]
11pub struct NpmCommand;
12
13impl Transformer for NpmCommand {
14 fn name(&self) -> &str {
15 "npm-command"
16 }
17
18 fn transform(
19 &self,
20 doc: &mut Document,
21 _meta: &SourceMeta,
22 _diag_engine: &mut duck_diagnostic::DiagnosticEngine<Code>,
23 ) {
24 let mut v = Apply;
25 walk_root(&mut doc.children, &mut v);
26 }
27}
28
29struct Apply;
30
31impl NpmCommand {
32 fn derive(value: &str) -> Option<[(&'static str, String); 4]> {
34 let line = value.lines().next()?.trim();
35 if let Some(rest) = line.strip_prefix("npm install") {
36 let pkgs = rest.trim();
37 return Some([
38 ("npm", format!("npm install {}", pkgs)),
39 ("yarn", format!("yarn add {}", pkgs)),
40 ("pnpm", format!("pnpm add {}", pkgs)),
41 ("bun", format!("bun add {}", pkgs)),
42 ]);
43 }
44 if let Some(rest) = line.strip_prefix("npx create-") {
45 let rest = rest.trim();
46 return Some([
47 ("npm", format!("npx create-{rest}")),
48 ("yarn", format!("yarn create {rest}")),
49 ("pnpm", format!("pnpm create {rest}")),
50 ("bun", format!("bunx create-{rest}")),
51 ]);
52 }
53 if let Some(rest) = line.strip_prefix("npx ") {
54 let rest = rest.trim();
55 return Some([
56 ("npm", format!("npx {rest}")),
57 ("yarn", format!("yarn run {rest}")),
58 ("pnpm", format!("pnpm run {rest}")),
59 ("bun", format!("bunx {rest}")),
60 ]);
61 }
62 None
63 }
64}
65
66impl Visitor for Apply {
67 fn visit_node(&mut self, node: &mut Node) -> NodeAction {
68 let Node::CodeBlock(cb) = node else { return NodeAction::Keep };
69 let Some(variants) = NpmCommand::derive(&cb.value) else { return NodeAction::Keep };
70 let span = cb.span.clone();
71
72 let attrs: Vec<JsxAttr> = variants
73 .iter()
74 .map(|(name, value)| JsxAttr {
75 name: name.to_string(),
76 value: JsxAttrValue::String(value.to_string()),
77 span: span.clone(),
78 })
79 .collect();
80
81 let jsx = Node::JsxSelfClosing(JsxSelfClosing { name: "PackageManagerTabs".to_string(), attrs, span });
82
83 NodeAction::Replace(vec![jsx])
84 }
85}