1use std::{borrow::Cow, sync::Arc};
2
3use anyhow::{Result, bail};
4use bincode::{Decode, Encode};
5use swc_core::{
6 common::{
7 DUMMY_SP, Span,
8 errors::{DiagnosticId, Handler},
9 },
10 ecma::{
11 ast::{
12 Expr, ExprStmt, KeyValueProp, Lit, ModuleItem, ObjectLit, Prop, PropName, PropOrSpread,
13 Stmt, {self},
14 },
15 codegen::{Emitter, text_writer::JsWriter},
16 },
17 quote, quote_expr,
18};
19use turbo_rcstr::{RcStr, rcstr};
20use turbo_tasks::{
21 FxIndexMap, NonLocalValue, ResolvedVc, TryJoinIterExt, ValueToString, Vc,
22 debug::ValueDebugFormat, trace::TraceRawVcs,
23};
24use turbo_tasks_fs::{
25 DirectoryEntry, FileSystemPath, ReadGlobResult,
26 glob::{Glob, GlobOptions},
27};
28use turbopack_core::{
29 chunk::{
30 AsyncModuleInfo, ChunkableModule, ChunkingContext, ChunkingType, MinifyType,
31 ModuleChunkItemIdExt,
32 },
33 ident::AssetIdent,
34 issue::{IssueExt, IssueSeverity, IssueSource, StyledString, code_gen::CodeGenerationIssue},
35 module::{Module, ModuleSideEffects},
36 module_graph::ModuleGraph,
37 reference::{ModuleReference, ModuleReferences},
38 reference_type::EcmaScriptModulesReferenceSubType,
39 resolve::{
40 BindingUsage, ExportUsage, ModuleResolveResult, ResolveErrorMode, origin::ResolveOrigin,
41 parse::Request,
42 },
43};
44use turbopack_resolve::ecmascript::esm_resolve;
45
46use crate::{
47 EcmascriptChunkPlaceable,
48 analyzer::JsValue,
49 chunk::{EcmascriptChunkItemContent, EcmascriptExports, ecmascript_chunk_item},
50 code_gen::{CodeGen, CodeGeneration, IntoCodeGenReference},
51 create_visitor,
52 references::{
53 AstPath,
54 pattern_mapping::{PatternMapping, ResolveType},
55 },
56 runtime_functions::{TURBOPACK_EXPORT_VALUE, TURBOPACK_REQUIRE},
57 utils::module_id_to_lit,
58};
59
60#[derive(Debug, Clone)]
66pub struct ImportMetaGlobOptions {
67 pub patterns: Vec<RcStr>,
69 pub eager: bool,
71 pub import: Option<RcStr>,
73 pub query: Option<RcStr>,
75 pub base: Option<RcStr>,
77 pub case_sensitive: bool,
79}
80
81pub fn parse_import_meta_glob(
93 args: &[JsValue<'_>],
94 handler: &Handler,
95 span: Span,
96 diagnostic_id: DiagnosticId,
97) -> Option<ImportMetaGlobOptions> {
98 if args.is_empty() || args.len() > 2 {
99 handler.span_warn_with_code(
100 span,
101 "import.meta.glob() requires 1 or 2 arguments",
102 diagnostic_id,
103 );
104 return None;
105 }
106
107 let patterns = {
109 let mut pats = Vec::new();
110 match &args[0] {
111 JsValue::Array { items, .. } => {
112 for item in items {
113 if let Some(s) = item.as_str() {
114 pats.push(s.into());
115 } else {
116 handler.span_warn_with_code(
117 span,
118 "import.meta.glob() pattern array elements must be constant strings",
119 diagnostic_id,
120 );
121 return None;
122 }
123 }
124 if pats.is_empty() {
125 handler.span_warn_with_code(
126 span,
127 "import.meta.glob() requires at least one pattern",
128 diagnostic_id,
129 );
130 return None;
131 }
132 }
133 _ => {
134 if let Some(s) = args[0].as_str() {
135 pats.push(s.into());
136 } else {
137 handler.span_warn_with_code(
138 span,
139 "import.meta.glob() first argument must be a string literal or array of \
140 string literals",
141 diagnostic_id,
142 );
143 return None;
144 }
145 }
146 }
147 pats
148 };
149
150 let mut eager = false;
152 let mut import = None;
153 let mut query = None;
154 let mut base = None;
155 let mut case_sensitive = true;
156
157 if let Some(opts) = args.get(1) {
158 match opts {
159 JsValue::Object { parts, .. } => {
160 use crate::analyzer::ObjectPart;
161 for part in parts {
162 if let ObjectPart::KeyValue(key, val) = part {
163 match key.as_str() {
164 Some("eager") => {
165 if let Some(b) = val.as_bool() {
166 eager = b;
167 } else {
168 handler.span_warn_with_code(
169 span,
170 "import.meta.glob() 'eager' option must be a constant \
171 boolean (true or false), defaulting to false",
172 diagnostic_id.clone(),
173 );
174 }
175 }
176 Some("import") => {
177 if let Some(s) = val.as_str() {
178 if s != "*" {
181 import = Some(s.into());
182 }
183 } else {
184 handler.span_warn_with_code(
185 span,
186 "import.meta.glob() 'import' option must be a constant \
187 string, ignoring",
188 diagnostic_id.clone(),
189 );
190 }
191 }
192 Some("query") => {
193 if let Some(s) = val.as_str() {
194 let q: RcStr = if s.starts_with('?') {
196 s.into()
197 } else {
198 format!("?{s}").into()
199 };
200 query = Some(q);
201 } else if let JsValue::Object { parts, .. } = val {
202 use crate::analyzer::ObjectPart;
205 let mut pairs: Vec<String> = Vec::new();
206 for part in parts {
207 if let ObjectPart::KeyValue(k, v) = part {
208 if let Some(k_str) = k.as_str() {
209 let enc_key = urlencoding::encode(k_str);
210 if let Some(v_str) = v.as_str() {
211 let enc_val = urlencoding::encode(v_str);
212 pairs.push(format!("{enc_key}={enc_val}"));
213 } else if let Some(v_bool) = v.as_bool() {
214 pairs.push(format!("{enc_key}={v_bool}"));
215 } else {
216 handler.span_warn_with_code(
217 span,
218 &format!(
219 "import.meta.glob() 'query' object \
220 value for key '{k_str}' must be a \
221 constant string or boolean, ignoring"
222 ),
223 diagnostic_id.clone(),
224 );
225 }
226 } else {
227 handler.span_warn_with_code(
228 span,
229 "import.meta.glob() 'query' object keys must \
230 be constant strings",
231 diagnostic_id.clone(),
232 );
233 }
234 } else {
235 handler.span_warn_with_code(
236 span,
237 "import.meta.glob() 'query' object must only \
238 contain constant key-value pairs",
239 diagnostic_id.clone(),
240 );
241 }
242 }
243 if !pairs.is_empty() {
244 query = Some(format!("?{}", pairs.join("&")).into());
245 }
246 } else {
247 handler.span_warn_with_code(
248 span,
249 "import.meta.glob() 'query' option must be a constant \
250 string, ignoring",
251 diagnostic_id.clone(),
252 );
253 }
254 }
255 Some("base") => {
256 if let Some(s) = val.as_str() {
257 base = Some(s.into());
258 } else {
259 handler.span_warn_with_code(
260 span,
261 "import.meta.glob() 'base' option must be a constant \
262 string, ignoring",
263 diagnostic_id.clone(),
264 );
265 }
266 }
267 Some("caseSensitive") => {
268 if let Some(b) = val.as_bool() {
269 case_sensitive = b;
270 } else {
271 handler.span_warn_with_code(
272 span,
273 "import.meta.glob() 'caseSensitive' option must be a \
274 constant boolean (true or false), defaulting to true",
275 diagnostic_id.clone(),
276 );
277 }
278 }
279 Some("as") => {
282 handler.span_warn_with_code(
283 span,
284 "import.meta.glob() 'as' option is not supported. Use 'query' \
285 instead (e.g. { query: '?raw' })",
286 diagnostic_id.clone(),
287 );
288 }
289 Some(other) => {
290 handler.span_warn_with_code(
291 span,
292 &format!(
293 "import.meta.glob() unsupported option '{other}'. \
294 Supported options are: eager, import, query, base, \
295 caseSensitive"
296 ),
297 diagnostic_id.clone(),
298 );
299 }
300 None => {
301 handler.span_warn_with_code(
302 span,
303 "import.meta.glob() option keys must be constant strings",
304 diagnostic_id.clone(),
305 );
306 }
307 }
308 }
309 }
310 }
311 _ => {
312 handler.span_err_with_code(
313 span,
314 "import.meta.glob() second argument must be an object literal",
315 diagnostic_id.clone(),
316 );
317 return None;
318 }
319 }
320 }
321
322 Some(ImportMetaGlobOptions {
323 patterns,
324 eager,
325 import,
326 query,
327 base,
328 case_sensitive,
329 })
330}
331
332fn strip_relative_prefix(pattern: &str) -> &str {
340 pattern.strip_prefix("./").unwrap_or(pattern)
341}
342
343async fn flatten_read_glob(result: &ReadGlobResult) -> Result<Vec<(RcStr, FileSystemPath)>> {
351 let mut files = Vec::new();
352
353 fn collect_files(
355 node: &ReadGlobResult,
356 prefix: &str,
357 files: &mut Vec<(RcStr, FileSystemPath)>,
358 ) {
359 for (segment, entry) in &node.results {
360 let full_path = if prefix.is_empty() {
361 segment.to_string()
362 } else {
363 format!("{prefix}/{segment}")
364 };
365 if let DirectoryEntry::File(path) = entry {
366 files.push((full_path.into(), path.clone()));
367 }
368 }
369 }
370
371 let mut pending: Vec<(String, turbo_tasks::ReadRef<ReadGlobResult>)> = Vec::new();
373 collect_files(result, "", &mut files);
374
375 for (segment, inner_vc) in &result.inner {
377 let child_prefix = segment.to_string();
378 let inner = inner_vc.await?;
379 pending.push((child_prefix, inner));
380 }
381
382 while let Some((prefix, node)) = pending.pop() {
383 collect_files(&node, &prefix, &mut files);
384 for (segment, inner_vc) in &node.inner {
385 let child_prefix = format!("{prefix}/{segment}");
386 let inner = inner_vc.await?;
387 pending.push((child_prefix, inner));
388 }
389 }
390
391 files.sort_by(|a: &(RcStr, _), b: &(RcStr, _)| a.0.cmp(&b.0));
392 Ok(files)
393}
394
395#[turbo_tasks::value]
400#[derive(Debug)]
401pub struct ImportMetaGlobMapEntry {
402 pub origin_relative: RcStr,
405 pub request: ResolvedVc<Request>,
406 pub result: ResolvedVc<ModuleResolveResult>,
407}
408
409#[turbo_tasks::value(transparent)]
410pub struct ImportMetaGlobMap(
411 #[bincode(with = "turbo_bincode::indexmap")] FxIndexMap<RcStr, ImportMetaGlobMapEntry>,
412);
413
414#[turbo_tasks::value_impl]
415impl ImportMetaGlobMap {
416 #[turbo_tasks::function]
423 pub(crate) async fn generate(
424 origin: Vc<Box<dyn ResolveOrigin>>,
425 base_dir: FileSystemPath,
426 positive_glob: Vc<Glob>,
427 negative_glob: Option<Vc<Glob>>,
428 query: Option<RcStr>,
429 eager: bool,
430 issue_source: Option<IssueSource>,
431 error_mode: ResolveErrorMode,
432 ) -> Result<Vc<Self>> {
433 let origin_path = origin.into_trait_ref().await?.origin_path().parent();
434
435 let glob_result = base_dir.read_glob(positive_glob).await?;
437 let files = flatten_read_glob(&glob_result).await?;
438
439 let negative = if let Some(neg) = negative_glob {
441 Some(neg.await?)
442 } else {
443 None
444 };
445
446 let reference_sub_type = if eager {
447 EcmaScriptModulesReferenceSubType::Import
448 } else {
449 EcmaScriptModulesReferenceSubType::DynamicImport
450 };
451
452 let entries: Vec<_> = files
454 .iter()
455 .filter(|(base_relative, _)| {
456 if let Some(ref neg) = negative {
458 !neg.matches(base_relative)
459 } else {
460 true
461 }
462 })
463 .map(|(base_relative, _logical_path)| {
464 let origin_path = &origin_path;
465 let base_dir = &base_dir;
466 let query = &query;
467 let reference_sub_type = &reference_sub_type;
468 async move {
469 let logical_path = base_dir.join(base_relative)?;
473 let Some(origin_relative) = origin_path.get_relative_path_to(&logical_path)
474 else {
475 bail!(
476 "import.meta.glob: failed to compute relative path from origin to \
477 matched file"
478 );
479 };
480 let origin_relative = if origin_relative.starts_with("../") {
481 origin_relative
482 } else {
483 RcStr::from(format!("./{origin_relative}"))
484 };
485
486 let request_str: RcStr = if let Some(q) = query {
488 format!("{origin_relative}{q}").into()
489 } else {
490 origin_relative.clone()
491 };
492
493 let request = Request::parse_string(request_str).to_resolved().await?;
494
495 let result = esm_resolve(
496 origin,
497 *request,
498 reference_sub_type.clone(),
499 error_mode,
500 issue_source,
501 )
502 .await?
503 .to_resolved()
504 .await?;
505
506 Ok((
507 origin_relative.clone(),
508 ImportMetaGlobMapEntry {
509 origin_relative,
510 request,
511 result,
512 },
513 ))
514 }
515 })
516 .try_join()
517 .await?;
518
519 let mut map: FxIndexMap<RcStr, ImportMetaGlobMapEntry> = entries.into_iter().collect();
520
521 map.sort_keys();
522
523 Ok(Vc::cell(map))
524 }
525}
526
527#[turbo_tasks::value]
536#[derive(ValueToString)]
537#[value_to_string("import.meta.glob resolved reference")]
538pub struct ImportMetaGlobModuleReference {
539 result: ResolvedVc<ModuleResolveResult>,
540 export: ExportUsage,
541}
542
543#[turbo_tasks::value_impl]
544impl ModuleReference for ImportMetaGlobModuleReference {
545 #[turbo_tasks::function]
546 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
547 *self.result
548 }
549
550 fn chunking_type(&self) -> Option<ChunkingType> {
551 Some(ChunkingType::Parallel {
552 inherit_async: false,
553 hoisted: false,
554 })
555 }
556
557 fn binding_usage(&self) -> BindingUsage {
558 BindingUsage {
559 import: Default::default(),
560 export: self.export.clone(),
561 }
562 }
563}
564
565fn modifier(
575 patterns: &[RcStr],
576 eager: bool,
577 import: &Option<RcStr>,
578 query: &Option<RcStr>,
579 base: &Option<RcStr>,
580 case_sensitive: bool,
581) -> RcStr {
582 let mut s = format!("import.meta.glob {}", patterns.join(", "));
583 if eager {
584 s.push_str(" eager");
585 }
586 if let Some(named) = import {
587 s.push_str(" import=");
588 s.push_str(named);
589 }
590 if let Some(q) = query {
591 s.push_str(" query=");
592 s.push_str(q);
593 }
594 if let Some(b) = base {
595 s.push_str(" base=");
596 s.push_str(b);
597 }
598 if !case_sensitive {
599 s.push_str(" case-insensitive");
600 }
601 s.into()
602}
603
604#[turbo_tasks::value]
605pub struct ImportMetaGlobAsset {
606 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
607 pub patterns: Vec<RcStr>,
608 pub eager: bool,
609 pub import: Option<RcStr>,
610 pub query: Option<RcStr>,
611 pub base: Option<RcStr>,
612 pub case_sensitive: bool,
613 pub issue_source: Option<IssueSource>,
614 pub error_mode: ResolveErrorMode,
615}
616
617#[turbo_tasks::value_impl]
618impl ImportMetaGlobAsset {
619 #[turbo_tasks::function]
626 pub async fn map(&self) -> Result<Vc<ImportMetaGlobMap>> {
627 let origin = *self.origin;
628 let origin_dir = origin.into_trait_ref().await?.origin_path().parent();
629
630 let base_dir = if let Some(ref b) = self.base {
633 origin_dir.join(b)?
634 } else {
635 origin_dir
636 };
637
638 let (positive_raw, negative_raw): (Vec<_>, Vec<_>) =
641 self.patterns.iter().partition(|p| !p.starts_with('!'));
642 let glob_options = GlobOptions {
643 case_insensitive: !self.case_sensitive,
644 ..Default::default()
645 };
646
647 let positive_globs: Vec<Vc<Glob>> = positive_raw
651 .iter()
652 .map(|p| Glob::new(strip_relative_prefix(p).into(), glob_options))
653 .collect();
654
655 let positive_glob = if positive_globs.len() == 1 {
656 positive_globs.into_iter().next().unwrap()
657 } else {
658 Glob::alternatives(positive_globs)
659 };
660
661 let negative_glob = if !negative_raw.is_empty() {
664 let neg_globs: Vec<Vc<Glob>> = negative_raw
665 .iter()
666 .map(|p| {
667 let stripped = p.strip_prefix('!').unwrap_or(p);
668 let stripped = strip_relative_prefix(stripped);
669 Glob::new(stripped.into(), glob_options)
670 })
671 .collect();
672
673 let neg = if neg_globs.len() == 1 {
674 neg_globs.into_iter().next().unwrap()
675 } else {
676 Glob::alternatives(neg_globs)
677 };
678 Some(neg)
679 } else {
680 None
681 };
682
683 Ok(ImportMetaGlobMap::generate(
684 origin,
685 base_dir,
686 positive_glob,
687 negative_glob,
688 self.query.clone(),
689 self.eager,
690 self.issue_source,
691 self.error_mode,
692 ))
693 }
694}
695
696#[turbo_tasks::value_impl]
697impl Module for ImportMetaGlobAsset {
698 #[turbo_tasks::function]
699 async fn ident(&self) -> Result<Vc<AssetIdent>> {
700 let origin = self.origin.into_trait_ref().await?;
701 let origin_path = origin.origin_path();
702 Ok(AssetIdent::from_path(origin_path)
706 .with_layer(origin.asset_context().into_trait_ref().await?.layer())
707 .with_modifier(modifier(
708 &self.patterns,
709 self.eager,
710 &self.import,
711 &self.query,
712 &self.base,
713 self.case_sensitive,
714 ))
715 .into_vc())
716 }
717
718 #[turbo_tasks::function]
719 fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
720 Vc::cell(None)
721 }
722
723 #[turbo_tasks::function]
724 async fn references(self: Vc<Self>) -> Result<Vc<ModuleReferences>> {
725 let this = self.await?;
726 let map = &*self.map().await?;
727
728 let export = match &this.import {
729 Some(name) => ExportUsage::Named(name.clone()),
730 None => ExportUsage::All,
731 };
732
733 for (key, entry) in map.iter() {
738 if entry.result.await?.primary.iter().any(|(_, item)| {
739 matches!(
740 item,
741 turbopack_core::resolve::ModuleResolveResultItem::Unknown(_)
742 )
743 }) {
744 CodeGenerationIssue {
745 severity: IssueSeverity::Error,
746 title: StyledString::Text(rcstr!(
747 "import.meta.glob() matched a file that has no module type"
748 ))
749 .resolved_cell(),
750 message: StyledString::Text(
751 format!(
752 "import.meta.glob({}) matched {key}, which doesn't have an associated \
753 module type. Narrow the pattern, exclude the file with a negative \
754 pattern (\"!...\"), or register a loader or module type for its file \
755 extension.",
756 this.patterns
757 .iter()
758 .map(|p| format!("{p:?}"))
759 .collect::<Vec<_>>()
760 .join(", ")
761 )
762 .into(),
763 )
764 .resolved_cell(),
765 path: this.origin.into_trait_ref().await?.origin_path(),
766 source: this.issue_source,
767 }
768 .resolved_cell()
769 .emit();
770 }
771 }
772
773 Ok(Vc::cell(
774 map.iter()
775 .map(|(_, entry)| {
776 ResolvedVc::upcast(
777 ImportMetaGlobModuleReference {
778 result: entry.result,
779 export: export.clone(),
780 }
781 .resolved_cell(),
782 )
783 })
784 .collect(),
785 ))
786 }
787
788 #[turbo_tasks::function]
789 fn side_effects(&self) -> Vc<ModuleSideEffects> {
790 if self.eager {
791 ModuleSideEffects::ModuleEvaluationIsSideEffectFree.cell()
795 } else {
796 ModuleSideEffects::SideEffectFree.cell()
799 }
800 }
801}
802
803#[turbo_tasks::value_impl]
804impl ChunkableModule for ImportMetaGlobAsset {
805 #[turbo_tasks::function]
806 fn as_chunk_item(
807 self: ResolvedVc<Self>,
808 module_graph: ResolvedVc<ModuleGraph>,
809 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
810 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
811 ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
812 }
813}
814
815#[turbo_tasks::value_impl]
816impl EcmascriptChunkPlaceable for ImportMetaGlobAsset {
817 #[turbo_tasks::function]
818 fn get_exports(&self) -> Vc<EcmascriptExports> {
819 EcmascriptExports::Value.cell()
820 }
821
822 #[turbo_tasks::function]
823 async fn chunk_item_content(
824 self: Vc<Self>,
825 chunking_context: Vc<Box<dyn ChunkingContext>>,
826 _module_graph: Vc<ModuleGraph>,
827 _async_module_info: Option<Vc<AsyncModuleInfo>>,
828 _estimated: bool,
829 ) -> Result<Vc<EcmascriptChunkItemContent>> {
830 let this = self.await?;
831 let map = &*self.map().await?;
832 let minify = chunking_context.minify_type().await?;
833
834 let mut glob_map = ObjectLit {
835 span: DUMMY_SP,
836 props: vec![],
837 };
838
839 for (key, entry) in map {
840 let pm = PatternMapping::resolve_request(
841 *entry.request,
842 *this.origin,
843 chunking_context,
844 *entry.result,
845 ResolveType::ChunkItem,
846 None,
847 )
848 .await?;
849
850 let PatternMapping::Single(pm) = &*pm else {
851 continue;
852 };
853
854 let key_expr = Expr::Lit(Lit::Str(entry.origin_relative.as_str().into()));
855
856 let value_expr = if this.eager {
858 let module_expr = pm.create_esm_require(Cow::Borrowed(&key_expr));
861 if let Some(named) = &this.import {
863 quote!(
864 "$module[$named]" as Expr,
865 module: Expr = module_expr,
866 named: Expr = Expr::Lit(Lit::Str(named.as_str().into()))
867 )
868 } else {
869 module_expr
870 }
871 } else {
872 let import_expr = pm.create_import(Cow::Borrowed(&key_expr), false);
874 if let Some(named) = &this.import {
875 quote!(
877 "() => $promise.then((m) => m[$named])" as Expr,
878 promise: Expr = import_expr,
879 named: Expr = Expr::Lit(Lit::Str(named.as_str().into()))
880 )
881 } else {
882 quote!(
883 "() => $promise" as Expr,
884 promise: Expr = import_expr
885 )
886 }
887 };
888
889 let prop = KeyValueProp {
892 key: PropName::Str(key.as_str().into()),
893 value: Box::new(value_expr),
894 };
895
896 glob_map
897 .props
898 .push(PropOrSpread::Prop(Box::new(Prop::KeyValue(prop))));
899 }
900
901 let expr = quote_expr!(
902 "$turbopack_export_value($obj);",
903 turbopack_export_value: Expr = TURBOPACK_EXPORT_VALUE.into(),
904 obj: Expr = Expr::Object(glob_map),
905 );
906
907 let module = ast::Module {
908 span: DUMMY_SP,
909 body: vec![ModuleItem::Stmt(Stmt::Expr(ExprStmt {
910 span: DUMMY_SP,
911 expr,
912 }))],
913 shebang: None,
914 };
915
916 let source_map: Arc<swc_core::common::SourceMap> = Default::default();
917
918 let mut bytes: Vec<u8> = vec![];
919 let mut wr: JsWriter<'_, &mut Vec<u8>> =
920 JsWriter::new(source_map.clone(), "\n", &mut bytes, None);
921 if matches!(*minify, MinifyType::Minify { .. }) {
922 wr.set_indent_str("");
923 }
924
925 let mut emitter = Emitter {
926 cfg: swc_core::ecma::codegen::Config::default(),
927 cm: source_map.clone(),
928 comments: None,
929 wr,
930 };
931
932 emitter.emit_module(&module)?;
933
934 Ok(EcmascriptChunkItemContent {
935 inner_code: bytes.into(),
936 ..Default::default()
937 }
938 .cell())
939 }
940}
941
942#[turbo_tasks::value]
947#[derive(Hash, Debug, ValueToString)]
948pub struct ImportMetaGlobAssetReference {
949 pub inner: ResolvedVc<ImportMetaGlobAsset>,
950 pub patterns: Vec<RcStr>,
951}
952
953impl std::fmt::Display for ImportMetaGlobAssetReference {
954 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
955 write!(f, "import.meta.glob {}", self.patterns.join(", "))
956 }
957}
958
959impl ImportMetaGlobAssetReference {
960 pub fn new(
961 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
962 patterns: Vec<RcStr>,
963 eager: bool,
964 import: Option<RcStr>,
965 query: Option<RcStr>,
966 base: Option<RcStr>,
967 case_sensitive: bool,
968 issue_source: Option<IssueSource>,
969 error_mode: ResolveErrorMode,
970 ) -> Self {
971 let inner = ImportMetaGlobAsset {
972 origin,
973 patterns: patterns.clone(),
974 eager,
975 import,
976 query,
977 base,
978 case_sensitive,
979 issue_source,
980 error_mode,
981 }
982 .resolved_cell();
983
984 ImportMetaGlobAssetReference { inner, patterns }
985 }
986}
987
988#[turbo_tasks::value_impl]
989impl ModuleReference for ImportMetaGlobAssetReference {
990 #[turbo_tasks::function]
991 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
992 *ModuleResolveResult::module(ResolvedVc::upcast(self.inner))
993 }
994
995 fn chunking_type(&self) -> Option<ChunkingType> {
996 Some(ChunkingType::Parallel {
997 inherit_async: false,
998 hoisted: false,
999 })
1000 }
1001}
1002
1003impl IntoCodeGenReference for ImportMetaGlobAssetReference {
1004 fn into_reference(self) -> ResolvedVc<Box<dyn ModuleReference>> {
1005 ResolvedVc::upcast(self.resolved_cell())
1006 }
1007
1008 fn into_code_gen_reference(
1009 self,
1010 path: AstPath,
1011 ) -> (ResolvedVc<Box<dyn ModuleReference>>, CodeGen) {
1012 let reference = self.resolved_cell();
1013 (
1014 ResolvedVc::upcast(reference),
1015 CodeGen::ImportMetaGlobAssetReferenceCodeGen(ImportMetaGlobAssetReferenceCodeGen {
1016 reference,
1017 path,
1018 }),
1019 )
1020 }
1021}
1022
1023#[derive(
1028 PartialEq, Eq, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, Encode, Decode,
1029)]
1030pub struct ImportMetaGlobAssetReferenceCodeGen {
1031 path: AstPath,
1032 reference: ResolvedVc<ImportMetaGlobAssetReference>,
1033}
1034
1035impl ImportMetaGlobAssetReferenceCodeGen {
1036 pub async fn code_generation(
1037 &self,
1038 chunking_context: Vc<Box<dyn ChunkingContext>>,
1039 ) -> Result<CodeGeneration> {
1040 let module_id = self
1041 .reference
1042 .await?
1043 .inner
1044 .chunk_item_id(chunking_context)
1045 .await?;
1046
1047 let mut visitors = Vec::new();
1048 visitors.push(create_visitor!(
1049 self.path,
1050 visit_mut_expr,
1051 |expr: &mut Expr| {
1052 if let Expr::Call(_) = expr {
1053 *expr = quote!(
1055 "$turbopack_require($id)" as Expr,
1056 turbopack_require: Expr = TURBOPACK_REQUIRE.into(),
1057 id: Expr = module_id_to_lit(&module_id)
1058 );
1059 }
1060 }
1061 ));
1062 Ok(CodeGeneration::visitors(visitors))
1063 }
1064}