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_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(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)]
30pub struct ImportMetaBinding {
31 path: FileSystemPath,
32}
33
34impl ImportMetaBinding {
35 pub fn new(path: FileSystemPath) -> Self {
36 ImportMetaBinding { path }
37 }
38
39 pub async fn code_generation(
40 &self,
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);
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 rcstr!("import.meta"),
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 _chunking_context: Vc<Box<dyn ChunkingContext>>,
101 ) -> Result<CodeGeneration> {
102 let visitor = create_visitor!(self.ast_path, visit_mut_expr, |expr: &mut Expr| {
103 *expr = Expr::Ident(meta_ident());
104 });
105
106 Ok(CodeGeneration::visitors(vec![visitor]))
107 }
108}
109
110impl From<ImportMetaRef> for CodeGen {
111 fn from(val: ImportMetaRef) -> Self {
112 CodeGen::ImportMetaRef(val)
113 }
114}
115
116fn encode_path(path: &'_ str) -> Cow<'_, str> {
119 let mut encoded = String::new();
120 let mut start = 0;
121 for (i, c) in path.char_indices() {
122 let mapping = match c {
123 '%' => "%25",
124 '\\' => "%5C",
125 '\n' => "%0A",
126 '\r' => "%0D",
127 '\t' => "%09",
128 _ => continue,
129 };
130
131 if encoded.is_empty() {
132 encoded.reserve(path.len());
133 }
134
135 encoded += &path[start..i];
136 encoded += mapping;
137 start = i + 1;
138 }
139
140 if encoded.is_empty() {
141 return Cow::Borrowed(path);
142 }
143 encoded += &path[start..];
144 Cow::Owned(encoded)
145}
146
147fn meta_ident() -> Ident {
148 Ident::new(
149 magic_identifier::mangle("import.meta").into(),
150 DUMMY_SP,
151 Default::default(),
152 )
153}
154
155#[cfg(test)]
156mod test {
157 use super::encode_path;
158
159 #[test]
160 fn test_encode_path_regular() {
161 let input = "abc";
162 assert_eq!(encode_path(input), "abc");
163 }
164
165 #[test]
166 fn test_encode_path_special_chars() {
167 let input = "abc%def\\ghi\njkl\rmno\tpqr";
168 assert_eq!(encode_path(input), "abc%25def%5Cghi%0Ajkl%0Dmno%09pqr");
169 }
170
171 #[test]
172 fn test_encode_path_special_char_start() {
173 let input = "%abc";
174 assert_eq!(encode_path(input), "%25abc");
175 }
176
177 #[test]
178 fn test_encode_path_special_char_end() {
179 let input = "abc%";
180 assert_eq!(encode_path(input), "abc%25");
181 }
182
183 #[test]
184 fn test_encode_path_special_char_contiguous() {
185 let input = "%%%";
186 assert_eq!(encode_path(input), "%25%25%25");
187 }
188}