1#![allow(non_local_definitions)]
3#![feature(box_patterns)]
4#![feature(min_specialization)]
5#![feature(iter_intersperse)]
6#![feature(int_roundings)]
7#![feature(arbitrary_self_types)]
8#![feature(arbitrary_self_types_pointers)]
9#![recursion_limit = "256"]
10
11pub mod analyzer;
12pub mod annotations;
13pub mod async_chunk;
14pub mod chunk;
15pub mod code_gen;
16mod errors;
17pub mod magic_identifier;
18pub mod manifest;
19mod merged_module;
20pub mod minify;
21pub mod parse;
22mod path_visitor;
23pub mod references;
24pub mod runtime_functions;
25pub mod side_effect_optimization;
26pub(crate) mod special_cases;
27pub(crate) mod static_code;
28mod swc_comments;
29pub mod text;
30pub(crate) mod transform;
31pub mod tree_shake;
32pub mod typescript;
33pub mod utils;
34pub mod webpack;
35pub mod worker_chunk;
36
37use std::{
38 borrow::Cow,
39 collections::hash_map::Entry,
40 fmt::{Debug, Display, Formatter},
41 mem::take,
42 sync::Arc,
43};
44
45use anyhow::{Context, Result, bail};
46use chunk::EcmascriptChunkItem;
47use code_gen::{CodeGeneration, CodeGenerationHoistedStmt};
48use either::Either;
49use itertools::Itertools;
50use parse::{ParseResult, parse};
51use path_visitor::ApplyVisitors;
52use references::esm::UrlRewriteBehavior;
53pub use references::{AnalyzeEcmascriptModuleResult, TURBOPACK_HELPER};
54use rustc_hash::{FxHashMap, FxHashSet};
55use serde::{Deserialize, Serialize};
56use smallvec::SmallVec;
57pub use static_code::StaticEcmascriptCode;
58use swc_core::{
59 atoms::Atom,
60 base::SwcComments,
61 common::{
62 BytePos, DUMMY_SP, FileName, GLOBALS, Globals, Loc, Mark, SourceFile, SourceMap,
63 SourceMapper, Span, SpanSnippetError, SyntaxContext,
64 comments::{Comment, CommentKind, Comments},
65 source_map::{FileLinesResult, Files, SourceMapLookupError},
66 util::take::Take,
67 },
68 ecma::{
69 ast::{
70 self, CallExpr, Callee, Decl, EmptyStmt, Expr, ExprStmt, Id, Ident, ModuleItem,
71 Program, Script, SourceMapperExt, Stmt,
72 },
73 codegen::{Emitter, text_writer::JsWriter},
74 utils::StmtLikeInjector,
75 visit::{VisitMut, VisitMutWith, VisitMutWithAstPath},
76 },
77 quote,
78};
79use tracing::{Instrument, Level, instrument};
80pub use transform::{
81 CustomTransformer, EcmascriptInputTransform, EcmascriptInputTransforms, TransformContext,
82 TransformPlugin,
83};
84use turbo_rcstr::{RcStr, rcstr};
85use turbo_tasks::{
86 FxDashMap, FxIndexMap, IntoTraitRef, NonLocalValue, ReadRef, ResolvedVc, TaskInput,
87 TryFlatJoinIterExt, TryJoinIterExt, ValueToString, Vc, trace::TraceRawVcs,
88};
89use turbo_tasks_fs::{FileJsonContent, FileSystemPath, glob::Glob, rope::Rope};
90use turbopack_core::{
91 asset::{Asset, AssetContent},
92 chunk::{
93 AsyncModuleInfo, ChunkItem, ChunkType, ChunkableModule, ChunkingContext, EvaluatableAsset,
94 MergeableModule, MergeableModuleExposure, MergeableModules, MergeableModulesExposed,
95 MinifyType, ModuleChunkItemIdExt, ModuleId,
96 },
97 compile_time_info::CompileTimeInfo,
98 context::AssetContext,
99 ident::AssetIdent,
100 module::{Module, OptionModule},
101 module_graph::ModuleGraph,
102 reference::ModuleReferences,
103 reference_type::InnerAssets,
104 resolve::{
105 FindContextFileResult, find_context_file, origin::ResolveOrigin, package_json,
106 parse::Request,
107 },
108 source::Source,
109 source_map::GenerateSourceMap,
110};
111pub use turbopack_resolve::ecmascript as resolve;
113
114use self::chunk::{EcmascriptChunkItemContent, EcmascriptChunkType, EcmascriptExports};
115use crate::{
116 analyzer::graph::EvalContext,
117 chunk::{EcmascriptChunkPlaceable, placeable::is_marked_as_side_effect_free},
118 code_gen::{CodeGens, ModifiableAst},
119 merged_module::MergedEcmascriptModule,
120 parse::generate_js_source_map,
121 references::{
122 analyse_ecmascript_module,
123 async_module::OptionAsyncModule,
124 esm::{base::EsmAssetReferences, export},
125 },
126 side_effect_optimization::reference::EcmascriptModulePartReference,
127 swc_comments::{CowComments, ImmutableComments},
128 transform::{remove_directives, remove_shebang},
129};
130
131#[derive(
132 Eq,
133 PartialEq,
134 Hash,
135 Debug,
136 Clone,
137 Copy,
138 Default,
139 TaskInput,
140 TraceRawVcs,
141 NonLocalValue,
142 Serialize,
143 Deserialize,
144)]
145pub enum SpecifiedModuleType {
146 #[default]
147 Automatic,
148 CommonJs,
149 EcmaScript,
150}
151
152#[derive(
153 PartialOrd,
154 Ord,
155 PartialEq,
156 Eq,
157 Hash,
158 Debug,
159 Clone,
160 Copy,
161 Default,
162 Serialize,
163 Deserialize,
164 TaskInput,
165 TraceRawVcs,
166 NonLocalValue,
167)]
168#[serde(rename_all = "kebab-case")]
169pub enum TreeShakingMode {
170 #[default]
171 ModuleFragments,
172 ReexportsOnly,
173}
174
175#[turbo_tasks::value(transparent)]
176pub struct OptionTreeShaking(pub Option<TreeShakingMode>);
177
178#[turbo_tasks::value(shared)]
179#[derive(Hash, Debug, Default, Copy, Clone)]
180pub struct EcmascriptOptions {
181 pub refresh: bool,
182 pub tree_shaking_mode: Option<TreeShakingMode>,
184 pub specified_module_type: SpecifiedModuleType,
186 pub url_rewrite_behavior: Option<UrlRewriteBehavior>,
190 pub import_externals: bool,
193 pub ignore_dynamic_requests: bool,
197 pub extract_source_map: bool,
200 pub keep_last_successful_parse: bool,
204}
205
206#[turbo_tasks::value]
207#[derive(Hash, Debug, Copy, Clone, TaskInput)]
208pub enum EcmascriptModuleAssetType {
209 Ecmascript,
211 Typescript {
213 tsx: bool,
215 analyze_types: bool,
217 },
218 TypescriptDeclaration,
220}
221
222impl Display for EcmascriptModuleAssetType {
223 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
224 match self {
225 EcmascriptModuleAssetType::Ecmascript => write!(f, "ecmascript"),
226 EcmascriptModuleAssetType::Typescript { tsx, analyze_types } => {
227 write!(f, "typescript")?;
228 if *tsx {
229 write!(f, "with JSX")?;
230 }
231 if *analyze_types {
232 write!(f, "with types")?;
233 }
234 Ok(())
235 }
236 EcmascriptModuleAssetType::TypescriptDeclaration => write!(f, "typescript declaration"),
237 }
238 }
239}
240
241#[derive(Clone)]
242pub struct EcmascriptModuleAssetBuilder {
243 source: ResolvedVc<Box<dyn Source>>,
244 asset_context: ResolvedVc<Box<dyn AssetContext>>,
245 ty: EcmascriptModuleAssetType,
246 transforms: ResolvedVc<EcmascriptInputTransforms>,
247 options: ResolvedVc<EcmascriptOptions>,
248 compile_time_info: ResolvedVc<CompileTimeInfo>,
249 inner_assets: Option<ResolvedVc<InnerAssets>>,
250}
251
252impl EcmascriptModuleAssetBuilder {
253 pub fn with_inner_assets(mut self, inner_assets: ResolvedVc<InnerAssets>) -> Self {
254 self.inner_assets = Some(inner_assets);
255 self
256 }
257
258 pub fn with_type(mut self, ty: EcmascriptModuleAssetType) -> Self {
259 self.ty = ty;
260 self
261 }
262
263 pub fn build(self) -> Vc<EcmascriptModuleAsset> {
264 if let Some(inner_assets) = self.inner_assets {
265 EcmascriptModuleAsset::new_with_inner_assets(
266 *self.source,
267 *self.asset_context,
268 self.ty,
269 *self.transforms,
270 *self.options,
271 *self.compile_time_info,
272 *inner_assets,
273 )
274 } else {
275 EcmascriptModuleAsset::new(
276 *self.source,
277 *self.asset_context,
278 self.ty,
279 *self.transforms,
280 *self.options,
281 *self.compile_time_info,
282 )
283 }
284 }
285}
286
287#[turbo_tasks::value]
288pub struct EcmascriptModuleAsset {
289 pub source: ResolvedVc<Box<dyn Source>>,
290 pub asset_context: ResolvedVc<Box<dyn AssetContext>>,
291 pub ty: EcmascriptModuleAssetType,
292 pub transforms: ResolvedVc<EcmascriptInputTransforms>,
293 pub options: ResolvedVc<EcmascriptOptions>,
294 pub compile_time_info: ResolvedVc<CompileTimeInfo>,
295 pub inner_assets: Option<ResolvedVc<InnerAssets>>,
296 #[turbo_tasks(debug_ignore)]
297 last_successful_parse: turbo_tasks::TransientState<ReadRef<ParseResult>>,
298}
299impl core::fmt::Debug for EcmascriptModuleAsset {
300 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
301 f.debug_struct("EcmascriptModuleAsset")
302 .field("source", &self.source)
303 .field("asset_context", &self.asset_context)
304 .field("ty", &self.ty)
305 .field("transforms", &self.transforms)
306 .field("options", &self.options)
307 .field("compile_time_info", &self.compile_time_info)
308 .field("inner_assets", &self.inner_assets)
309 .finish()
310 }
311}
312
313#[turbo_tasks::value_trait]
314pub trait EcmascriptParsable {
315 #[turbo_tasks::function]
316 fn failsafe_parse(self: Vc<Self>) -> Result<Vc<ParseResult>>;
317
318 #[turbo_tasks::function]
319 fn parse_original(self: Vc<Self>) -> Result<Vc<ParseResult>>;
320
321 #[turbo_tasks::function]
322 fn ty(self: Vc<Self>) -> Result<Vc<EcmascriptModuleAssetType>>;
323}
324
325#[turbo_tasks::value_trait]
326pub trait EcmascriptAnalyzable: Module + Asset {
327 #[turbo_tasks::function]
328 fn analyze(self: Vc<Self>) -> Vc<AnalyzeEcmascriptModuleResult>;
329
330 #[turbo_tasks::function]
333 async fn module_content_without_analysis(
334 self: Vc<Self>,
335 generate_source_map: bool,
336 ) -> Result<Vc<EcmascriptModuleContent>>;
337
338 #[turbo_tasks::function]
339 async fn module_content_options(
340 self: Vc<Self>,
341 chunking_context: Vc<Box<dyn ChunkingContext>>,
342 async_module_info: Option<Vc<AsyncModuleInfo>>,
343 ) -> Result<Vc<EcmascriptModuleContentOptions>>;
344
345 #[turbo_tasks::function]
346 fn module_content(
347 self: Vc<Self>,
348 chunking_context: Vc<Box<dyn ChunkingContext>>,
349 async_module_info: Option<Vc<AsyncModuleInfo>>,
350 ) -> Result<Vc<EcmascriptModuleContent>> {
351 let own_options = self.module_content_options(chunking_context, async_module_info);
352 Ok(EcmascriptModuleContent::new(own_options))
353 }
354}
355
356impl EcmascriptModuleAsset {
357 pub fn builder(
358 source: ResolvedVc<Box<dyn Source>>,
359 asset_context: ResolvedVc<Box<dyn AssetContext>>,
360 transforms: ResolvedVc<EcmascriptInputTransforms>,
361 options: ResolvedVc<EcmascriptOptions>,
362 compile_time_info: ResolvedVc<CompileTimeInfo>,
363 ) -> EcmascriptModuleAssetBuilder {
364 EcmascriptModuleAssetBuilder {
365 source,
366 asset_context,
367 ty: EcmascriptModuleAssetType::Ecmascript,
368 transforms,
369 options,
370 compile_time_info,
371 inner_assets: None,
372 }
373 }
374}
375
376#[turbo_tasks::value]
377#[derive(Clone)]
378pub(crate) struct ModuleTypeResult {
379 pub module_type: SpecifiedModuleType,
380 pub referenced_package_json: Option<FileSystemPath>,
381}
382
383#[turbo_tasks::value_impl]
384impl ModuleTypeResult {
385 #[turbo_tasks::function]
386 fn new(module_type: SpecifiedModuleType) -> Vc<Self> {
387 Self::cell(ModuleTypeResult {
388 module_type,
389 referenced_package_json: None,
390 })
391 }
392
393 #[turbo_tasks::function]
394 fn new_with_package_json(
395 module_type: SpecifiedModuleType,
396 package_json: FileSystemPath,
397 ) -> Vc<Self> {
398 Self::cell(ModuleTypeResult {
399 module_type,
400 referenced_package_json: Some(package_json),
401 })
402 }
403}
404
405#[turbo_tasks::value_impl]
406impl EcmascriptParsable for EcmascriptModuleAsset {
407 #[turbo_tasks::function]
408 async fn failsafe_parse(self: Vc<Self>) -> Result<Vc<ParseResult>> {
409 let real_result = self.parse();
410 let this = self.await?;
411 if this.options.await?.keep_last_successful_parse {
412 let real_result_value = real_result.await?;
413 let result_value = if matches!(*real_result_value, ParseResult::Ok { .. }) {
414 this.last_successful_parse.set(real_result_value.clone());
415 real_result_value
416 } else {
417 let state_ref = this.last_successful_parse.get();
418 state_ref.as_ref().unwrap_or(&real_result_value).clone()
419 };
420 Ok(ReadRef::cell(result_value))
421 } else {
422 Ok(real_result)
423 }
424 }
425
426 #[turbo_tasks::function]
427 fn parse_original(self: Vc<Self>) -> Vc<ParseResult> {
428 self.failsafe_parse()
429 }
430
431 #[turbo_tasks::function]
432 fn ty(&self) -> Vc<EcmascriptModuleAssetType> {
433 self.ty.cell()
434 }
435}
436
437#[turbo_tasks::value_impl]
438impl EcmascriptAnalyzable for EcmascriptModuleAsset {
439 #[turbo_tasks::function]
440 fn analyze(self: Vc<Self>) -> Vc<AnalyzeEcmascriptModuleResult> {
441 analyse_ecmascript_module(self, None)
442 }
443
444 #[turbo_tasks::function]
447 async fn module_content_without_analysis(
448 self: Vc<Self>,
449 generate_source_map: bool,
450 ) -> Result<Vc<EcmascriptModuleContent>> {
451 let this = self.await?;
452
453 let parsed = self.parse();
454
455 Ok(EcmascriptModuleContent::new_without_analysis(
456 parsed,
457 self.ident(),
458 this.options.await?.specified_module_type,
459 generate_source_map,
460 ))
461 }
462
463 #[turbo_tasks::function]
464 async fn module_content_options(
465 self: ResolvedVc<Self>,
466 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
467 async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
468 ) -> Result<Vc<EcmascriptModuleContentOptions>> {
469 let parsed = self.parse().to_resolved().await?;
470
471 let analyze = self.analyze();
472 let analyze_ref = analyze.await?;
473
474 let module_type_result = self.determine_module_type().await?;
475 let generate_source_map = *chunking_context
476 .reference_module_source_maps(Vc::upcast(*self))
477 .await?;
478
479 Ok(EcmascriptModuleContentOptions {
480 parsed,
481 module: ResolvedVc::upcast(self),
482 specified_module_type: module_type_result.module_type,
483 chunking_context,
484 references: analyze.references().to_resolved().await?,
485 esm_references: analyze_ref.esm_references,
486 part_references: vec![],
487 code_generation: analyze_ref.code_generation,
488 async_module: analyze_ref.async_module,
489 generate_source_map,
490 original_source_map: analyze_ref.source_map,
491 exports: analyze_ref.exports,
492 async_module_info,
493 }
494 .cell())
495 }
496}
497
498#[turbo_tasks::function]
499async fn determine_module_type_for_directory(
500 context_path: FileSystemPath,
501) -> Result<Vc<ModuleTypeResult>> {
502 let find_package_json =
503 find_context_file(context_path, package_json().resolve().await?).await?;
504 let FindContextFileResult::Found(package_json, _) = &*find_package_json else {
505 return Ok(ModuleTypeResult::new(SpecifiedModuleType::Automatic));
506 };
507
508 if let FileJsonContent::Content(content) = &*package_json.read_json().await?
510 && let Some(r#type) = content.get("type")
511 {
512 return Ok(ModuleTypeResult::new_with_package_json(
513 match r#type.as_str() {
514 Some("module") => SpecifiedModuleType::EcmaScript,
515 Some("commonjs") => SpecifiedModuleType::CommonJs,
516 _ => SpecifiedModuleType::Automatic,
517 },
518 package_json.clone(),
519 ));
520 }
521
522 Ok(ModuleTypeResult::new_with_package_json(
523 SpecifiedModuleType::Automatic,
524 package_json.clone(),
525 ))
526}
527
528#[turbo_tasks::value_impl]
529impl EcmascriptModuleAsset {
530 #[turbo_tasks::function]
531 pub fn new(
532 source: ResolvedVc<Box<dyn Source>>,
533 asset_context: ResolvedVc<Box<dyn AssetContext>>,
534 ty: EcmascriptModuleAssetType,
535 transforms: ResolvedVc<EcmascriptInputTransforms>,
536 options: ResolvedVc<EcmascriptOptions>,
537 compile_time_info: ResolvedVc<CompileTimeInfo>,
538 ) -> Vc<Self> {
539 Self::cell(EcmascriptModuleAsset {
540 source,
541 asset_context,
542 ty,
543 transforms,
544 options,
545
546 compile_time_info,
547 inner_assets: None,
548 last_successful_parse: Default::default(),
549 })
550 }
551
552 #[turbo_tasks::function]
553 pub async fn new_with_inner_assets(
554 source: ResolvedVc<Box<dyn Source>>,
555 asset_context: ResolvedVc<Box<dyn AssetContext>>,
556 ty: EcmascriptModuleAssetType,
557 transforms: ResolvedVc<EcmascriptInputTransforms>,
558 options: ResolvedVc<EcmascriptOptions>,
559 compile_time_info: ResolvedVc<CompileTimeInfo>,
560 inner_assets: ResolvedVc<InnerAssets>,
561 ) -> Result<Vc<Self>> {
562 if inner_assets.await?.is_empty() {
563 Ok(Self::new(
564 *source,
565 *asset_context,
566 ty,
567 *transforms,
568 *options,
569 *compile_time_info,
570 ))
571 } else {
572 Ok(Self::cell(EcmascriptModuleAsset {
573 source,
574 asset_context,
575 ty,
576 transforms,
577 options,
578 compile_time_info,
579 inner_assets: Some(inner_assets),
580 last_successful_parse: Default::default(),
581 }))
582 }
583 }
584
585 #[turbo_tasks::function]
586 pub fn source(&self) -> Vc<Box<dyn Source>> {
587 *self.source
588 }
589
590 #[turbo_tasks::function]
591 pub fn analyze(self: Vc<Self>) -> Vc<AnalyzeEcmascriptModuleResult> {
592 analyse_ecmascript_module(self, None)
593 }
594
595 #[turbo_tasks::function]
596 pub fn options(&self) -> Vc<EcmascriptOptions> {
597 *self.options
598 }
599
600 #[turbo_tasks::function]
601 pub fn parse(&self) -> Vc<ParseResult> {
602 parse(*self.source, self.ty, *self.transforms)
603 }
604}
605
606impl EcmascriptModuleAsset {
607 #[tracing::instrument(level = "trace", skip_all)]
608 pub(crate) async fn determine_module_type(self: Vc<Self>) -> Result<ReadRef<ModuleTypeResult>> {
609 let this = self.await?;
610
611 match this.options.await?.specified_module_type {
612 SpecifiedModuleType::EcmaScript => {
613 return ModuleTypeResult::new(SpecifiedModuleType::EcmaScript).await;
614 }
615 SpecifiedModuleType::CommonJs => {
616 return ModuleTypeResult::new(SpecifiedModuleType::CommonJs).await;
617 }
618 SpecifiedModuleType::Automatic => {}
619 }
620
621 determine_module_type_for_directory(self.origin_path().await?.parent()).await
622 }
623}
624
625#[turbo_tasks::value_impl]
626impl Module for EcmascriptModuleAsset {
627 #[turbo_tasks::function]
628 async fn ident(&self) -> Result<Vc<AssetIdent>> {
629 let mut ident = self.source.ident().owned().await?;
630 if let Some(inner_assets) = self.inner_assets {
631 for (name, asset) in inner_assets.await?.iter() {
632 ident.add_asset(name.clone(), asset.ident().to_resolved().await?);
633 }
634 }
635 ident.add_modifier(rcstr!("ecmascript"));
636 ident.layer = Some(self.asset_context.into_trait_ref().await?.layer());
637 Ok(AssetIdent::new(ident))
638 }
639
640 #[turbo_tasks::function]
641 fn references(self: Vc<Self>) -> Result<Vc<ModuleReferences>> {
642 Ok(self.analyze().references())
643 }
644
645 #[turbo_tasks::function]
646 async fn is_self_async(self: Vc<Self>) -> Result<Vc<bool>> {
647 if let Some(async_module) = *self.get_async_module().await? {
648 Ok(async_module.is_self_async(self.references()))
649 } else {
650 Ok(Vc::cell(false))
651 }
652 }
653}
654
655#[turbo_tasks::value_impl]
656impl Asset for EcmascriptModuleAsset {
657 #[turbo_tasks::function]
658 fn content(&self) -> Vc<AssetContent> {
659 self.source.content()
660 }
661}
662
663#[turbo_tasks::value_impl]
664impl ChunkableModule for EcmascriptModuleAsset {
665 #[turbo_tasks::function]
666 async fn as_chunk_item(
667 self: ResolvedVc<Self>,
668 _module_graph: ResolvedVc<ModuleGraph>,
669 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
670 ) -> Vc<Box<dyn ChunkItem>> {
671 Vc::upcast(ModuleChunkItem::cell(ModuleChunkItem {
672 module: self,
673 chunking_context,
674 }))
675 }
676}
677
678#[turbo_tasks::value_impl]
679impl EcmascriptChunkPlaceable for EcmascriptModuleAsset {
680 #[turbo_tasks::function]
681 async fn get_exports(self: Vc<Self>) -> Result<Vc<EcmascriptExports>> {
682 Ok(*self.analyze().await?.exports)
683 }
684
685 #[turbo_tasks::function]
686 async fn get_async_module(self: Vc<Self>) -> Result<Vc<OptionAsyncModule>> {
687 Ok(*self.analyze().await?.async_module)
688 }
689
690 #[turbo_tasks::function]
691 async fn is_marked_as_side_effect_free(
692 self: Vc<Self>,
693 side_effect_free_packages: Vc<Glob>,
694 ) -> Result<Vc<bool>> {
695 let pkg_side_effect_free = is_marked_as_side_effect_free(
697 self.ident().path().await?.clone_value(),
698 side_effect_free_packages,
699 );
700 Ok(if *pkg_side_effect_free.await? {
701 pkg_side_effect_free
702 } else {
703 Vc::cell(self.analyze().await?.has_side_effect_free_directive)
704 })
705 }
706}
707
708#[turbo_tasks::value_impl]
709impl MergeableModule for EcmascriptModuleAsset {
710 #[turbo_tasks::function]
711 async fn is_mergeable(self: ResolvedVc<Self>) -> Result<Vc<bool>> {
712 if matches!(
713 &*self.get_exports().await?,
714 EcmascriptExports::EsmExports(_)
715 ) {
716 return Ok(Vc::cell(true));
717 }
718
719 Ok(Vc::cell(false))
720 }
721
722 #[turbo_tasks::function]
723 async fn merge(
724 self: Vc<Self>,
725 modules: Vc<MergeableModulesExposed>,
726 entry_points: Vc<MergeableModules>,
727 ) -> Result<Vc<Box<dyn ChunkableModule>>> {
728 Ok(Vc::upcast(
729 *MergedEcmascriptModule::new(
730 modules,
731 entry_points,
732 self.options().to_resolved().await?,
733 )
734 .await?,
735 ))
736 }
737}
738
739#[turbo_tasks::value_impl]
740impl EvaluatableAsset for EcmascriptModuleAsset {}
741
742#[turbo_tasks::value_impl]
743impl ResolveOrigin for EcmascriptModuleAsset {
744 #[turbo_tasks::function]
745 fn origin_path(&self) -> Vc<FileSystemPath> {
746 self.source.ident().path()
747 }
748
749 #[turbo_tasks::function]
750 fn asset_context(&self) -> Vc<Box<dyn AssetContext>> {
751 *self.asset_context
752 }
753
754 #[turbo_tasks::function]
755 async fn get_inner_asset(&self, request: Vc<Request>) -> Result<Vc<OptionModule>> {
756 Ok(Vc::cell(if let Some(inner_assets) = &self.inner_assets {
757 if let Some(request) = request.await?.request() {
758 inner_assets.await?.get(&request).copied()
759 } else {
760 None
761 }
762 } else {
763 None
764 }))
765 }
766}
767
768#[turbo_tasks::value]
769struct ModuleChunkItem {
770 module: ResolvedVc<EcmascriptModuleAsset>,
771 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
772}
773
774#[turbo_tasks::value_impl]
775impl ChunkItem for ModuleChunkItem {
776 #[turbo_tasks::function]
777 fn asset_ident(&self) -> Vc<AssetIdent> {
778 self.module.ident()
779 }
780
781 #[turbo_tasks::function]
782 fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
783 *ResolvedVc::upcast(self.chunking_context)
784 }
785
786 #[turbo_tasks::function]
787 async fn ty(&self) -> Result<Vc<Box<dyn ChunkType>>> {
788 Ok(Vc::upcast(
789 Vc::<EcmascriptChunkType>::default().resolve().await?,
790 ))
791 }
792
793 #[turbo_tasks::function]
794 fn module(&self) -> Vc<Box<dyn Module>> {
795 *ResolvedVc::upcast(self.module)
796 }
797}
798
799#[turbo_tasks::value_impl]
800impl EcmascriptChunkItem for ModuleChunkItem {
801 #[turbo_tasks::function]
802 fn content(self: Vc<Self>) -> Vc<EcmascriptChunkItemContent> {
803 panic!("content() should not be called");
804 }
805
806 #[turbo_tasks::function]
807 async fn content_with_async_module_info(
808 self: Vc<Self>,
809 async_module_info: Option<Vc<AsyncModuleInfo>>,
810 ) -> Result<Vc<EcmascriptChunkItemContent>> {
811 let span = tracing::info_span!(
812 "code generation",
813 module = self.asset_ident().to_string().await?.to_string()
814 );
815 async {
816 let this = self.await?;
817 let async_module_options = this
818 .module
819 .get_async_module()
820 .module_options(async_module_info);
821
822 let content = this
824 .module
825 .module_content(*this.chunking_context, async_module_info);
826
827 EcmascriptChunkItemContent::new(
828 content,
829 *this.chunking_context,
830 this.module.options(),
831 async_module_options,
832 )
833 .resolve()
834 .await
835 }
836 .instrument(span)
837 .await
838 }
839}
840
841#[turbo_tasks::value(shared)]
843pub struct EcmascriptModuleContent {
844 pub inner_code: Rope,
845 pub source_map: Option<Rope>,
846 pub is_esm: bool,
847 pub strict: bool,
848 pub additional_ids: SmallVec<[ResolvedVc<ModuleId>; 1]>,
849}
850
851#[turbo_tasks::value(shared)]
852#[derive(Clone, Debug, Hash, TaskInput)]
853pub struct EcmascriptModuleContentOptions {
854 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
855 parsed: ResolvedVc<ParseResult>,
856 specified_module_type: SpecifiedModuleType,
857 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
858 references: ResolvedVc<ModuleReferences>,
859 esm_references: ResolvedVc<EsmAssetReferences>,
860 part_references: Vec<ResolvedVc<EcmascriptModulePartReference>>,
861 code_generation: ResolvedVc<CodeGens>,
862 async_module: ResolvedVc<OptionAsyncModule>,
863 generate_source_map: bool,
864 original_source_map: Option<ResolvedVc<Box<dyn GenerateSourceMap>>>,
865 exports: ResolvedVc<EcmascriptExports>,
866 async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
867}
868
869impl EcmascriptModuleContentOptions {
870 async fn merged_code_gens(
871 &self,
872 scope_hoisting_context: ScopeHoistingContext<'_>,
873 eval_context: &EvalContext,
874 ) -> Result<Vec<CodeGeneration>> {
875 let EcmascriptModuleContentOptions {
878 module,
879 chunking_context,
880 references,
881 esm_references,
882 part_references,
883 code_generation,
884 async_module,
885 exports,
886 async_module_info,
887 ..
888 } = self;
889
890 async {
891 let additional_code_gens = [
892 if let Some(async_module) = &*async_module.await? {
893 Some(
894 async_module
895 .code_generation(
896 async_module_info.map(|info| *info),
897 **references,
898 **chunking_context,
899 )
900 .await?,
901 )
902 } else {
903 None
904 },
905 if let EcmascriptExports::EsmExports(exports) = *exports.await? {
906 Some(
907 exports
908 .code_generation(
909 **chunking_context,
910 scope_hoisting_context,
911 eval_context,
912 *module,
913 )
914 .await?,
915 )
916 } else {
917 None
918 },
919 ];
920
921 let esm_code_gens = esm_references
922 .await?
923 .iter()
924 .map(|r| r.code_generation(**chunking_context, scope_hoisting_context))
925 .try_join()
926 .await?;
927
928 let part_code_gens = part_references
929 .iter()
930 .map(|r| r.code_generation(**chunking_context, scope_hoisting_context))
931 .try_join()
932 .await?;
933
934 let code_gens = code_generation
935 .await?
936 .iter()
937 .map(|c| c.code_generation(**chunking_context, scope_hoisting_context))
938 .try_join()
939 .await?;
940
941 anyhow::Ok(
942 esm_code_gens
943 .into_iter()
944 .chain(part_code_gens.into_iter())
945 .chain(additional_code_gens.into_iter().flatten())
946 .chain(code_gens.into_iter())
947 .collect(),
948 )
949 }
950 .instrument(tracing::info_span!("precompute code generation"))
951 .await
952 }
953}
954
955#[turbo_tasks::value_impl]
956impl EcmascriptModuleContent {
957 #[turbo_tasks::function]
959 pub async fn new(input: Vc<EcmascriptModuleContentOptions>) -> Result<Vc<Self>> {
960 let input = input.await?;
961 let EcmascriptModuleContentOptions {
962 parsed,
963 module,
964 specified_module_type,
965 generate_source_map,
966 original_source_map,
967 chunking_context,
968 ..
969 } = &*input;
970
971 async {
972 let minify = chunking_context.minify_type().await?;
973
974 let content = process_parse_result(
975 *parsed,
976 module.ident(),
977 *specified_module_type,
978 *generate_source_map,
979 *original_source_map,
980 *minify,
981 Some(&*input),
982 None,
983 )
984 .await?;
985 emit_content(content, Default::default()).await
986 }
987 .instrument(tracing::info_span!("gen content with code gens"))
988 .await
989 }
990
991 #[turbo_tasks::function]
993 pub async fn new_without_analysis(
994 parsed: Vc<ParseResult>,
995 ident: Vc<AssetIdent>,
996 specified_module_type: SpecifiedModuleType,
997 generate_source_map: bool,
998 ) -> Result<Vc<Self>> {
999 let content = process_parse_result(
1000 parsed.to_resolved().await?,
1001 ident,
1002 specified_module_type,
1003 generate_source_map,
1004 None,
1005 MinifyType::NoMinify,
1006 None,
1007 None,
1008 )
1009 .await?;
1010 emit_content(content, Default::default()).await
1011 }
1012
1013 #[turbo_tasks::function]
1020 pub async fn new_merged(
1021 modules: Vec<(
1022 ResolvedVc<Box<dyn EcmascriptAnalyzable>>,
1023 MergeableModuleExposure,
1024 )>,
1025 module_options: Vec<Vc<EcmascriptModuleContentOptions>>,
1026 entry_points: Vec<ResolvedVc<Box<dyn EcmascriptAnalyzable>>>,
1027 ) -> Result<Vc<Self>> {
1028 async {
1029 let modules = modules
1030 .into_iter()
1031 .map(|(m, exposed)| {
1032 (
1033 ResolvedVc::try_sidecast::<Box<dyn EcmascriptChunkPlaceable>>(m).unwrap(),
1034 exposed,
1035 )
1036 })
1037 .collect::<FxIndexMap<_, _>>();
1038 let entry_points = entry_points
1039 .into_iter()
1040 .map(|m| {
1041 let m =
1042 ResolvedVc::try_sidecast::<Box<dyn EcmascriptChunkPlaceable>>(m).unwrap();
1043 (m, modules.get_index_of(&m).unwrap())
1044 })
1045 .collect::<Vec<_>>();
1046
1047 let globals_merged = Globals::default();
1048
1049 let contents = module_options
1050 .iter()
1051 .map(async |options| {
1052 let options = options.await?;
1053 let EcmascriptModuleContentOptions {
1054 chunking_context,
1055 parsed,
1056 module,
1057 specified_module_type,
1058 generate_source_map,
1059 original_source_map,
1060 ..
1061 } = &*options;
1062
1063 let result = process_parse_result(
1064 *parsed,
1065 module.ident(),
1066 *specified_module_type,
1067 *generate_source_map,
1068 *original_source_map,
1069 *chunking_context.minify_type().await?,
1070 Some(&*options),
1071 Some(ScopeHoistingOptions {
1072 module: *module,
1073 modules: &modules,
1074 }),
1075 )
1076 .await?;
1077
1078 Ok((*module, result))
1079 })
1080 .try_join()
1081 .await?;
1082
1083 let (merged_ast, comments, source_maps, original_source_maps) =
1084 merge_modules(contents, &entry_points, &globals_merged).await?;
1085
1086 let options = module_options.last().unwrap().await?;
1089
1090 let modules_header_width = modules.len().next_power_of_two().trailing_zeros();
1091 let content = CodeGenResult {
1092 program: merged_ast,
1093 source_map: CodeGenResultSourceMap::ScopeHoisting {
1094 modules_header_width,
1095 source_maps,
1096 },
1097 comments: CodeGenResultComments::ScopeHoisting {
1098 modules_header_width,
1099 comments,
1100 },
1101 is_esm: true,
1102 strict: true,
1103 original_source_map: CodeGenResultOriginalSourceMap::ScopeHoisting(
1104 original_source_maps,
1105 ),
1106 minify: *options.chunking_context.minify_type().await?,
1107 scope_hoisting_syntax_contexts: None,
1108 };
1109
1110 let first_entry = entry_points.first().unwrap().0;
1111 let additional_ids = modules
1112 .keys()
1113 .filter(|m| {
1120 **m != first_entry
1121 && *modules.get(*m).unwrap() == MergeableModuleExposure::External
1122 })
1123 .map(|m| m.chunk_item_id(*options.chunking_context).to_resolved())
1124 .try_join()
1125 .await?
1126 .into();
1127
1128 emit_content(content, additional_ids)
1129 .instrument(tracing::info_span!("emit"))
1130 .await
1131 }
1132 .instrument(tracing::info_span!(
1133 "merged EcmascriptModuleContent",
1134 modules = module_options.len()
1135 ))
1136 .await
1137 }
1138}
1139
1140#[instrument(level = Level::TRACE, skip_all, name = "merge")]
1149#[allow(clippy::type_complexity)]
1150async fn merge_modules(
1151 mut contents: Vec<(ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, CodeGenResult)>,
1152 entry_points: &Vec<(ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, usize)>,
1153 globals_merged: &'_ Globals,
1154) -> Result<(
1155 Program,
1156 Vec<CodeGenResultComments>,
1157 Vec<CodeGenResultSourceMap>,
1158 SmallVec<[ResolvedVc<Box<dyn GenerateSourceMap>>; 1]>,
1159)> {
1160 struct SetSyntaxContextVisitor<'a> {
1161 modules_header_width: u32,
1162 current_module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1163 current_module_idx: u32,
1164 reverse_module_contexts:
1166 FxHashMap<SyntaxContext, ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
1167 export_contexts:
1170 &'a FxHashMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, &'a FxHashMap<RcStr, Id>>,
1171 unique_contexts_cache: &'a mut FxHashMap<
1174 (ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, SyntaxContext),
1175 SyntaxContext,
1176 >,
1177 }
1178
1179 impl<'a> SetSyntaxContextVisitor<'a> {
1180 fn get_context_for(
1181 &mut self,
1182 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1183 local_ctxt: SyntaxContext,
1184 ) -> SyntaxContext {
1185 if let Some(&global_ctxt) = self.unique_contexts_cache.get(&(module, local_ctxt)) {
1186 global_ctxt
1187 } else {
1188 let global_ctxt = SyntaxContext::empty().apply_mark(Mark::new());
1189 self.unique_contexts_cache
1190 .insert((module, local_ctxt), global_ctxt);
1191 global_ctxt
1192 }
1193 }
1194 }
1195
1196 impl VisitMut for SetSyntaxContextVisitor<'_> {
1197 fn visit_mut_ident(&mut self, ident: &mut Ident) {
1198 let Ident {
1199 sym, ctxt, span, ..
1200 } = ident;
1201
1202 if let Some(&module) = self.reverse_module_contexts.get(ctxt) {
1205 let eval_context_exports = self.export_contexts.get(&module).unwrap();
1206 let sym_rc_str: RcStr = sym.as_str().into();
1208 let (local, local_ctxt) = if let Some((local, local_ctxt)) =
1209 eval_context_exports.get(&sym_rc_str)
1210 {
1211 (Some(local), *local_ctxt)
1212 } else if sym.starts_with("__TURBOPACK__imported__module__") {
1213 (None, SyntaxContext::empty())
1218 } else {
1219 panic!(
1220 "Expected to find a local export for {sym} with ctxt {ctxt:#?} in \
1221 {eval_context_exports:?}",
1222 );
1223 };
1224
1225 let global_ctxt = self.get_context_for(module, local_ctxt);
1226
1227 if let Some(local) = local {
1228 *sym = local.clone();
1229 }
1230 *ctxt = global_ctxt;
1231 span.visit_mut_with(self);
1232 } else {
1233 ident.visit_mut_children_with(self);
1234 }
1235 }
1236
1237 fn visit_mut_syntax_context(&mut self, local_ctxt: &mut SyntaxContext) {
1238 let module = self
1241 .reverse_module_contexts
1242 .get(local_ctxt)
1243 .copied()
1244 .unwrap_or(self.current_module);
1245
1246 let global_ctxt = self.get_context_for(module, *local_ctxt);
1247 *local_ctxt = global_ctxt;
1248 }
1249 fn visit_mut_span(&mut self, span: &mut Span) {
1250 span.lo = CodeGenResultComments::encode_bytepos(
1253 self.modules_header_width,
1254 self.current_module_idx,
1255 span.lo,
1256 );
1257 span.hi = CodeGenResultComments::encode_bytepos(
1258 self.modules_header_width,
1259 self.current_module_idx,
1260 span.hi,
1261 );
1262 }
1263 }
1264
1265 let mut programs = contents
1268 .iter_mut()
1269 .map(|(_, content)| content.program.take())
1270 .collect::<Vec<_>>();
1271
1272 let export_contexts = contents
1273 .iter()
1274 .map(|(module, content)| {
1275 Ok((
1276 *module,
1277 content
1278 .scope_hoisting_syntax_contexts
1279 .as_ref()
1280 .map(|(_, export_contexts)| export_contexts)
1281 .context("expected exports contexts")?,
1282 ))
1283 })
1284 .collect::<Result<FxHashMap<_, _>>>()?;
1285
1286 let (merged_ast, inserted) = GLOBALS.set(globals_merged, || {
1287 let _ = tracing::trace_span!("merge inner").entered();
1288 let mut unique_contexts_cache =
1290 FxHashMap::with_capacity_and_hasher(contents.len() * 5, Default::default());
1291
1292 let mut prepare_module =
1293 |module_count: usize,
1294 current_module_idx: usize,
1295 (module, content): &(ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, CodeGenResult),
1296 program: &mut Program| {
1297 let _ = tracing::trace_span!("prepare module").entered();
1298 if let CodeGenResult {
1299 scope_hoisting_syntax_contexts: Some((module_contexts, _)),
1300 ..
1301 } = content
1302 {
1303 let modules_header_width = module_count.next_power_of_two().trailing_zeros();
1304 GLOBALS.set(globals_merged, || {
1305 program.visit_mut_with(&mut SetSyntaxContextVisitor {
1306 modules_header_width,
1307 current_module: *module,
1308 current_module_idx: current_module_idx as u32,
1309 reverse_module_contexts: module_contexts
1310 .iter()
1311 .map(|e| (*e.value(), *e.key()))
1312 .collect(),
1313 export_contexts: &export_contexts,
1314 unique_contexts_cache: &mut unique_contexts_cache,
1315 });
1316 anyhow::Ok(())
1317 })?;
1318
1319 Ok(match program.take() {
1320 Program::Module(module) => Either::Left(module.body.into_iter()),
1321 Program::Script(script) => {
1324 Either::Right(script.body.into_iter().map(ModuleItem::Stmt))
1325 }
1326 })
1327 } else {
1328 bail!("Expected scope_hosting_syntax_contexts");
1329 }
1330 };
1331
1332 let mut inserted = FxHashSet::with_capacity_and_hasher(contents.len(), Default::default());
1333 inserted.extend(entry_points.iter().map(|(_, i)| *i));
1335
1336 let mut inserted_imports = FxHashMap::default();
1337
1338 let span = tracing::trace_span!("merge ASTs");
1339 let mut queue = entry_points
1342 .iter()
1343 .map(|(_, i)| prepare_module(contents.len(), *i, &contents[*i], &mut programs[*i]))
1344 .flatten_ok()
1345 .rev()
1346 .collect::<Result<Vec<_>>>()?;
1347 let mut result = vec![];
1348 while let Some(item) = queue.pop() {
1349 if let ModuleItem::Stmt(stmt) = &item {
1350 match stmt {
1351 Stmt::Expr(ExprStmt { expr, .. }) => {
1352 if let Expr::Call(CallExpr {
1353 callee: Callee::Expr(callee),
1354 args,
1355 ..
1356 }) = &**expr
1357 && callee.is_ident_ref_to("__turbopack_merged_esm__")
1358 {
1359 let index =
1360 args[0].expr.as_lit().unwrap().as_num().unwrap().value as usize;
1361
1362 if inserted.insert(index) {
1364 queue.extend(
1365 prepare_module(
1366 contents.len(),
1367 index,
1368 &contents[index],
1369 &mut programs[index],
1370 )?
1371 .into_iter()
1372 .rev(),
1373 );
1374 }
1375 continue;
1376 }
1377 }
1378 Stmt::Decl(Decl::Var(var)) => {
1379 if let [decl] = &*var.decls
1380 && let Some(name) = decl.name.as_ident()
1381 && name.sym.starts_with("__TURBOPACK__imported__module__")
1382 {
1383 match inserted_imports.entry(name.sym.clone()) {
1388 Entry::Occupied(entry) => {
1389 let entry_ctxt = *entry.get();
1394 let new = Ident::new(name.sym.clone(), DUMMY_SP, name.ctxt);
1395 let old = Ident::new(name.sym.clone(), DUMMY_SP, entry_ctxt);
1396 result.push(ModuleItem::Stmt(
1397 quote!("var $new = $old;" as Stmt,
1398 new: Ident = new,
1399 old: Ident = old
1400 ),
1401 ));
1402 continue;
1403 }
1404 Entry::Vacant(entry) => {
1405 entry.insert(name.ctxt);
1406 }
1407 }
1408 }
1409 }
1410 _ => (),
1411 }
1412 }
1413
1414 result.push(item);
1415 }
1416 drop(span);
1417
1418 let span = tracing::trace_span!("hygiene").entered();
1419 let mut merged_ast = Program::Module(swc_core::ecma::ast::Module {
1420 body: result,
1421 span: DUMMY_SP,
1422 shebang: None,
1423 });
1424 merged_ast.visit_mut_with(&mut swc_core::ecma::transforms::base::hygiene::hygiene());
1425 drop(span);
1426
1427 anyhow::Ok((merged_ast, inserted))
1428 })?;
1429
1430 debug_assert!(
1431 inserted.len() == contents.len(),
1432 "Not all merged modules were inserted: {:?}",
1433 contents
1434 .iter()
1435 .enumerate()
1436 .map(async |(i, m)| Ok((inserted.contains(&i), m.0.ident().to_string().await?)))
1437 .try_join()
1438 .await?,
1439 );
1440
1441 let comments = contents
1442 .iter_mut()
1443 .map(|(_, content)| content.comments.take())
1444 .collect::<Vec<_>>();
1445
1446 let source_maps = contents
1447 .iter_mut()
1448 .map(|(_, content)| std::mem::take(&mut content.source_map))
1449 .collect::<Vec<_>>();
1450
1451 let original_source_maps = contents
1452 .iter_mut()
1453 .flat_map(|(_, content)| match content.original_source_map {
1454 CodeGenResultOriginalSourceMap::ScopeHoisting(_) => unreachable!(
1455 "Didn't expect nested CodeGenResultOriginalSourceMap::ScopeHoisting: {:?}",
1456 content.original_source_map
1457 ),
1458 CodeGenResultOriginalSourceMap::Single(map) => map,
1459 })
1460 .collect();
1461
1462 Ok((merged_ast, comments, source_maps, original_source_maps))
1463}
1464
1465#[derive(Clone, Copy)]
1470pub enum ScopeHoistingContext<'a> {
1471 Some {
1472 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1474 modules:
1476 &'a FxIndexMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, MergeableModuleExposure>,
1477
1478 is_import_mark: Mark,
1479 globals: &'a Arc<Globals>,
1480 module_syntax_contexts_cache:
1482 &'a FxDashMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, SyntaxContext>,
1483 },
1484 None,
1485}
1486
1487impl<'a> ScopeHoistingContext<'a> {
1488 pub fn module(&self) -> Option<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>> {
1490 match self {
1491 ScopeHoistingContext::Some { module, .. } => Some(*module),
1492 ScopeHoistingContext::None => None,
1493 }
1494 }
1495
1496 pub fn skip_module_exports(&self) -> bool {
1498 match self {
1499 ScopeHoistingContext::Some {
1500 module, modules, ..
1501 } => match modules.get(module).unwrap() {
1502 MergeableModuleExposure::None => true,
1503 MergeableModuleExposure::Internal | MergeableModuleExposure::External => false,
1504 },
1505 ScopeHoistingContext::None => false,
1506 }
1507 }
1508
1509 pub fn get_module_syntax_context(
1511 &self,
1512 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1513 ) -> Option<SyntaxContext> {
1514 match self {
1515 ScopeHoistingContext::Some {
1516 modules,
1517 module_syntax_contexts_cache,
1518 globals,
1519 is_import_mark,
1520 ..
1521 } => {
1522 if !modules.contains_key(&module) {
1523 return None;
1524 }
1525
1526 Some(match module_syntax_contexts_cache.entry(module) {
1527 dashmap::Entry::Occupied(e) => *e.get(),
1528 dashmap::Entry::Vacant(e) => {
1529 let ctxt = GLOBALS.set(globals, || {
1530 let mark = Mark::fresh(*is_import_mark);
1531 SyntaxContext::empty()
1532 .apply_mark(*is_import_mark)
1533 .apply_mark(mark)
1534 });
1535
1536 e.insert(ctxt);
1537 ctxt
1538 }
1539 })
1540 }
1541 ScopeHoistingContext::None => None,
1542 }
1543 }
1544
1545 pub fn get_module_index(
1546 &self,
1547 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1548 ) -> Option<usize> {
1549 match self {
1550 ScopeHoistingContext::Some { modules, .. } => modules.get_index_of(&module),
1551 ScopeHoistingContext::None => None,
1552 }
1553 }
1554}
1555
1556struct CodeGenResult {
1557 program: Program,
1558 source_map: CodeGenResultSourceMap,
1559 comments: CodeGenResultComments,
1560 is_esm: bool,
1561 strict: bool,
1562 original_source_map: CodeGenResultOriginalSourceMap,
1563 minify: MinifyType,
1564 #[allow(clippy::type_complexity)]
1565 scope_hoisting_syntax_contexts: Option<(
1567 FxDashMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable + 'static>>, SyntaxContext>,
1568 FxHashMap<RcStr, Id>,
1569 )>,
1570}
1571
1572struct ScopeHoistingOptions<'a> {
1573 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1574 modules: &'a FxIndexMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, MergeableModuleExposure>,
1575}
1576
1577#[instrument(level = Level::TRACE, skip_all, name = "process module")]
1578async fn process_parse_result(
1579 parsed: ResolvedVc<ParseResult>,
1580 ident: Vc<AssetIdent>,
1581 specified_module_type: SpecifiedModuleType,
1582 generate_source_map: bool,
1583 original_source_map: Option<ResolvedVc<Box<dyn GenerateSourceMap>>>,
1584 minify: MinifyType,
1585 options: Option<&EcmascriptModuleContentOptions>,
1586 scope_hoisting_options: Option<ScopeHoistingOptions<'_>>,
1587) -> Result<CodeGenResult> {
1588 with_consumed_parse_result(
1589 parsed,
1590 async |mut program, source_map, globals, eval_context, comments| -> Result<CodeGenResult> {
1591 let (top_level_mark, is_esm, strict) = eval_context
1592 .as_ref()
1593 .map_either(
1594 |e| {
1595 (
1596 e.top_level_mark,
1597 e.is_esm(specified_module_type),
1598 e.imports.strict,
1599 )
1600 },
1601 |e| {
1602 (
1603 e.top_level_mark,
1604 e.is_esm(specified_module_type),
1605 e.imports.strict,
1606 )
1607 },
1608 )
1609 .into_inner();
1610
1611 let (mut code_gens, retain_syntax_context, prepend_ident_comment) =
1612 if let Some(scope_hoisting_options) = scope_hoisting_options {
1613 let is_import_mark = GLOBALS.set(globals, || Mark::new());
1614
1615 let module_syntax_contexts_cache = FxDashMap::default();
1616 let ctx = ScopeHoistingContext::Some {
1617 module: scope_hoisting_options.module,
1618 modules: scope_hoisting_options.modules,
1619 module_syntax_contexts_cache: &module_syntax_contexts_cache,
1620 is_import_mark,
1621 globals,
1622 };
1623 let code_gens = options
1624 .unwrap()
1625 .merged_code_gens(
1626 ctx,
1627 match &eval_context {
1628 Either::Left(e) => e,
1629 Either::Right(e) => e,
1630 },
1631 )
1632 .await?;
1633
1634 let export_contexts = eval_context
1635 .map_either(
1636 |e| Cow::Owned(e.imports.exports),
1637 |e| Cow::Borrowed(&e.imports.exports),
1638 )
1639 .into_inner();
1640 let preserved_exports =
1641 match &*scope_hoisting_options.module.get_exports().await? {
1642 EcmascriptExports::EsmExports(exports) => exports
1643 .await?
1644 .exports
1645 .iter()
1646 .filter(|(_, e)| matches!(e, export::EsmExport::LocalBinding(_, _)))
1647 .map(|(name, e)| {
1648 if let Some((sym, ctxt)) = export_contexts.get(name) {
1649 Ok((sym.clone(), *ctxt))
1650 } else {
1651 bail!("Couldn't find export {} for binding {:?}", name, e);
1652 }
1653 })
1654 .collect::<Result<FxHashSet<_>>>()?,
1655 _ => Default::default(),
1656 };
1657
1658 let prepend_ident_comment = if matches!(minify, MinifyType::NoMinify) {
1659 Some(Comment {
1660 kind: CommentKind::Line,
1661 span: DUMMY_SP,
1662 text: format!(" MERGED MODULE: {}", ident.to_string().await?).into(),
1663 })
1664 } else {
1665 None
1666 };
1667
1668 (
1669 code_gens,
1670 Some((
1671 is_import_mark,
1672 module_syntax_contexts_cache,
1673 preserved_exports,
1674 export_contexts,
1675 )),
1676 prepend_ident_comment,
1677 )
1678 } else if let Some(options) = options {
1679 (
1680 options
1681 .merged_code_gens(
1682 ScopeHoistingContext::None,
1683 match &eval_context {
1684 Either::Left(e) => e,
1685 Either::Right(e) => e,
1686 },
1687 )
1688 .await?,
1689 None,
1690 None,
1691 )
1692 } else {
1693 (vec![], None, None)
1694 };
1695
1696 let extra_comments = SwcComments {
1697 leading: Default::default(),
1698 trailing: Default::default(),
1699 };
1700
1701 process_content_with_code_gens(&mut program, globals, &mut code_gens);
1702
1703 for comments in code_gens.iter_mut().flat_map(|cg| cg.comments.as_mut()) {
1704 let leading = Arc::unwrap_or_clone(take(&mut comments.leading));
1705 let trailing = Arc::unwrap_or_clone(take(&mut comments.trailing));
1706
1707 for (pos, v) in leading {
1708 extra_comments.leading.entry(pos).or_default().extend(v);
1709 }
1710
1711 for (pos, v) in trailing {
1712 extra_comments.trailing.entry(pos).or_default().extend(v);
1713 }
1714 }
1715
1716 GLOBALS.set(globals, || {
1717 if let Some(prepend_ident_comment) = prepend_ident_comment {
1718 let span = Span::dummy_with_cmt();
1719 extra_comments.add_leading(span.lo, prepend_ident_comment);
1720 let stmt = Stmt::Empty(EmptyStmt { span });
1721 match &mut program {
1722 Program::Module(module) => module.body.prepend_stmt(ModuleItem::Stmt(stmt)),
1723 Program::Script(script) => script.body.prepend_stmt(stmt),
1724 }
1725 }
1726
1727 if let Some((is_import_mark, _, preserved_exports, _)) = &retain_syntax_context {
1728 program.visit_mut_with(&mut hygiene_rename_only(
1729 Some(top_level_mark),
1730 *is_import_mark,
1731 preserved_exports,
1732 ));
1733 } else {
1734 program.visit_mut_with(
1735 &mut swc_core::ecma::transforms::base::hygiene::hygiene_with_config(
1736 swc_core::ecma::transforms::base::hygiene::Config {
1737 top_level_mark,
1738 ..Default::default()
1739 },
1740 ),
1741 );
1742 }
1743 program.visit_mut_with(&mut swc_core::ecma::transforms::base::fixer::fixer(None));
1744
1745 remove_shebang(&mut program);
1748 remove_directives(&mut program);
1749 });
1750
1751 Ok(CodeGenResult {
1752 program,
1753 source_map: if generate_source_map {
1754 CodeGenResultSourceMap::Single {
1755 source_map: source_map.clone(),
1756 }
1757 } else {
1758 CodeGenResultSourceMap::None
1759 },
1760 comments: CodeGenResultComments::Single {
1761 comments,
1762 extra_comments,
1763 },
1764 is_esm,
1765 strict,
1766 original_source_map: CodeGenResultOriginalSourceMap::Single(original_source_map),
1767 minify,
1768 scope_hoisting_syntax_contexts: retain_syntax_context
1769 .map(|(_, ctxts, _, export_contexts)| (ctxts, export_contexts.into_owned())),
1771 })
1772 },
1773 async |parse_result| -> Result<CodeGenResult> {
1774 Ok(match parse_result {
1775 ParseResult::Ok { .. } => unreachable!(),
1776 ParseResult::Unparsable { messages } => {
1777 let path = ident.path().to_string().await?;
1778 let error_messages = messages
1779 .as_ref()
1780 .and_then(|m| m.first().map(|f| format!("\n{f}")))
1781 .unwrap_or("".into());
1782 let msg = format!("Could not parse module '{path}'\n{error_messages}");
1783 let body = vec![
1784 quote!(
1785 "const e = new Error($msg);" as Stmt,
1786 msg: Expr = Expr::Lit(msg.into()),
1787 ),
1788 quote!("e.code = 'MODULE_UNPARSABLE';" as Stmt),
1789 quote!("throw e;" as Stmt),
1790 ];
1791
1792 CodeGenResult {
1793 program: Program::Script(Script {
1794 span: DUMMY_SP,
1795 body,
1796 shebang: None,
1797 }),
1798 source_map: CodeGenResultSourceMap::None,
1799 comments: CodeGenResultComments::Empty,
1800 is_esm: false,
1801 strict: false,
1802 original_source_map: CodeGenResultOriginalSourceMap::Single(None),
1803 minify: MinifyType::NoMinify,
1804 scope_hoisting_syntax_contexts: None,
1805 }
1806 }
1807 ParseResult::NotFound => {
1808 let path = ident.path().to_string().await?;
1809 let msg = format!("Could not parse module '{path}'");
1810 let body = vec![
1811 quote!(
1812 "const e = new Error($msg);" as Stmt,
1813 msg: Expr = Expr::Lit(msg.into()),
1814 ),
1815 quote!("e.code = 'MODULE_UNPARSABLE';" as Stmt),
1816 quote!("throw e;" as Stmt),
1817 ];
1818 CodeGenResult {
1819 program: Program::Script(Script {
1820 span: DUMMY_SP,
1821 body,
1822 shebang: None,
1823 }),
1824 source_map: CodeGenResultSourceMap::None,
1825 comments: CodeGenResultComments::Empty,
1826 is_esm: false,
1827 strict: false,
1828 original_source_map: CodeGenResultOriginalSourceMap::Single(None),
1829 minify: MinifyType::NoMinify,
1830 scope_hoisting_syntax_contexts: None,
1831 }
1832 }
1833 })
1834 },
1835 )
1836 .await
1837}
1838
1839async fn with_consumed_parse_result<T>(
1841 parsed: ResolvedVc<ParseResult>,
1842 success: impl AsyncFnOnce(
1843 Program,
1844 &Arc<SourceMap>,
1845 &Arc<Globals>,
1846 Either<EvalContext, &'_ EvalContext>,
1847 Either<ImmutableComments, Arc<ImmutableComments>>,
1848 ) -> Result<T>,
1849 error: impl AsyncFnOnce(&ParseResult) -> Result<T>,
1850) -> Result<T> {
1851 let parsed = parsed.final_read_hint().await?;
1852 match &*parsed {
1853 ParseResult::Ok { .. } => {
1854 let mut parsed = ReadRef::try_unwrap(parsed);
1855 let (program, source_map, globals, eval_context, comments) = match &mut parsed {
1856 Ok(ParseResult::Ok {
1857 program,
1858 source_map,
1859 globals,
1860 eval_context,
1861 comments,
1862 }) => (
1863 program.take(),
1864 &*source_map,
1865 &*globals,
1866 Either::Left(std::mem::replace(
1867 eval_context,
1868 EvalContext {
1869 unresolved_mark: eval_context.unresolved_mark,
1870 top_level_mark: eval_context.top_level_mark,
1871 imports: Default::default(),
1872 force_free_values: Default::default(),
1873 },
1874 )),
1875 match Arc::try_unwrap(take(comments)) {
1876 Ok(comments) => Either::Left(comments),
1877 Err(comments) => Either::Right(comments),
1878 },
1879 ),
1880 Err(parsed) => {
1881 let ParseResult::Ok {
1882 program,
1883 source_map,
1884 globals,
1885 eval_context,
1886 comments,
1887 } = &**parsed
1888 else {
1889 unreachable!();
1890 };
1891 (
1892 program.clone(),
1893 source_map,
1894 globals,
1895 Either::Right(eval_context),
1896 Either::Right(comments.clone()),
1897 )
1898 }
1899 _ => unreachable!(),
1900 };
1901
1902 success(program, source_map, globals, eval_context, comments).await
1903 }
1904 _ => error(&parsed).await,
1905 }
1906}
1907
1908async fn emit_content(
1909 content: CodeGenResult,
1910 additional_ids: SmallVec<[ResolvedVc<ModuleId>; 1]>,
1911) -> Result<Vc<EcmascriptModuleContent>> {
1912 let CodeGenResult {
1913 program,
1914 source_map,
1915 comments,
1916 is_esm,
1917 strict,
1918 original_source_map,
1919 minify,
1920 scope_hoisting_syntax_contexts: _,
1921 } = content;
1922
1923 let generate_source_map = source_map.is_some();
1924
1925 let mut bytes: Vec<u8> = vec![];
1926 let mut mappings = vec![];
1930
1931 let source_map = Arc::new(source_map);
1932
1933 {
1934 let mut wr = JsWriter::new(
1935 Default::default(),
1937 "\n",
1938 &mut bytes,
1939 generate_source_map.then_some(&mut mappings),
1940 );
1941 if matches!(minify, MinifyType::Minify { .. }) {
1942 wr.set_indent_str("");
1943 }
1944
1945 let comments = comments.consumable();
1946
1947 let mut emitter = Emitter {
1948 cfg: swc_core::ecma::codegen::Config::default(),
1949 cm: source_map.clone(),
1950 comments: Some(&comments as &dyn Comments),
1951 wr,
1952 };
1953
1954 emitter.emit_program(&program)?;
1955 drop(program);
1957 }
1958
1959 let source_map = if generate_source_map {
1960 Some(generate_js_source_map(
1961 &*source_map,
1962 mappings,
1963 original_source_map
1964 .iter()
1965 .map(|map| map.generate_source_map())
1966 .try_flat_join()
1967 .await?,
1968 matches!(
1969 original_source_map,
1970 CodeGenResultOriginalSourceMap::Single(_)
1971 ),
1972 true,
1973 )?)
1974 } else {
1975 None
1976 };
1977
1978 Ok(EcmascriptModuleContent {
1979 inner_code: bytes.into(),
1980 source_map,
1981 is_esm,
1982 strict,
1983 additional_ids,
1984 }
1985 .cell())
1986}
1987
1988#[instrument(level = Level::TRACE, skip_all, name = "apply code generation")]
1989fn process_content_with_code_gens(
1990 program: &mut Program,
1991 globals: &Globals,
1992 code_gens: &mut Vec<CodeGeneration>,
1993) {
1994 let mut visitors = Vec::new();
1995 let mut root_visitors = Vec::new();
1996 let mut early_hoisted_stmts = FxIndexMap::default();
1997 let mut hoisted_stmts = FxIndexMap::default();
1998 for code_gen in code_gens {
1999 for CodeGenerationHoistedStmt { key, stmt } in code_gen.hoisted_stmts.drain(..) {
2000 hoisted_stmts.entry(key).or_insert(stmt);
2001 }
2002 for CodeGenerationHoistedStmt { key, stmt } in code_gen.early_hoisted_stmts.drain(..) {
2003 early_hoisted_stmts.insert(key.clone(), stmt);
2004 }
2005
2006 for (path, visitor) in &code_gen.visitors {
2007 if path.is_empty() {
2008 root_visitors.push(&**visitor);
2009 } else {
2010 visitors.push((path, &**visitor));
2011 }
2012 }
2013 }
2014
2015 GLOBALS.set(globals, || {
2016 if !visitors.is_empty() {
2017 program.visit_mut_with_ast_path(
2018 &mut ApplyVisitors::new(visitors),
2019 &mut Default::default(),
2020 );
2021 }
2022 for pass in root_visitors {
2023 program.modify(pass);
2024 }
2025 });
2026
2027 match program {
2028 Program::Module(ast::Module { body, .. }) => {
2029 body.splice(
2030 0..0,
2031 early_hoisted_stmts
2032 .into_values()
2033 .chain(hoisted_stmts.into_values())
2034 .map(ModuleItem::Stmt),
2035 );
2036 }
2037 Program::Script(Script { body, .. }) => {
2038 body.splice(
2039 0..0,
2040 early_hoisted_stmts
2041 .into_values()
2042 .chain(hoisted_stmts.into_values()),
2043 );
2044 }
2045 };
2046}
2047
2048fn hygiene_rename_only(
2055 top_level_mark: Option<Mark>,
2056 is_import_mark: Mark,
2057 preserved_exports: &FxHashSet<Id>,
2058) -> impl VisitMut {
2059 struct HygieneRenamer<'a> {
2060 preserved_exports: &'a FxHashSet<Id>,
2061 is_import_mark: Mark,
2062 }
2063 impl swc_core::ecma::transforms::base::rename::Renamer for HygieneRenamer<'_> {
2065 const MANGLE: bool = false;
2066 const RESET_N: bool = true;
2067
2068 fn new_name_for(&self, orig: &Id, n: &mut usize) -> Atom {
2069 let res = if *n == 0 {
2070 orig.0.clone()
2071 } else {
2072 format!("{}{}", orig.0, n).into()
2073 };
2074 *n += 1;
2075 res
2076 }
2077
2078 fn preserve_name(&self, orig: &Id) -> bool {
2079 self.preserved_exports.contains(orig) || orig.1.has_mark(self.is_import_mark)
2080 }
2081 }
2082 swc_core::ecma::transforms::base::rename::renamer(
2083 swc_core::ecma::transforms::base::hygiene::Config {
2084 top_level_mark: top_level_mark.unwrap_or_default(),
2085 ..Default::default()
2086 },
2087 HygieneRenamer {
2088 preserved_exports,
2089 is_import_mark,
2090 },
2091 )
2092}
2093
2094#[derive(Default)]
2095enum CodeGenResultSourceMap {
2096 #[default]
2097 None,
2099 Single {
2100 source_map: Arc<SourceMap>,
2101 },
2102 ScopeHoisting {
2103 modules_header_width: u32,
2106 source_maps: Vec<CodeGenResultSourceMap>,
2107 },
2108}
2109
2110impl CodeGenResultSourceMap {
2111 fn is_some(&self) -> bool {
2112 match self {
2113 CodeGenResultSourceMap::None => false,
2114 CodeGenResultSourceMap::Single { .. }
2115 | CodeGenResultSourceMap::ScopeHoisting { .. } => true,
2116 }
2117 }
2118}
2119
2120impl Debug for CodeGenResultSourceMap {
2121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2122 match self {
2123 CodeGenResultSourceMap::None => write!(f, "CodeGenResultSourceMap::None"),
2124 CodeGenResultSourceMap::Single { source_map } => {
2125 write!(
2126 f,
2127 "CodeGenResultSourceMap::Single {{ source_map: {:?} }}",
2128 source_map.files().clone()
2129 )
2130 }
2131 CodeGenResultSourceMap::ScopeHoisting {
2132 modules_header_width,
2133 source_maps,
2134 } => write!(
2135 f,
2136 "CodeGenResultSourceMap::ScopeHoisting {{ modules_header_width: \
2137 {modules_header_width}, source_maps: {source_maps:?} }}",
2138 ),
2139 }
2140 }
2141}
2142
2143impl Files for CodeGenResultSourceMap {
2144 fn try_lookup_source_file(
2145 &self,
2146 pos: BytePos,
2147 ) -> Result<Option<Arc<SourceFile>>, SourceMapLookupError> {
2148 match self {
2149 CodeGenResultSourceMap::None => Ok(None),
2150 CodeGenResultSourceMap::Single { source_map } => source_map.try_lookup_source_file(pos),
2151 CodeGenResultSourceMap::ScopeHoisting {
2152 modules_header_width,
2153 source_maps,
2154 } => {
2155 let (module, pos) =
2156 CodeGenResultComments::decode_bytepos(*modules_header_width, pos);
2157 source_maps[module].try_lookup_source_file(pos)
2158 }
2159 }
2160 }
2161
2162 fn is_in_file(&self, f: &Arc<SourceFile>, raw_pos: BytePos) -> bool {
2163 match self {
2164 CodeGenResultSourceMap::None => false,
2165 CodeGenResultSourceMap::Single { .. } => f.start_pos <= raw_pos && raw_pos < f.end_pos,
2166 CodeGenResultSourceMap::ScopeHoisting { .. } => {
2167 false
2173 }
2174 }
2175 }
2176
2177 fn map_raw_pos(&self, pos: BytePos) -> BytePos {
2178 match self {
2179 CodeGenResultSourceMap::None => BytePos::DUMMY,
2180 CodeGenResultSourceMap::Single { .. } => pos,
2181 CodeGenResultSourceMap::ScopeHoisting {
2182 modules_header_width,
2183 ..
2184 } => CodeGenResultComments::decode_bytepos(*modules_header_width, pos).1,
2185 }
2186 }
2187}
2188
2189impl SourceMapper for CodeGenResultSourceMap {
2190 fn lookup_char_pos(&self, pos: BytePos) -> Loc {
2191 match self {
2192 CodeGenResultSourceMap::None => {
2193 panic!("CodeGenResultSourceMap::None cannot lookup_char_pos")
2194 }
2195 CodeGenResultSourceMap::Single { source_map } => source_map.lookup_char_pos(pos),
2196 CodeGenResultSourceMap::ScopeHoisting {
2197 modules_header_width,
2198 source_maps,
2199 } => {
2200 let (module, pos) =
2201 CodeGenResultComments::decode_bytepos(*modules_header_width, pos);
2202 source_maps[module].lookup_char_pos(pos)
2203 }
2204 }
2205 }
2206 fn span_to_lines(&self, sp: Span) -> FileLinesResult {
2207 match self {
2208 CodeGenResultSourceMap::None => {
2209 panic!("CodeGenResultSourceMap::None cannot span_to_lines")
2210 }
2211 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_lines(sp),
2212 CodeGenResultSourceMap::ScopeHoisting {
2213 modules_header_width,
2214 source_maps,
2215 } => {
2216 let (module, lo) =
2217 CodeGenResultComments::decode_bytepos(*modules_header_width, sp.lo);
2218 source_maps[module].span_to_lines(Span {
2219 lo,
2220 hi: CodeGenResultComments::decode_bytepos(*modules_header_width, sp.hi).1,
2221 })
2222 }
2223 }
2224 }
2225 fn span_to_string(&self, sp: Span) -> String {
2226 match self {
2227 CodeGenResultSourceMap::None => {
2228 panic!("CodeGenResultSourceMap::None cannot span_to_string")
2229 }
2230 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_string(sp),
2231 CodeGenResultSourceMap::ScopeHoisting {
2232 modules_header_width,
2233 source_maps,
2234 } => {
2235 let (module, lo) =
2236 CodeGenResultComments::decode_bytepos(*modules_header_width, sp.lo);
2237 source_maps[module].span_to_string(Span {
2238 lo,
2239 hi: CodeGenResultComments::decode_bytepos(*modules_header_width, sp.hi).1,
2240 })
2241 }
2242 }
2243 }
2244 fn span_to_filename(&self, sp: Span) -> Arc<FileName> {
2245 match self {
2246 CodeGenResultSourceMap::None => {
2247 panic!("CodeGenResultSourceMap::None cannot span_to_filename")
2248 }
2249 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_filename(sp),
2250 CodeGenResultSourceMap::ScopeHoisting {
2251 modules_header_width,
2252 source_maps,
2253 } => {
2254 let (module, lo) =
2255 CodeGenResultComments::decode_bytepos(*modules_header_width, sp.lo);
2256 source_maps[module].span_to_filename(Span {
2257 lo,
2258 hi: CodeGenResultComments::decode_bytepos(*modules_header_width, sp.hi).1,
2259 })
2260 }
2261 }
2262 }
2263 fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> {
2264 match self {
2265 CodeGenResultSourceMap::None => {
2266 panic!("CodeGenResultSourceMap::None cannot merge_spans")
2267 }
2268 CodeGenResultSourceMap::Single { source_map } => source_map.merge_spans(sp_lhs, sp_rhs),
2269 CodeGenResultSourceMap::ScopeHoisting {
2270 modules_header_width,
2271 source_maps,
2272 } => {
2273 let (module_lhs, lo_lhs) =
2274 CodeGenResultComments::decode_bytepos(*modules_header_width, sp_lhs.lo);
2275 let (module_rhs, lo_rhs) =
2276 CodeGenResultComments::decode_bytepos(*modules_header_width, sp_rhs.lo);
2277 if module_lhs != module_rhs {
2278 return None;
2279 }
2280 source_maps[module_lhs].merge_spans(
2281 Span {
2282 lo: lo_lhs,
2283 hi: CodeGenResultComments::decode_bytepos(*modules_header_width, sp_lhs.hi)
2284 .1,
2285 },
2286 Span {
2287 lo: lo_rhs,
2288 hi: CodeGenResultComments::decode_bytepos(*modules_header_width, sp_rhs.hi)
2289 .1,
2290 },
2291 )
2292 }
2293 }
2294 }
2295 fn call_span_if_macro(&self, sp: Span) -> Span {
2296 match self {
2297 CodeGenResultSourceMap::None => {
2298 panic!("CodeGenResultSourceMap::None cannot call_span_if_macro")
2299 }
2300 CodeGenResultSourceMap::Single { source_map } => source_map.call_span_if_macro(sp),
2301 CodeGenResultSourceMap::ScopeHoisting {
2302 modules_header_width,
2303 source_maps,
2304 } => {
2305 let (module, lo) =
2306 CodeGenResultComments::decode_bytepos(*modules_header_width, sp.lo);
2307 source_maps[module].call_span_if_macro(Span {
2308 lo,
2309 hi: CodeGenResultComments::decode_bytepos(*modules_header_width, sp.hi).1,
2310 })
2311 }
2312 }
2313 }
2314 fn doctest_offset_line(&self, _line: usize) -> usize {
2315 panic!("doctest_offset_line is not implemented for CodeGenResultSourceMap");
2316 }
2317 fn span_to_snippet(&self, sp: Span) -> Result<String, Box<SpanSnippetError>> {
2318 match self {
2319 CodeGenResultSourceMap::None => {
2320 panic!("CodeGenResultSourceMap::None cannot span_to_snippet")
2321 }
2322 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_snippet(sp),
2323 CodeGenResultSourceMap::ScopeHoisting {
2324 modules_header_width,
2325 source_maps,
2326 } => {
2327 let (module, lo) =
2328 CodeGenResultComments::decode_bytepos(*modules_header_width, sp.lo);
2329 source_maps[module].span_to_snippet(Span {
2330 lo,
2331 hi: CodeGenResultComments::decode_bytepos(*modules_header_width, sp.hi).1,
2332 })
2333 }
2334 }
2335 }
2336}
2337impl SourceMapperExt for CodeGenResultSourceMap {
2338 fn get_code_map(&self) -> &dyn SourceMapper {
2339 self
2340 }
2341}
2342
2343#[derive(Debug)]
2344enum CodeGenResultOriginalSourceMap {
2345 Single(Option<ResolvedVc<Box<dyn GenerateSourceMap>>>),
2346 ScopeHoisting(SmallVec<[ResolvedVc<Box<dyn GenerateSourceMap>>; 1]>),
2347}
2348
2349impl CodeGenResultOriginalSourceMap {
2350 fn iter(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn GenerateSourceMap>>> {
2351 match self {
2352 CodeGenResultOriginalSourceMap::Single(map) => Either::Left(map.iter().copied()),
2353 CodeGenResultOriginalSourceMap::ScopeHoisting(maps) => {
2354 Either::Right(maps.iter().copied())
2355 }
2356 }
2357 }
2358}
2359
2360enum CodeGenResultComments {
2361 Single {
2362 comments: Either<ImmutableComments, Arc<ImmutableComments>>,
2363 extra_comments: SwcComments,
2364 },
2365 ScopeHoisting {
2366 modules_header_width: u32,
2369 comments: Vec<CodeGenResultComments>,
2370 },
2371 Empty,
2372}
2373
2374impl CodeGenResultComments {
2375 fn take(&mut self) -> Self {
2376 std::mem::replace(self, CodeGenResultComments::Empty)
2377 }
2378
2379 fn consumable(&self) -> CodeGenResultCommentsConsumable<'_> {
2380 match self {
2381 CodeGenResultComments::Single {
2382 comments,
2383 extra_comments,
2384 } => CodeGenResultCommentsConsumable::Single {
2385 comments: match comments {
2386 Either::Left(comments) => comments.consumable(),
2387 Either::Right(comments) => comments.consumable(),
2388 },
2389 extra_comments,
2390 },
2391 CodeGenResultComments::ScopeHoisting {
2392 modules_header_width,
2393 comments,
2394 } => CodeGenResultCommentsConsumable::ScopeHoisting {
2395 modules_header_width: *modules_header_width,
2396 comments: comments.iter().map(|c| c.consumable()).collect(),
2397 },
2398 CodeGenResultComments::Empty => CodeGenResultCommentsConsumable::Empty,
2399 }
2400 }
2401
2402 fn encode_bytepos(modules_header_width: u32, module: u32, pos: BytePos) -> BytePos {
2403 if pos.is_dummy() {
2404 return pos;
2406 }
2407
2408 let header_width = modules_header_width + 1;
2423 let pos_width = 32 - header_width;
2424
2425 let pos = pos.0;
2426
2427 let old_high_bits = pos >> pos_width;
2428 let high_bits_set = if (2u32.pow(header_width) - 1) == old_high_bits {
2429 true
2430 } else if old_high_bits == 0 {
2431 false
2432 } else {
2433 panic!("The high bits of the position {pos} are not all 0s or 1s: {old_high_bits:b}",);
2434 };
2435
2436 let pos = pos & !((2u32.pow(header_width) - 1) << pos_width);
2437 let encoded_high_bits = if high_bits_set { 1 } else { 0 } << pos_width;
2438 let encoded_module = module << (pos_width + 1);
2439
2440 BytePos(encoded_module | encoded_high_bits | pos)
2441 }
2442
2443 fn decode_bytepos(modules_header_width: u32, pos: BytePos) -> (usize, BytePos) {
2444 if pos.is_dummy() {
2445 panic!("Cannot decode dummy BytePos");
2447 }
2448
2449 let header_width = modules_header_width + 1;
2450 let pos_width = 32 - header_width;
2451
2452 let high_bits_set = ((pos.0 >> (pos_width)) & 1) == 1;
2453 let module = pos.0 >> (pos_width + 1);
2454 let pos = pos.0 & !((2u32.pow(header_width) - 1) << pos_width);
2455 let pos = if high_bits_set {
2456 pos | ((2u32.pow(header_width) - 1) << pos_width)
2457 } else {
2458 pos
2459 };
2460 (module as usize, BytePos(pos))
2461 }
2462}
2463
2464enum CodeGenResultCommentsConsumable<'a> {
2465 Single {
2466 comments: CowComments<'a>,
2467 extra_comments: &'a SwcComments,
2468 },
2469 ScopeHoisting {
2470 modules_header_width: u32,
2471 comments: Vec<CodeGenResultCommentsConsumable<'a>>,
2472 },
2473 Empty,
2474}
2475
2476unsafe impl Send for CodeGenResultComments {}
2477unsafe impl Sync for CodeGenResultComments {}
2478
2479fn encode_module_into_comment_span(
2483 modules_header_width: u32,
2484 module: usize,
2485 mut comment: Comment,
2486) -> Comment {
2487 comment.span.lo =
2488 CodeGenResultComments::encode_bytepos(modules_header_width, module as u32, comment.span.lo);
2489 comment.span.hi =
2490 CodeGenResultComments::encode_bytepos(modules_header_width, module as u32, comment.span.hi);
2491 comment
2492}
2493
2494impl Comments for CodeGenResultCommentsConsumable<'_> {
2495 fn add_leading(&self, _pos: BytePos, _cmt: Comment) {
2496 unimplemented!("add_leading")
2497 }
2498
2499 fn add_leading_comments(&self, _pos: BytePos, _comments: Vec<Comment>) {
2500 unimplemented!("add_leading_comments")
2501 }
2502
2503 fn has_leading(&self, pos: BytePos) -> bool {
2504 if pos.is_dummy() {
2505 return false;
2506 }
2507 match self {
2508 Self::Single {
2509 comments,
2510 extra_comments,
2511 } => comments.has_leading(pos) || extra_comments.has_leading(pos),
2512 Self::ScopeHoisting {
2513 modules_header_width,
2514 comments,
2515 } => {
2516 let (module, pos) =
2517 CodeGenResultComments::decode_bytepos(*modules_header_width, pos);
2518 comments[module].has_leading(pos)
2519 }
2520 Self::Empty => false,
2521 }
2522 }
2523
2524 fn move_leading(&self, _from: BytePos, _to: BytePos) {
2525 unimplemented!("move_leading")
2526 }
2527
2528 fn take_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
2529 if pos.is_dummy() {
2530 return None;
2531 }
2532 match self {
2533 Self::Single {
2534 comments,
2535 extra_comments,
2536 } => merge_option_vec(comments.take_leading(pos), extra_comments.take_leading(pos)),
2537 Self::ScopeHoisting {
2538 modules_header_width,
2539 comments,
2540 } => {
2541 let (module, pos) =
2542 CodeGenResultComments::decode_bytepos(*modules_header_width, pos);
2543 comments[module].take_leading(pos).map(|comments| {
2544 comments
2545 .into_iter()
2546 .map(|c| encode_module_into_comment_span(*modules_header_width, module, c))
2547 .collect()
2548 })
2549 }
2550 Self::Empty => None,
2551 }
2552 }
2553
2554 fn get_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
2555 if pos.is_dummy() {
2556 return None;
2557 }
2558 match self {
2559 Self::Single {
2560 comments,
2561 extra_comments,
2562 } => merge_option_vec(comments.get_leading(pos), extra_comments.get_leading(pos)),
2563 Self::ScopeHoisting {
2564 modules_header_width,
2565 comments,
2566 } => {
2567 let (module, pos) =
2568 CodeGenResultComments::decode_bytepos(*modules_header_width, pos);
2569 comments[module].get_leading(pos).map(|comments| {
2570 comments
2571 .into_iter()
2572 .map(|c| encode_module_into_comment_span(*modules_header_width, module, c))
2573 .collect()
2574 })
2575 }
2576 Self::Empty => None,
2577 }
2578 }
2579
2580 fn add_trailing(&self, _pos: BytePos, _cmt: Comment) {
2581 unimplemented!("add_trailing")
2582 }
2583
2584 fn add_trailing_comments(&self, _pos: BytePos, _comments: Vec<Comment>) {
2585 unimplemented!("add_trailing_comments")
2586 }
2587
2588 fn has_trailing(&self, pos: BytePos) -> bool {
2589 if pos.is_dummy() {
2590 return false;
2591 }
2592 match self {
2593 Self::Single {
2594 comments,
2595 extra_comments,
2596 } => comments.has_trailing(pos) || extra_comments.has_trailing(pos),
2597 Self::ScopeHoisting {
2598 modules_header_width,
2599 comments,
2600 } => {
2601 let (module, pos) =
2602 CodeGenResultComments::decode_bytepos(*modules_header_width, pos);
2603 comments[module].has_trailing(pos)
2604 }
2605 Self::Empty => false,
2606 }
2607 }
2608
2609 fn move_trailing(&self, _from: BytePos, _to: BytePos) {
2610 unimplemented!("move_trailing")
2611 }
2612
2613 fn take_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
2614 if pos.is_dummy() {
2615 return None;
2616 }
2617 match self {
2618 Self::Single {
2619 comments,
2620 extra_comments,
2621 } => merge_option_vec(
2622 comments.take_trailing(pos),
2623 extra_comments.take_trailing(pos),
2624 ),
2625 Self::ScopeHoisting {
2626 modules_header_width,
2627 comments,
2628 } => {
2629 let (module, pos) =
2630 CodeGenResultComments::decode_bytepos(*modules_header_width, pos);
2631 comments[module].take_trailing(pos).map(|comments| {
2632 comments
2633 .into_iter()
2634 .map(|c| encode_module_into_comment_span(*modules_header_width, module, c))
2635 .collect()
2636 })
2637 }
2638 Self::Empty => None,
2639 }
2640 }
2641
2642 fn get_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
2643 if pos.is_dummy() {
2644 return None;
2645 }
2646 match self {
2647 Self::Single {
2648 comments,
2649 extra_comments,
2650 } => merge_option_vec(comments.get_leading(pos), extra_comments.get_leading(pos)),
2651 Self::ScopeHoisting {
2652 modules_header_width,
2653 comments,
2654 } => {
2655 let (module, pos) =
2656 CodeGenResultComments::decode_bytepos(*modules_header_width, pos);
2657 comments[module].get_leading(pos).map(|comments| {
2658 comments
2659 .into_iter()
2660 .map(|c| encode_module_into_comment_span(*modules_header_width, module, c))
2661 .collect()
2662 })
2663 }
2664 Self::Empty => None,
2665 }
2666 }
2667
2668 fn add_pure_comment(&self, _pos: BytePos) {
2669 unimplemented!("add_pure_comment")
2670 }
2671}
2672
2673fn merge_option_vec<T>(a: Option<Vec<T>>, b: Option<Vec<T>>) -> Option<Vec<T>> {
2674 match (a, b) {
2675 (Some(a), Some(b)) => Some(a.into_iter().chain(b).collect()),
2676 (Some(a), None) => Some(a),
2677 (None, Some(b)) => Some(b),
2678 (None, None) => None,
2679 }
2680}
2681
2682pub fn register() {
2683 turbo_tasks::register();
2684 turbo_tasks_fs::register();
2685 turbopack_core::register();
2686 turbo_esregex::register();
2687 include!(concat!(env!("OUT_DIR"), "/register.rs"));
2688}
2689
2690#[cfg(test)]
2691mod tests {
2692 use super::*;
2693 fn bytepos_ensure_identical(modules_header_width: u32, pos: BytePos) {
2694 let module_count = 2u32.pow(modules_header_width);
2695
2696 for module in [
2697 0,
2698 1,
2699 2,
2700 module_count / 2,
2701 module_count.wrapping_sub(5),
2702 module_count.wrapping_sub(1),
2703 ]
2704 .into_iter()
2705 .filter(|&m| m < module_count)
2706 {
2707 let encoded = CodeGenResultComments::encode_bytepos(modules_header_width, module, pos);
2708 let (decoded_module, decoded_pos) =
2709 CodeGenResultComments::decode_bytepos(modules_header_width, encoded);
2710 assert_eq!(
2711 decoded_module as u32, module,
2712 "Testing width {modules_header_width} and pos {pos:?}"
2713 );
2714 assert_eq!(
2715 decoded_pos, pos,
2716 "Testing width {modules_header_width} and pos {pos:?}"
2717 );
2718 }
2719 }
2720
2721 #[test]
2722 fn test_encode_decode_bytepos_format() {
2723 for (pos, module, modules_header_width, result) in [
2724 (
2725 0b00000000000000000000000000000101,
2726 0b1,
2727 1,
2728 0b10000000000000000000000000000101,
2729 ),
2730 (
2731 0b00000000000000000000000000000101,
2732 0b01,
2733 2,
2734 0b01000000000000000000000000000101,
2735 ),
2736 (
2737 0b11111111111111110000000000000101,
2738 0b0001,
2739 4,
2740 0b00011111111111110000000000000101,
2741 ),
2742 (
2743 0b00000111111111110000000000000101,
2744 0b0001,
2745 4,
2746 0b00010111111111110000000000000101,
2747 ),
2748 (BytePos::DUMMY.0, 0b0001, 4, BytePos::DUMMY.0),
2750 ] {
2751 let encoded =
2752 CodeGenResultComments::encode_bytepos(modules_header_width, module, BytePos(pos));
2753 assert_eq!(encoded.0, result);
2754 }
2755 }
2756
2757 #[test]
2758 fn test_encode_decode_bytepos_lossless() {
2759 const DUMMY_RESERVE: u32 = u32::MAX - 2_u32.pow(16);
2761
2762 for modules_header_width in 1..=6 {
2763 for pos in [
2764 BytePos(1),
2766 BytePos(2),
2767 BytePos(100),
2768 BytePos(4_000_000),
2769 BytePos(60_000_000),
2770 BytePos::PLACEHOLDER,
2771 BytePos::SYNTHESIZED,
2772 BytePos::PURE,
2773 BytePos(DUMMY_RESERVE),
2774 BytePos(DUMMY_RESERVE + 10),
2775 BytePos(DUMMY_RESERVE + 10000),
2776 ] {
2777 if modules_header_width == 6 && pos.0 == 60_000_000 {
2778 continue;
2780 }
2781 bytepos_ensure_identical(modules_header_width, pos);
2782 }
2783 }
2784 }
2785}