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