turbopack_ecmascript/references/esm/
meta.rs1use std::borrow::Cow;
2
3use anyhow::Result;
4use serde::{Deserialize, Serialize};
5use swc_core::{
6 common::DUMMY_SP,
7 ecma::ast::{Expr, Ident},
8 quote,
9};
10use turbo_tasks::{NonLocalValue, ResolvedVc, Vc, debug::ValueDebugFormat, trace::TraceRawVcs};
11use turbo_tasks_fs::FileSystemPath;
12use turbopack_core::{chunk::ChunkingContext, module_graph::ModuleGraph};
13
14use crate::{
15 code_gen::{CodeGen, CodeGeneration},
16 create_visitor, magic_identifier,
17 references::AstPath,
18 runtime_functions::TURBOPACK_RESOLVE_ABSOLUTE_PATH,
19};
20
21#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)]
29pub struct ImportMetaBinding {
30 path: ResolvedVc<FileSystemPath>,
31}
32
33impl ImportMetaBinding {
34 pub fn new(path: ResolvedVc<FileSystemPath>) -> Self {
35 ImportMetaBinding { path }
36 }
37
38 pub async fn code_generation(
39 &self,
40 _module_graph: Vc<ModuleGraph>,
41 chunking_context: Vc<Box<dyn ChunkingContext>>,
42 ) -> Result<CodeGeneration> {
43 let rel_path = chunking_context
44 .root_path()
45 .await?
46 .get_relative_path_to(&*self.path.await?);
47 let path = rel_path.map_or_else(
48 || {
49 quote!(
50 "(() => { throw new Error('could not convert import.meta.url to filepath') })()"
51 as Expr
52 )
53 },
54 |path| {
55 let formatted = encode_path(path.trim_start_matches("./")).to_string();
56 quote!(
57 "`file://${$turbopack_resolve_absolute_path($formatted)}`" as Expr,
58 turbopack_resolve_absolute_path: Expr = TURBOPACK_RESOLVE_ABSOLUTE_PATH.into(),
59 formatted: Expr = formatted.into()
60 )
61 },
62 );
63
64 Ok(CodeGeneration::hoisted_stmt(
65 "import.meta".into(),
66 quote!(
69 "const $name = { get url() { return $path } };" as Stmt,
70 name = meta_ident(),
71 path: Expr = path.clone(),
72 ),
73 ))
74 }
75}
76
77impl From<ImportMetaBinding> for CodeGen {
78 fn from(val: ImportMetaBinding) -> Self {
79 CodeGen::ImportMetaBinding(val)
80 }
81}
82
83#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)]
89pub struct ImportMetaRef {
90 ast_path: AstPath,
91}
92
93impl ImportMetaRef {
94 pub fn new(ast_path: AstPath) -> Self {
95 ImportMetaRef { ast_path }
96 }
97
98 pub async fn code_generation(
99 &self,
100 _module_graph: Vc<ModuleGraph>,
101 _chunking_context: Vc<Box<dyn ChunkingContext>>,
102 ) -> Result<CodeGeneration> {
103 let visitor = create_visitor!(self.ast_path, visit_mut_expr(expr: &mut Expr) {
104 *expr = Expr::Ident(meta_ident());
105 });
106
107 Ok(CodeGeneration::visitors(vec![visitor]))
108 }
109}
110
111impl From<ImportMetaRef> for CodeGen {
112 fn from(val: ImportMetaRef) -> Self {
113 CodeGen::ImportMetaRef(val)
114 }
115}
116
117fn encode_path(path: &'_ str) -> Cow<'_, str> {
120 let mut encoded = String::new();
121 let mut start = 0;
122 for (i, c) in path.char_indices() {
123 let mapping = match c {
124 '%' => "%25",
125 '\\' => "%5C",
126 '\n' => "%0A",
127 '\r' => "%0D",
128 '\t' => "%09",
129 _ => continue,
130 };
131
132 if encoded.is_empty() {
133 encoded.reserve(path.len());
134 }
135
136 encoded += &path[start..i];
137 encoded += mapping;
138 start = i + 1;
139 }
140
141 if encoded.is_empty() {
142 return Cow::Borrowed(path);
143 }
144 encoded += &path[start..];
145 Cow::Owned(encoded)
146}
147
148fn meta_ident() -> Ident {
149 Ident::new(
150 magic_identifier::mangle("import.meta").into(),
151 DUMMY_SP,
152 Default::default(),
153 )
154}
155
156#[cfg(test)]
157mod test {
158 use super::encode_path;
159
160 #[test]
161 fn test_encode_path_regular() {
162 let input = "abc";
163 assert_eq!(encode_path(input), "abc");
164 }
165
166 #[test]
167 fn test_encode_path_special_chars() {
168 let input = "abc%def\\ghi\njkl\rmno\tpqr";
169 assert_eq!(encode_path(input), "abc%25def%5Cghi%0Ajkl%0Dmno%09pqr");
170 }
171
172 #[test]
173 fn test_encode_path_special_char_start() {
174 let input = "%abc";
175 assert_eq!(encode_path(input), "%25abc");
176 }
177
178 #[test]
179 fn test_encode_path_special_char_end() {
180 let input = "abc%";
181 assert_eq!(encode_path(input), "abc%25");
182 }
183
184 #[test]
185 fn test_encode_path_special_char_contiguous() {
186 let input = "%%%";
187 assert_eq!(encode_path(input), "%25%25%25");
188 }
189}