swc_ecma_preset_env/
node_colon_prefix_strip.rs

1use swc_common::{Mark, SyntaxContext};
2use swc_ecma_ast::*;
3use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};
4
5pub fn strip_node_colon_prefix(unresolved_mark: Mark) -> impl Pass {
6    visit_mut_pass(NodeColonPrefixStrip {
7        unresolved_ctxt: SyntaxContext::empty().apply_mark(unresolved_mark),
8    })
9}
10
11struct NodeColonPrefixStrip {
12    unresolved_ctxt: SyntaxContext,
13}
14
15impl VisitMut for NodeColonPrefixStrip {
16    noop_visit_mut_type!();
17
18    fn visit_mut_import_decl(&mut self, node: &mut ImportDecl) {
19        if let Some(value) = Self::strip_node_colon(&node.src.value.to_string_lossy()) {
20            node.src.value = value.into();
21            node.src.raw = None;
22        }
23    }
24
25    fn visit_mut_named_export(&mut self, node: &mut NamedExport) {
26        let Some(src) = &mut node.src else {
27            return;
28        };
29        if let Some(value) = Self::strip_node_colon(&src.value.to_string_lossy()) {
30            src.value = value.into();
31            src.raw = None;
32        }
33    }
34
35    fn visit_mut_export_all(&mut self, node: &mut ExportAll) {
36        if let Some(value) = Self::strip_node_colon(&node.src.value.to_string_lossy()) {
37            node.src.value = value.into();
38            node.src.raw = None;
39        }
40    }
41
42    fn visit_mut_call_expr(&mut self, node: &mut CallExpr) {
43        node.visit_mut_children_with(self);
44
45        match &node.callee {
46            Callee::Import(..) => {}
47            Callee::Expr(expr)
48                if expr
49                    .as_ident()
50                    .is_some_and(|id| id.sym == "require" && id.ctxt == self.unresolved_ctxt) => {}
51            _ => return,
52        }
53
54        if let Some(source) = node
55            .args
56            .first_mut()
57            .and_then(|arg| arg.expr.as_mut_lit())
58            .and_then(|lit| lit.as_mut_str())
59        {
60            if let Some(value) = Self::strip_node_colon(&source.value.to_string_lossy()) {
61                source.value = value.into();
62                source.raw = None;
63            }
64        }
65    }
66}
67
68impl NodeColonPrefixStrip {
69    /// Only Strip known `node:` prefixes.
70    /// There are some modules only available in the `node:` namespace which we
71    /// should not strip.
72    ///
73    /// You can use the following code to generate this list:
74    /// ```JavaScript
75    /// require("node:module").builtinModules.filter((m) => !m.startsWith("node:"))
76    /// ```
77    fn strip_node_colon(value: &str) -> Option<&str> {
78        value.strip_prefix("node:").filter(|value| {
79            matches!(
80                *value,
81                "_http_agent"
82                    | "_http_client"
83                    | "_http_common"
84                    | "_http_incoming"
85                    | "_http_outgoing"
86                    | "_http_server"
87                    | "_stream_duplex"
88                    | "_stream_passthrough"
89                    | "_stream_readable"
90                    | "_stream_transform"
91                    | "_stream_wrap"
92                    | "_stream_writable"
93                    | "_tls_common"
94                    | "_tls_wrap"
95                    | "assert"
96                    | "assert/strict"
97                    | "async_hooks"
98                    | "buffer"
99                    | "child_process"
100                    | "cluster"
101                    | "console"
102                    | "constants"
103                    | "crypto"
104                    | "dgram"
105                    | "diagnostics_channel"
106                    | "dns"
107                    | "dns/promises"
108                    | "domain"
109                    | "events"
110                    | "fs"
111                    | "fs/promises"
112                    | "http"
113                    | "http2"
114                    | "https"
115                    | "inspector"
116                    | "inspector/promises"
117                    | "module"
118                    | "net"
119                    | "os"
120                    | "path"
121                    | "path/posix"
122                    | "path/win32"
123                    | "perf_hooks"
124                    | "process"
125                    | "punycode"
126                    | "querystring"
127                    | "readline"
128                    | "readline/promises"
129                    | "repl"
130                    | "stream"
131                    | "stream/consumers"
132                    | "stream/promises"
133                    | "stream/web"
134                    | "string_decoder"
135                    | "sys"
136                    | "timers"
137                    | "timers/promises"
138                    | "tls"
139                    | "trace_events"
140                    | "tty"
141                    | "url"
142                    | "util"
143                    | "util/types"
144                    | "v8"
145                    | "vm"
146                    | "wasi"
147                    | "worker_threads"
148                    | "zlib"
149            )
150        })
151    }
152}