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