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 inlined_bytes_module;
18pub mod magic_identifier;
19pub mod manifest;
20mod merged_module;
21pub mod minify;
22pub mod parse;
23mod path_visitor;
24pub mod references;
25pub mod runtime_functions;
26pub mod side_effect_optimization;
27pub mod single_file_ecmascript_output;
28pub mod source_map;
29pub(crate) mod static_code;
30mod swc_comments;
31pub mod text;
32pub(crate) mod transform;
33pub mod tree_shake;
34pub mod typescript;
35pub mod utils;
36pub mod webpack;
37pub mod worker_chunk;
38
39use std::{
40 borrow::Cow,
41 collections::hash_map::Entry,
42 fmt::{Debug, Display, Formatter},
43 mem::take,
44 sync::{Arc, Mutex},
45};
46
47use anyhow::{Context, Result, anyhow, bail};
48use bincode::{Decode, Encode};
49use either::Either;
50use itertools::Itertools;
51use rustc_hash::{FxHashMap, FxHashSet};
52use serde::Deserialize;
53use smallvec::SmallVec;
54use swc_core::{
55 atoms::Atom,
56 base::SwcComments,
57 common::{
58 BytePos, DUMMY_SP, FileName, GLOBALS, Globals, Loc, Mark, SourceFile, SourceMap,
59 SourceMapper, Span, SpanSnippetError, SyntaxContext,
60 comments::{Comment, CommentKind, Comments},
61 source_map::{FileLinesResult, Files, SourceMapLookupError},
62 util::take::Take,
63 },
64 ecma::{
65 ast::{
66 self, CallExpr, Callee, Decl, EmptyStmt, Expr, ExprStmt, Id, Ident, ModuleItem,
67 Program, Script, SourceMapperExt, Stmt,
68 },
69 codegen::{Emitter, text_writer::JsWriter},
70 utils::StmtLikeInjector,
71 visit::{VisitMut, VisitMutWith, VisitMutWithAstPath, VisitWith},
72 },
73 quote,
74};
75use tracing::{Instrument, Level, instrument};
76use turbo_rcstr::{RcStr, rcstr};
77use turbo_tasks::{
78 FxDashMap, FxIndexMap, IntoTraitRef, NonLocalValue, ReadRef, ResolvedVc, TaskInput,
79 TryJoinIterExt, Upcast, ValueToString, Vc, trace::TraceRawVcs,
80};
81use turbo_tasks_fs::{FileJsonContent, FileSystemPath, glob::Glob, rope::Rope};
82use turbopack_core::{
83 asset::{Asset, AssetContent},
84 chunk::{
85 AsyncModuleInfo, ChunkItem, ChunkType, ChunkableModule, ChunkingContext, EvaluatableAsset,
86 MergeableModule, MergeableModuleExposure, MergeableModules, MergeableModulesExposed,
87 MinifyType, ModuleChunkItemIdExt, ModuleId,
88 },
89 compile_time_info::CompileTimeInfo,
90 context::AssetContext,
91 ident::AssetIdent,
92 module::{Module, ModuleSideEffects, OptionModule},
93 module_graph::ModuleGraph,
94 output::OutputAssetsReference,
95 reference::ModuleReferences,
96 reference_type::InnerAssets,
97 resolve::{
98 FindContextFileResult, find_context_file, origin::ResolveOrigin, package_json,
99 parse::Request,
100 },
101 source::Source,
102 source_map::GenerateSourceMap,
103};
104
105use crate::{
106 analyzer::graph::EvalContext,
107 chunk::{
108 EcmascriptChunkItem, EcmascriptChunkItemContent, EcmascriptChunkPlaceable,
109 EcmascriptChunkType, EcmascriptExports,
110 placeable::{SideEffectsDeclaration, get_side_effect_free_declaration},
111 },
112 code_gen::{CodeGeneration, CodeGenerationHoistedStmt, CodeGens, ModifiableAst},
113 merged_module::MergedEcmascriptModule,
114 parse::{IdentCollector, ParseResult, generate_js_source_map, parse},
115 path_visitor::ApplyVisitors,
116 references::{
117 analyze_ecmascript_module,
118 async_module::OptionAsyncModule,
119 esm::{UrlRewriteBehavior, base::EsmAssetReferences, export},
120 },
121 side_effect_optimization::reference::EcmascriptModulePartReference,
122 swc_comments::{CowComments, ImmutableComments},
123 transform::{remove_directives, remove_shebang},
124};
125pub use crate::{
126 references::{AnalyzeEcmascriptModuleResult, TURBOPACK_HELPER},
127 static_code::StaticEcmascriptCode,
128 transform::{
129 CustomTransformer, EcmascriptInputTransform, EcmascriptInputTransforms, TransformContext,
130 TransformPlugin,
131 },
132};
133
134#[derive(
135 Eq,
136 PartialEq,
137 Hash,
138 Debug,
139 Clone,
140 Copy,
141 Default,
142 TaskInput,
143 TraceRawVcs,
144 NonLocalValue,
145 Deserialize,
146 Encode,
147 Decode,
148)]
149pub enum SpecifiedModuleType {
150 #[default]
151 Automatic,
152 CommonJs,
153 EcmaScript,
154}
155
156#[derive(
157 PartialOrd,
158 Ord,
159 PartialEq,
160 Eq,
161 Hash,
162 Debug,
163 Clone,
164 Copy,
165 Default,
166 Deserialize,
167 TaskInput,
168 TraceRawVcs,
169 NonLocalValue,
170 Encode,
171 Decode,
172)]
173#[serde(rename_all = "kebab-case")]
174pub enum TreeShakingMode {
175 ModuleFragments,
176 #[default]
177 ReexportsOnly,
178}
179
180#[derive(
181 PartialOrd,
182 Ord,
183 PartialEq,
184 Eq,
185 Hash,
186 Debug,
187 Clone,
188 Copy,
189 Default,
190 Deserialize,
191 TaskInput,
192 TraceRawVcs,
193 NonLocalValue,
194 Encode,
195 Decode,
196)]
197pub enum AnalyzeMode {
198 #[default]
200 CodeGeneration,
201 CodeGenerationAndTracing,
203 Tracing,
205}
206
207impl AnalyzeMode {
208 pub fn is_tracing(self) -> bool {
209 match self {
210 AnalyzeMode::Tracing | AnalyzeMode::CodeGenerationAndTracing => true,
211 AnalyzeMode::CodeGeneration => false,
212 }
213 }
214
215 pub fn is_code_gen(self) -> bool {
216 match self {
217 AnalyzeMode::CodeGeneration | AnalyzeMode::CodeGenerationAndTracing => true,
218 AnalyzeMode::Tracing => false,
219 }
220 }
221}
222
223#[turbo_tasks::value(transparent)]
224pub struct OptionTreeShaking(pub Option<TreeShakingMode>);
225
226#[derive(
228 Copy, Clone, PartialEq, Eq, Debug, Hash, TraceRawVcs, NonLocalValue, TaskInput, Encode, Decode,
229)]
230pub enum TypeofWindow {
231 Object,
232 Undefined,
233}
234
235#[turbo_tasks::value(shared)]
236#[derive(Debug, Default, Copy, Clone)]
237pub struct EcmascriptOptions {
238 pub tree_shaking_mode: Option<TreeShakingMode>,
240 pub specified_module_type: SpecifiedModuleType,
242 pub url_rewrite_behavior: Option<UrlRewriteBehavior>,
246 pub import_externals: bool,
249 pub ignore_dynamic_requests: bool,
253 pub extract_source_map: bool,
256 pub keep_last_successful_parse: bool,
260 pub analyze_mode: AnalyzeMode,
263 pub enable_typeof_window_inlining: Option<TypeofWindow>,
268 pub enable_exports_info_inlining: bool,
270
271 pub inline_helpers: bool,
272 pub infer_module_side_effects: bool,
274}
275
276#[turbo_tasks::value]
277#[derive(Hash, Debug, Copy, Clone, TaskInput)]
278pub enum EcmascriptModuleAssetType {
279 Ecmascript,
281 EcmascriptExtensionless,
283 Typescript {
285 tsx: bool,
287 analyze_types: bool,
289 },
290 TypescriptDeclaration,
292}
293
294impl Display for EcmascriptModuleAssetType {
295 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
296 match self {
297 EcmascriptModuleAssetType::Ecmascript => write!(f, "ecmascript"),
298 EcmascriptModuleAssetType::EcmascriptExtensionless => {
299 write!(f, "ecmascript extensionless")
300 }
301 EcmascriptModuleAssetType::Typescript { tsx, analyze_types } => {
302 write!(f, "typescript")?;
303 if *tsx {
304 write!(f, " with JSX")?;
305 }
306 if *analyze_types {
307 write!(f, " with types")?;
308 }
309 Ok(())
310 }
311 EcmascriptModuleAssetType::TypescriptDeclaration => write!(f, "typescript declaration"),
312 }
313 }
314}
315
316#[derive(Clone)]
317pub struct EcmascriptModuleAssetBuilder {
318 source: ResolvedVc<Box<dyn Source>>,
319 asset_context: ResolvedVc<Box<dyn AssetContext>>,
320 ty: EcmascriptModuleAssetType,
321 transforms: ResolvedVc<EcmascriptInputTransforms>,
322 options: ResolvedVc<EcmascriptOptions>,
323 compile_time_info: ResolvedVc<CompileTimeInfo>,
324 side_effect_free_packages: Option<ResolvedVc<Glob>>,
325 inner_assets: Option<ResolvedVc<InnerAssets>>,
326}
327
328impl EcmascriptModuleAssetBuilder {
329 pub fn with_inner_assets(mut self, inner_assets: ResolvedVc<InnerAssets>) -> Self {
330 self.inner_assets = Some(inner_assets);
331 self
332 }
333
334 pub fn with_type(mut self, ty: EcmascriptModuleAssetType) -> Self {
335 self.ty = ty;
336 self
337 }
338
339 pub fn build(self) -> Vc<EcmascriptModuleAsset> {
340 if let Some(inner_assets) = self.inner_assets {
341 EcmascriptModuleAsset::new_with_inner_assets(
342 *self.source,
343 *self.asset_context,
344 self.ty,
345 *self.transforms,
346 *self.options,
347 *self.compile_time_info,
348 self.side_effect_free_packages.map(|g| *g),
349 *inner_assets,
350 )
351 } else {
352 EcmascriptModuleAsset::new(
353 *self.source,
354 *self.asset_context,
355 self.ty,
356 *self.transforms,
357 *self.options,
358 *self.compile_time_info,
359 self.side_effect_free_packages.map(|g| *g),
360 )
361 }
362 }
363}
364
365#[turbo_tasks::value]
366pub struct EcmascriptModuleAsset {
367 pub source: ResolvedVc<Box<dyn Source>>,
368 pub asset_context: ResolvedVc<Box<dyn AssetContext>>,
369 pub ty: EcmascriptModuleAssetType,
370 pub transforms: ResolvedVc<EcmascriptInputTransforms>,
371 pub options: ResolvedVc<EcmascriptOptions>,
372 pub compile_time_info: ResolvedVc<CompileTimeInfo>,
373 pub side_effect_free_packages: Option<ResolvedVc<Glob>>,
374 pub inner_assets: Option<ResolvedVc<InnerAssets>>,
375 #[turbo_tasks(debug_ignore)]
376 last_successful_parse: turbo_tasks::TransientState<ReadRef<ParseResult>>,
377}
378impl core::fmt::Debug for EcmascriptModuleAsset {
379 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
380 f.debug_struct("EcmascriptModuleAsset")
381 .field("source", &self.source)
382 .field("asset_context", &self.asset_context)
383 .field("ty", &self.ty)
384 .field("transforms", &self.transforms)
385 .field("options", &self.options)
386 .field("compile_time_info", &self.compile_time_info)
387 .field("side_effect_free_packages", &self.side_effect_free_packages)
388 .field("inner_assets", &self.inner_assets)
389 .finish()
390 }
391}
392
393#[turbo_tasks::value_trait]
394pub trait EcmascriptParsable {
395 #[turbo_tasks::function]
396 fn failsafe_parse(self: Vc<Self>) -> Result<Vc<ParseResult>>;
397
398 #[turbo_tasks::function]
399 fn parse_original(self: Vc<Self>) -> Result<Vc<ParseResult>>;
400
401 #[turbo_tasks::function]
402 fn ty(self: Vc<Self>) -> Result<Vc<EcmascriptModuleAssetType>>;
403}
404
405#[turbo_tasks::value_trait]
406pub trait EcmascriptAnalyzable: Module + Asset {
407 #[turbo_tasks::function]
408 fn analyze(self: Vc<Self>) -> Vc<AnalyzeEcmascriptModuleResult>;
409
410 #[turbo_tasks::function]
413 async fn module_content_without_analysis(
414 self: Vc<Self>,
415 generate_source_map: bool,
416 ) -> Result<Vc<EcmascriptModuleContent>>;
417
418 #[turbo_tasks::function]
419 async fn module_content_options(
420 self: Vc<Self>,
421 chunking_context: Vc<Box<dyn ChunkingContext>>,
422 async_module_info: Option<Vc<AsyncModuleInfo>>,
423 ) -> Result<Vc<EcmascriptModuleContentOptions>>;
424}
425
426pub trait EcmascriptAnalyzableExt {
427 fn module_content(
428 self: Vc<Self>,
429 chunking_context: Vc<Box<dyn ChunkingContext>>,
430 async_module_info: Option<Vc<AsyncModuleInfo>>,
431 ) -> Vc<EcmascriptModuleContent>;
432}
433
434impl<T> EcmascriptAnalyzableExt for T
435where
436 T: EcmascriptAnalyzable + Upcast<Box<dyn EcmascriptAnalyzable>>,
437{
438 fn module_content(
439 self: Vc<Self>,
440 chunking_context: Vc<Box<dyn ChunkingContext>>,
441 async_module_info: Option<Vc<AsyncModuleInfo>>,
442 ) -> Vc<EcmascriptModuleContent> {
443 let analyzable = Vc::upcast_non_strict::<Box<dyn EcmascriptAnalyzable>>(self);
444 let own_options = analyzable.module_content_options(chunking_context, async_module_info);
445 EcmascriptModuleContent::new(own_options)
446 }
447}
448
449impl EcmascriptModuleAsset {
450 pub fn builder(
451 source: ResolvedVc<Box<dyn Source>>,
452 asset_context: ResolvedVc<Box<dyn AssetContext>>,
453 transforms: ResolvedVc<EcmascriptInputTransforms>,
454 options: ResolvedVc<EcmascriptOptions>,
455 compile_time_info: ResolvedVc<CompileTimeInfo>,
456 side_effect_free_packages: Option<ResolvedVc<Glob>>,
457 ) -> EcmascriptModuleAssetBuilder {
458 EcmascriptModuleAssetBuilder {
459 source,
460 asset_context,
461 ty: EcmascriptModuleAssetType::Ecmascript,
462 transforms,
463 options,
464 compile_time_info,
465 side_effect_free_packages,
466 inner_assets: None,
467 }
468 }
469}
470
471#[turbo_tasks::value]
472#[derive(Clone)]
473pub(crate) struct ModuleTypeResult {
474 pub module_type: SpecifiedModuleType,
475 pub referenced_package_json: Option<FileSystemPath>,
476}
477
478#[turbo_tasks::value_impl]
479impl ModuleTypeResult {
480 #[turbo_tasks::function]
481 fn new(module_type: SpecifiedModuleType) -> Vc<Self> {
482 Self::cell(ModuleTypeResult {
483 module_type,
484 referenced_package_json: None,
485 })
486 }
487
488 #[turbo_tasks::function]
489 fn new_with_package_json(
490 module_type: SpecifiedModuleType,
491 package_json: FileSystemPath,
492 ) -> Vc<Self> {
493 Self::cell(ModuleTypeResult {
494 module_type,
495 referenced_package_json: Some(package_json),
496 })
497 }
498}
499
500#[turbo_tasks::value_impl]
501impl EcmascriptParsable for EcmascriptModuleAsset {
502 #[turbo_tasks::function]
503 async fn failsafe_parse(&self) -> Result<Vc<ParseResult>> {
504 let real_result = self.parse().await?;
505 if self.options.await?.keep_last_successful_parse {
506 let real_result_value = real_result.await?;
507 let result_value = if matches!(*real_result_value, ParseResult::Ok { .. }) {
508 self.last_successful_parse
509 .set_unconditionally(real_result_value.clone());
510 real_result_value
511 } else {
512 let state_ref = self.last_successful_parse.get();
513 state_ref.as_ref().unwrap_or(&real_result_value).clone()
514 };
515 Ok(ReadRef::cell(result_value))
516 } else {
517 Ok(real_result)
518 }
519 }
520
521 #[turbo_tasks::function]
522 fn parse_original(self: Vc<Self>) -> Vc<ParseResult> {
523 self.failsafe_parse()
524 }
525
526 #[turbo_tasks::function]
527 fn ty(&self) -> Vc<EcmascriptModuleAssetType> {
528 self.ty.cell()
529 }
530}
531
532#[turbo_tasks::value_impl]
533impl EcmascriptAnalyzable for EcmascriptModuleAsset {
534 #[turbo_tasks::function]
535 fn analyze(self: Vc<Self>) -> Vc<AnalyzeEcmascriptModuleResult> {
536 analyze_ecmascript_module(self, None)
537 }
538
539 #[turbo_tasks::function]
542 async fn module_content_without_analysis(
543 self: Vc<Self>,
544 generate_source_map: bool,
545 ) -> Result<Vc<EcmascriptModuleContent>> {
546 let this = self.await?;
547
548 let parsed = this.parse().await?;
549
550 Ok(EcmascriptModuleContent::new_without_analysis(
551 parsed,
552 self.ident(),
553 this.options.await?.specified_module_type,
554 generate_source_map,
555 ))
556 }
557
558 #[turbo_tasks::function]
559 async fn module_content_options(
560 self: ResolvedVc<Self>,
561 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
562 async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
563 ) -> Result<Vc<EcmascriptModuleContentOptions>> {
564 let parsed = self.await?.parse().await?.to_resolved().await?;
565
566 let analyze = self.analyze();
567 let analyze_ref = analyze.await?;
568
569 let module_type_result = self.determine_module_type().await?;
570 let generate_source_map = *chunking_context
571 .reference_module_source_maps(Vc::upcast(*self))
572 .await?;
573
574 Ok(EcmascriptModuleContentOptions {
575 parsed: Some(parsed),
576 module: ResolvedVc::upcast(self),
577 specified_module_type: module_type_result.module_type,
578 chunking_context,
579 references: analyze.references().to_resolved().await?,
580 esm_references: analyze_ref.esm_references,
581 part_references: vec![],
582 code_generation: analyze_ref.code_generation,
583 async_module: analyze_ref.async_module,
584 generate_source_map,
585 original_source_map: analyze_ref.source_map,
586 exports: analyze_ref.exports,
587 async_module_info,
588 }
589 .cell())
590 }
591}
592
593#[turbo_tasks::function]
594async fn determine_module_type_for_directory(
595 context_path: FileSystemPath,
596) -> Result<Vc<ModuleTypeResult>> {
597 let find_package_json =
598 find_context_file(context_path, package_json().resolve().await?, false).await?;
599 let FindContextFileResult::Found(package_json, _) = &*find_package_json else {
600 return Ok(ModuleTypeResult::new(SpecifiedModuleType::Automatic));
601 };
602
603 if let FileJsonContent::Content(content) = &*package_json.read_json().await?
605 && let Some(r#type) = content.get("type")
606 {
607 return Ok(ModuleTypeResult::new_with_package_json(
608 match r#type.as_str() {
609 Some("module") => SpecifiedModuleType::EcmaScript,
610 Some("commonjs") => SpecifiedModuleType::CommonJs,
611 _ => SpecifiedModuleType::Automatic,
612 },
613 package_json.clone(),
614 ));
615 }
616
617 Ok(ModuleTypeResult::new_with_package_json(
618 SpecifiedModuleType::Automatic,
619 package_json.clone(),
620 ))
621}
622
623#[turbo_tasks::value_impl]
624impl EcmascriptModuleAsset {
625 #[turbo_tasks::function]
626 fn new(
627 source: ResolvedVc<Box<dyn Source>>,
628 asset_context: ResolvedVc<Box<dyn AssetContext>>,
629 ty: EcmascriptModuleAssetType,
630 transforms: ResolvedVc<EcmascriptInputTransforms>,
631 options: ResolvedVc<EcmascriptOptions>,
632 compile_time_info: ResolvedVc<CompileTimeInfo>,
633 side_effect_free_packages: Option<ResolvedVc<Glob>>,
634 ) -> Vc<Self> {
635 Self::cell(EcmascriptModuleAsset {
636 source,
637 asset_context,
638 ty,
639 transforms,
640 options,
641 compile_time_info,
642 side_effect_free_packages,
643 inner_assets: None,
644 last_successful_parse: Default::default(),
645 })
646 }
647
648 #[turbo_tasks::function]
649 async fn new_with_inner_assets(
650 source: ResolvedVc<Box<dyn Source>>,
651 asset_context: ResolvedVc<Box<dyn AssetContext>>,
652 ty: EcmascriptModuleAssetType,
653 transforms: ResolvedVc<EcmascriptInputTransforms>,
654 options: ResolvedVc<EcmascriptOptions>,
655 compile_time_info: ResolvedVc<CompileTimeInfo>,
656 side_effect_free_packages: Option<ResolvedVc<Glob>>,
657 inner_assets: ResolvedVc<InnerAssets>,
658 ) -> Result<Vc<Self>> {
659 if inner_assets.await?.is_empty() {
660 Ok(Self::new(
661 *source,
662 *asset_context,
663 ty,
664 *transforms,
665 *options,
666 *compile_time_info,
667 side_effect_free_packages.map(|g| *g),
668 ))
669 } else {
670 Ok(Self::cell(EcmascriptModuleAsset {
671 source,
672 asset_context,
673 ty,
674 transforms,
675 options,
676 compile_time_info,
677 side_effect_free_packages,
678 inner_assets: Some(inner_assets),
679 last_successful_parse: Default::default(),
680 }))
681 }
682 }
683
684 #[turbo_tasks::function]
685 pub fn source(&self) -> Vc<Box<dyn Source>> {
686 *self.source
687 }
688
689 #[turbo_tasks::function]
690 pub fn analyze(self: Vc<Self>) -> Vc<AnalyzeEcmascriptModuleResult> {
691 analyze_ecmascript_module(self, None)
692 }
693
694 #[turbo_tasks::function]
695 pub fn options(&self) -> Vc<EcmascriptOptions> {
696 *self.options
697 }
698}
699
700impl EcmascriptModuleAsset {
701 pub async fn parse(&self) -> Result<Vc<ParseResult>> {
702 let options = self.options.await?;
703 Ok(parse(
704 *self.source,
705 self.ty,
706 *self.transforms,
707 options.analyze_mode == AnalyzeMode::Tracing,
708 options.inline_helpers,
709 ))
710 }
711
712 #[tracing::instrument(level = "trace", skip_all)]
713 pub(crate) async fn determine_module_type(self: Vc<Self>) -> Result<ReadRef<ModuleTypeResult>> {
714 let this = self.await?;
715
716 match this.options.await?.specified_module_type {
717 SpecifiedModuleType::EcmaScript => {
718 return ModuleTypeResult::new(SpecifiedModuleType::EcmaScript).await;
719 }
720 SpecifiedModuleType::CommonJs => {
721 return ModuleTypeResult::new(SpecifiedModuleType::CommonJs).await;
722 }
723 SpecifiedModuleType::Automatic => {}
724 }
725
726 determine_module_type_for_directory(self.origin_path().await?.parent()).await
727 }
728}
729
730#[turbo_tasks::value_impl]
731impl Module for EcmascriptModuleAsset {
732 #[turbo_tasks::function]
733 async fn ident(&self) -> Result<Vc<AssetIdent>> {
734 let mut ident = self.source.ident().owned().await?;
735 if let Some(inner_assets) = self.inner_assets {
736 for (name, asset) in inner_assets.await?.iter() {
737 ident.add_asset(name.clone(), asset.ident().to_resolved().await?);
738 }
739 }
740 ident.add_modifier(rcstr!("ecmascript"));
741 ident.layer = Some(self.asset_context.into_trait_ref().await?.layer());
742 Ok(AssetIdent::new(ident))
743 }
744
745 #[turbo_tasks::function]
746 fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
747 Vc::cell(Some(self.source))
748 }
749
750 #[turbo_tasks::function]
751 fn references(self: Vc<Self>) -> Result<Vc<ModuleReferences>> {
752 Ok(self.analyze().references())
753 }
754
755 #[turbo_tasks::function]
756 async fn is_self_async(self: Vc<Self>) -> Result<Vc<bool>> {
757 if let Some(async_module) = *self.get_async_module().await? {
758 Ok(async_module.is_self_async(self.references()))
759 } else {
760 Ok(Vc::cell(false))
761 }
762 }
763
764 #[turbo_tasks::function]
765 async fn side_effects(self: Vc<Self>) -> Result<Vc<ModuleSideEffects>> {
766 let this = self.await?;
767 Ok((match *get_side_effect_free_declaration(
770 self.ident().path().owned().await?,
771 this.side_effect_free_packages.map(|g| *g),
772 )
773 .await?
774 {
775 SideEffectsDeclaration::SideEffectful => ModuleSideEffects::SideEffectful,
776 SideEffectsDeclaration::SideEffectFree => ModuleSideEffects::SideEffectFree,
777 SideEffectsDeclaration::None => self.analyze().await?.side_effects,
778 })
779 .cell())
780 }
781}
782
783#[turbo_tasks::value_impl]
784impl Asset for EcmascriptModuleAsset {
785 #[turbo_tasks::function]
786 fn content(&self) -> Vc<AssetContent> {
787 self.source.content()
788 }
789}
790
791#[turbo_tasks::value_impl]
792impl ChunkableModule for EcmascriptModuleAsset {
793 #[turbo_tasks::function]
794 async fn as_chunk_item(
795 self: ResolvedVc<Self>,
796 _module_graph: ResolvedVc<ModuleGraph>,
797 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
798 ) -> Vc<Box<dyn ChunkItem>> {
799 Vc::upcast(ModuleChunkItem::cell(ModuleChunkItem {
800 module: self,
801 chunking_context,
802 }))
803 }
804}
805
806#[turbo_tasks::value_impl]
807impl EcmascriptChunkPlaceable for EcmascriptModuleAsset {
808 #[turbo_tasks::function]
809 async fn get_exports(self: Vc<Self>) -> Result<Vc<EcmascriptExports>> {
810 Ok(*self.analyze().await?.exports)
811 }
812
813 #[turbo_tasks::function]
814 async fn get_async_module(self: Vc<Self>) -> Result<Vc<OptionAsyncModule>> {
815 Ok(*self.analyze().await?.async_module)
816 }
817}
818
819#[turbo_tasks::value_impl]
820impl MergeableModule for EcmascriptModuleAsset {
821 #[turbo_tasks::function]
822 async fn is_mergeable(self: ResolvedVc<Self>) -> Result<Vc<bool>> {
823 if matches!(
824 &*self.get_exports().await?,
825 EcmascriptExports::EsmExports(_)
826 ) {
827 return Ok(Vc::cell(true));
828 }
829
830 Ok(Vc::cell(false))
831 }
832
833 #[turbo_tasks::function]
834 async fn merge(
835 self: Vc<Self>,
836 modules: Vc<MergeableModulesExposed>,
837 entry_points: Vc<MergeableModules>,
838 ) -> Result<Vc<Box<dyn ChunkableModule>>> {
839 Ok(Vc::upcast(
840 *MergedEcmascriptModule::new(
841 modules,
842 entry_points,
843 self.options().to_resolved().await?,
844 )
845 .await?,
846 ))
847 }
848}
849
850#[turbo_tasks::value_impl]
851impl EvaluatableAsset for EcmascriptModuleAsset {}
852
853#[turbo_tasks::value_impl]
854impl ResolveOrigin for EcmascriptModuleAsset {
855 #[turbo_tasks::function]
856 fn origin_path(&self) -> Vc<FileSystemPath> {
857 self.source.ident().path()
858 }
859
860 #[turbo_tasks::function]
861 fn asset_context(&self) -> Vc<Box<dyn AssetContext>> {
862 *self.asset_context
863 }
864
865 #[turbo_tasks::function]
866 async fn get_inner_asset(&self, request: Vc<Request>) -> Result<Vc<OptionModule>> {
867 Ok(Vc::cell(if let Some(inner_assets) = &self.inner_assets {
868 if let Some(request) = request.await?.request() {
869 inner_assets.await?.get(&request).copied()
870 } else {
871 None
872 }
873 } else {
874 None
875 }))
876 }
877}
878
879#[turbo_tasks::value]
880struct ModuleChunkItem {
881 module: ResolvedVc<EcmascriptModuleAsset>,
882 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
883}
884
885#[turbo_tasks::value_impl]
886impl OutputAssetsReference for ModuleChunkItem {}
887
888#[turbo_tasks::value_impl]
889impl ChunkItem for ModuleChunkItem {
890 #[turbo_tasks::function]
891 fn asset_ident(&self) -> Vc<AssetIdent> {
892 self.module.ident()
893 }
894
895 #[turbo_tasks::function]
896 fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
897 *self.chunking_context
898 }
899
900 #[turbo_tasks::function]
901 async fn ty(&self) -> Result<Vc<Box<dyn ChunkType>>> {
902 Ok(Vc::upcast(
903 Vc::<EcmascriptChunkType>::default().resolve().await?,
904 ))
905 }
906
907 #[turbo_tasks::function]
908 fn module(&self) -> Vc<Box<dyn Module>> {
909 *ResolvedVc::upcast(self.module)
910 }
911}
912
913#[turbo_tasks::value_impl]
914impl EcmascriptChunkItem for ModuleChunkItem {
915 #[turbo_tasks::function]
916 fn content(self: Vc<Self>) -> Vc<EcmascriptChunkItemContent> {
917 panic!("content() should not be called");
918 }
919
920 #[turbo_tasks::function]
921 async fn content_with_async_module_info(
922 self: Vc<Self>,
923 async_module_info: Option<Vc<AsyncModuleInfo>>,
924 _estimated: bool,
925 ) -> Result<Vc<EcmascriptChunkItemContent>> {
926 let span = tracing::info_span!(
927 "code generation",
928 name = display(self.asset_ident().to_string().await?)
929 );
930 async {
931 let this = self.await?;
932 let async_module_options = this
933 .module
934 .get_async_module()
935 .module_options(async_module_info);
936
937 let content = this
939 .module
940 .module_content(*this.chunking_context, async_module_info);
941
942 EcmascriptChunkItemContent::new(content, *this.chunking_context, async_module_options)
943 .resolve()
944 .await
945 }
946 .instrument(span)
947 .await
948 }
949}
950
951#[turbo_tasks::value(shared)]
953pub struct EcmascriptModuleContent {
954 pub inner_code: Rope,
955 pub source_map: Option<Rope>,
956 pub is_esm: bool,
957 pub strict: bool,
958 pub additional_ids: SmallVec<[ResolvedVc<ModuleId>; 1]>,
959}
960
961#[turbo_tasks::value(shared)]
962#[derive(Clone, Debug, Hash, TaskInput)]
963pub struct EcmascriptModuleContentOptions {
964 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
965 parsed: Option<ResolvedVc<ParseResult>>,
966 specified_module_type: SpecifiedModuleType,
967 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
968 references: ResolvedVc<ModuleReferences>,
969 part_references: Vec<ResolvedVc<EcmascriptModulePartReference>>,
970 esm_references: ResolvedVc<EsmAssetReferences>,
971 code_generation: ResolvedVc<CodeGens>,
972 async_module: ResolvedVc<OptionAsyncModule>,
973 generate_source_map: bool,
974 original_source_map: Option<ResolvedVc<Box<dyn GenerateSourceMap>>>,
975 exports: ResolvedVc<EcmascriptExports>,
976 async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
977}
978
979impl EcmascriptModuleContentOptions {
980 async fn merged_code_gens(
981 &self,
982 scope_hoisting_context: ScopeHoistingContext<'_>,
983 eval_context: &EvalContext,
984 ) -> Result<Vec<CodeGeneration>> {
985 let EcmascriptModuleContentOptions {
988 module,
989 chunking_context,
990 references,
991 part_references,
992 esm_references,
993 code_generation,
994 async_module,
995 exports,
996 async_module_info,
997 ..
998 } = self;
999
1000 async {
1001 let additional_code_gens = [
1002 if let Some(async_module) = &*async_module.await? {
1003 Some(
1004 async_module
1005 .code_generation(
1006 async_module_info.map(|info| *info),
1007 **references,
1008 **chunking_context,
1009 )
1010 .await?,
1011 )
1012 } else {
1013 None
1014 },
1015 if let EcmascriptExports::EsmExports(exports) = *exports.await? {
1016 Some(
1017 exports
1018 .code_generation(
1019 **chunking_context,
1020 scope_hoisting_context,
1021 eval_context,
1022 *module,
1023 )
1024 .await?,
1025 )
1026 } else {
1027 None
1028 },
1029 ];
1030
1031 let part_code_gens = part_references
1032 .iter()
1033 .map(|r| r.code_generation(**chunking_context, scope_hoisting_context))
1034 .try_join()
1035 .await?;
1036
1037 let esm_code_gens = esm_references
1038 .await?
1039 .iter()
1040 .map(|r| r.code_generation(**chunking_context, scope_hoisting_context))
1041 .try_join()
1042 .await?;
1043
1044 let code_gens = code_generation
1045 .await?
1046 .iter()
1047 .map(|c| {
1048 c.code_generation(
1049 **chunking_context,
1050 scope_hoisting_context,
1051 *module,
1052 *exports,
1053 )
1054 })
1055 .try_join()
1056 .await?;
1057
1058 anyhow::Ok(
1059 part_code_gens
1060 .into_iter()
1061 .chain(esm_code_gens.into_iter())
1062 .chain(additional_code_gens.into_iter().flatten())
1063 .chain(code_gens.into_iter())
1064 .collect(),
1065 )
1066 }
1067 .instrument(tracing::info_span!("precompute code generation"))
1068 .await
1069 }
1070}
1071
1072#[turbo_tasks::value_impl]
1073impl EcmascriptModuleContent {
1074 #[turbo_tasks::function]
1076 pub async fn new(input: Vc<EcmascriptModuleContentOptions>) -> Result<Vc<Self>> {
1077 let input = input.await?;
1078 let EcmascriptModuleContentOptions {
1079 parsed,
1080 module,
1081 specified_module_type,
1082 generate_source_map,
1083 original_source_map,
1084 chunking_context,
1085 ..
1086 } = &*input;
1087
1088 let minify = chunking_context.minify_type().await?;
1089
1090 let content = process_parse_result(
1091 *parsed,
1092 module.ident(),
1093 *specified_module_type,
1094 *generate_source_map,
1095 *original_source_map,
1096 *minify,
1097 Some(&*input),
1098 None,
1099 )
1100 .await?;
1101 emit_content(content, Default::default()).await
1102 }
1103
1104 #[turbo_tasks::function]
1106 pub async fn new_without_analysis(
1107 parsed: Vc<ParseResult>,
1108 ident: Vc<AssetIdent>,
1109 specified_module_type: SpecifiedModuleType,
1110 generate_source_map: bool,
1111 ) -> Result<Vc<Self>> {
1112 let content = process_parse_result(
1113 Some(parsed.to_resolved().await?),
1114 ident,
1115 specified_module_type,
1116 generate_source_map,
1117 None,
1118 MinifyType::NoMinify,
1119 None,
1120 None,
1121 )
1122 .await?;
1123 emit_content(content, Default::default()).await
1124 }
1125
1126 #[turbo_tasks::function]
1133 pub async fn new_merged(
1134 modules: Vec<(
1135 ResolvedVc<Box<dyn EcmascriptAnalyzable>>,
1136 MergeableModuleExposure,
1137 )>,
1138 module_options: Vec<Vc<EcmascriptModuleContentOptions>>,
1139 entry_points: Vec<ResolvedVc<Box<dyn EcmascriptAnalyzable>>>,
1140 ) -> Result<Vc<Self>> {
1141 async {
1142 let modules = modules
1143 .into_iter()
1144 .map(|(m, exposed)| {
1145 (
1146 ResolvedVc::try_sidecast::<Box<dyn EcmascriptChunkPlaceable>>(m).unwrap(),
1147 exposed,
1148 )
1149 })
1150 .collect::<FxIndexMap<_, _>>();
1151 let entry_points = entry_points
1152 .into_iter()
1153 .map(|m| {
1154 let m =
1155 ResolvedVc::try_sidecast::<Box<dyn EcmascriptChunkPlaceable>>(m).unwrap();
1156 (m, modules.get_index_of(&m).unwrap())
1157 })
1158 .collect::<Vec<_>>();
1159
1160 let globals_merged = Globals::default();
1161
1162 let contents = module_options
1163 .iter()
1164 .map(async |options| {
1165 let options = options.await?;
1166 let EcmascriptModuleContentOptions {
1167 chunking_context,
1168 parsed,
1169 module,
1170 specified_module_type,
1171 generate_source_map,
1172 original_source_map,
1173 ..
1174 } = &*options;
1175
1176 let result = process_parse_result(
1177 *parsed,
1178 module.ident(),
1179 *specified_module_type,
1180 *generate_source_map,
1181 *original_source_map,
1182 *chunking_context.minify_type().await?,
1183 Some(&*options),
1184 Some(ScopeHoistingOptions {
1185 module: *module,
1186 modules: &modules,
1187 }),
1188 )
1189 .await?;
1190
1191 Ok((*module, result))
1192 })
1193 .try_join()
1194 .await?;
1195
1196 let (merged_ast, comments, source_maps, original_source_maps, lookup_table) =
1197 merge_modules(contents, &entry_points, &globals_merged).await?;
1198
1199 let options = module_options.last().unwrap().await?;
1202
1203 let modules_header_width = modules.len().next_power_of_two().trailing_zeros();
1204 let content = CodeGenResult {
1205 program: merged_ast,
1206 source_map: CodeGenResultSourceMap::ScopeHoisting {
1207 modules_header_width,
1208 lookup_table: lookup_table.clone(),
1209 source_maps,
1210 },
1211 comments: CodeGenResultComments::ScopeHoisting {
1212 modules_header_width,
1213 lookup_table,
1214 comments,
1215 },
1216 is_esm: true,
1217 strict: true,
1218 original_source_map: CodeGenResultOriginalSourceMap::ScopeHoisting(
1219 original_source_maps,
1220 ),
1221 minify: *options.chunking_context.minify_type().await?,
1222 scope_hoisting_syntax_contexts: None,
1223 };
1224
1225 let first_entry = entry_points.first().unwrap().0;
1226 let additional_ids = modules
1227 .keys()
1228 .filter(|m| {
1235 **m != first_entry
1236 && *modules.get(*m).unwrap() == MergeableModuleExposure::External
1237 })
1238 .map(|m| m.chunk_item_id(*options.chunking_context).to_resolved())
1239 .try_join()
1240 .await?
1241 .into();
1242
1243 emit_content(content, additional_ids)
1244 .instrument(tracing::info_span!("emit code"))
1245 .await
1246 }
1247 .instrument(tracing::info_span!(
1248 "generate merged code",
1249 modules = module_options.len()
1250 ))
1251 .await
1252 }
1253}
1254
1255#[instrument(level = Level::TRACE, skip_all, name = "merge")]
1264#[allow(clippy::type_complexity)]
1265async fn merge_modules(
1266 mut contents: Vec<(ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, CodeGenResult)>,
1267 entry_points: &Vec<(ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, usize)>,
1268 globals_merged: &'_ Globals,
1269) -> Result<(
1270 Program,
1271 Vec<CodeGenResultComments>,
1272 Vec<CodeGenResultSourceMap>,
1273 SmallVec<[ResolvedVc<Box<dyn GenerateSourceMap>>; 1]>,
1274 Arc<Mutex<Vec<ModulePosition>>>,
1275)> {
1276 struct SetSyntaxContextVisitor<'a> {
1277 modules_header_width: u32,
1278 current_module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1279 current_module_idx: u32,
1280 lookup_table: &'a mut Vec<ModulePosition>,
1281 reverse_module_contexts:
1283 FxHashMap<SyntaxContext, ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
1284 export_contexts:
1287 &'a FxHashMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, &'a FxHashMap<RcStr, Id>>,
1288 unique_contexts_cache: &'a mut FxHashMap<
1291 (ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, SyntaxContext),
1292 SyntaxContext,
1293 >,
1294
1295 error: anyhow::Result<()>,
1296 }
1297
1298 impl<'a> SetSyntaxContextVisitor<'a> {
1299 fn get_context_for(
1300 &mut self,
1301 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1302 local_ctxt: SyntaxContext,
1303 ) -> SyntaxContext {
1304 if let Some(&global_ctxt) = self.unique_contexts_cache.get(&(module, local_ctxt)) {
1305 global_ctxt
1306 } else {
1307 let global_ctxt = SyntaxContext::empty().apply_mark(Mark::new());
1308 self.unique_contexts_cache
1309 .insert((module, local_ctxt), global_ctxt);
1310 global_ctxt
1311 }
1312 }
1313 }
1314
1315 impl VisitMut for SetSyntaxContextVisitor<'_> {
1316 fn visit_mut_ident(&mut self, ident: &mut Ident) {
1317 let Ident {
1318 sym, ctxt, span, ..
1319 } = ident;
1320
1321 if let Some(&module) = self.reverse_module_contexts.get(ctxt) {
1324 let eval_context_exports = self.export_contexts.get(&module).unwrap();
1325 let sym_rc_str: RcStr = sym.as_str().into();
1328 let (local, local_ctxt) = if let Some((local, local_ctxt)) =
1329 eval_context_exports.get(&sym_rc_str)
1330 {
1331 (Some(local), *local_ctxt)
1332 } else if sym.starts_with("__TURBOPACK__imported__module__") {
1333 (None, SyntaxContext::empty())
1338 } else {
1339 self.error = Err(anyhow::anyhow!(
1340 "Expected to find a local export for {sym} with ctxt {ctxt:#?} in \
1341 {eval_context_exports:?}",
1342 ));
1343 return;
1344 };
1345
1346 let global_ctxt = self.get_context_for(module, local_ctxt);
1347
1348 if let Some(local) = local {
1349 *sym = local.clone();
1350 }
1351 *ctxt = global_ctxt;
1352 span.visit_mut_with(self);
1353 } else {
1354 ident.visit_mut_children_with(self);
1355 }
1356 }
1357
1358 fn visit_mut_syntax_context(&mut self, local_ctxt: &mut SyntaxContext) {
1359 let module = self
1362 .reverse_module_contexts
1363 .get(local_ctxt)
1364 .copied()
1365 .unwrap_or(self.current_module);
1366
1367 let global_ctxt = self.get_context_for(module, *local_ctxt);
1368 *local_ctxt = global_ctxt;
1369 }
1370 fn visit_mut_span(&mut self, span: &mut Span) {
1371 span.lo = CodeGenResultComments::encode_bytepos_with_vec(
1374 self.modules_header_width,
1375 self.current_module_idx,
1376 span.lo,
1377 self.lookup_table,
1378 )
1379 .unwrap_or_else(|err| {
1380 self.error = Err(err);
1381 span.lo
1382 });
1383 span.hi = CodeGenResultComments::encode_bytepos_with_vec(
1384 self.modules_header_width,
1385 self.current_module_idx,
1386 span.hi,
1387 self.lookup_table,
1388 )
1389 .unwrap_or_else(|err| {
1390 self.error = Err(err);
1391 span.hi
1392 });
1393 }
1394 }
1395
1396 let mut programs = contents
1399 .iter_mut()
1400 .map(|(_, content)| content.program.take())
1401 .collect::<Vec<_>>();
1402
1403 let export_contexts = contents
1404 .iter()
1405 .map(|(module, content)| {
1406 Ok((
1407 *module,
1408 content
1409 .scope_hoisting_syntax_contexts
1410 .as_ref()
1411 .map(|(_, export_contexts)| export_contexts)
1412 .context("expected exports contexts")?,
1413 ))
1414 })
1415 .collect::<Result<FxHashMap<_, _>>>()?;
1416
1417 let mut lookup_table = Vec::new();
1418 let result = GLOBALS.set(globals_merged, || {
1419 let _ = tracing::trace_span!("merge inner").entered();
1420 let mut unique_contexts_cache =
1422 FxHashMap::with_capacity_and_hasher(contents.len() * 5, Default::default());
1423
1424 let mut prepare_module =
1425 |module_count: usize,
1426 current_module_idx: usize,
1427 (module, content): &(ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, CodeGenResult),
1428 program: &mut Program,
1429 lookup_table: &mut Vec<ModulePosition>| {
1430 let _ = tracing::trace_span!("prepare module").entered();
1431 if let CodeGenResult {
1432 scope_hoisting_syntax_contexts: Some((module_contexts, _)),
1433 ..
1434 } = content
1435 {
1436 let modules_header_width = module_count.next_power_of_two().trailing_zeros();
1437 GLOBALS.set(globals_merged, || {
1438 let mut visitor = SetSyntaxContextVisitor {
1439 modules_header_width,
1440 current_module: *module,
1441 current_module_idx: current_module_idx as u32,
1442 lookup_table,
1443 reverse_module_contexts: module_contexts
1444 .iter()
1445 .map(|e| (*e.value(), *e.key()))
1446 .collect(),
1447 export_contexts: &export_contexts,
1448 unique_contexts_cache: &mut unique_contexts_cache,
1449 error: Ok(()),
1450 };
1451 program.visit_mut_with(&mut visitor);
1452 visitor.error
1453 })?;
1454
1455 Ok(match program.take() {
1456 Program::Module(module) => Either::Left(module.body.into_iter()),
1457 Program::Script(script) => {
1460 Either::Right(script.body.into_iter().map(ModuleItem::Stmt))
1461 }
1462 })
1463 } else {
1464 bail!("Expected scope_hosting_syntax_contexts");
1465 }
1466 };
1467
1468 let mut inserted = FxHashSet::with_capacity_and_hasher(contents.len(), Default::default());
1469 inserted.extend(entry_points.iter().map(|(_, i)| *i));
1471
1472 let mut inserted_imports = FxHashMap::default();
1473
1474 let span = tracing::trace_span!("merge ASTs");
1475 let mut queue = entry_points
1478 .iter()
1479 .map(|&(_, i)| {
1480 prepare_module(
1481 contents.len(),
1482 i,
1483 &contents[i],
1484 &mut programs[i],
1485 &mut lookup_table,
1486 )
1487 .map_err(|err| (i, err))
1488 })
1489 .flatten_ok()
1490 .rev()
1491 .collect::<Result<Vec<_>, _>>()?;
1492 let mut result = vec![];
1493 while let Some(item) = queue.pop() {
1494 if let ModuleItem::Stmt(stmt) = &item {
1495 match stmt {
1496 Stmt::Expr(ExprStmt { expr, .. }) => {
1497 if let Expr::Call(CallExpr {
1498 callee: Callee::Expr(callee),
1499 args,
1500 ..
1501 }) = &**expr
1502 && callee.is_ident_ref_to("__turbopack_merged_esm__")
1503 {
1504 let index =
1505 args[0].expr.as_lit().unwrap().as_num().unwrap().value as usize;
1506
1507 if inserted.insert(index) {
1509 queue.extend(
1510 prepare_module(
1511 contents.len(),
1512 index,
1513 &contents[index],
1514 &mut programs[index],
1515 &mut lookup_table,
1516 )
1517 .map_err(|err| (index, err))?
1518 .into_iter()
1519 .rev(),
1520 );
1521 }
1522 continue;
1523 }
1524 }
1525 Stmt::Decl(Decl::Var(var)) => {
1526 if let [decl] = &*var.decls
1527 && let Some(name) = decl.name.as_ident()
1528 && name.sym.starts_with("__TURBOPACK__imported__module__")
1529 {
1530 match inserted_imports.entry(name.sym.clone()) {
1535 Entry::Occupied(entry) => {
1536 let entry_ctxt = *entry.get();
1541 let new = Ident::new(name.sym.clone(), DUMMY_SP, name.ctxt);
1542 let old = Ident::new(name.sym.clone(), DUMMY_SP, entry_ctxt);
1543 result.push(ModuleItem::Stmt(
1544 quote!("var $new = $old;" as Stmt,
1545 new: Ident = new,
1546 old: Ident = old
1547 ),
1548 ));
1549 continue;
1550 }
1551 Entry::Vacant(entry) => {
1552 entry.insert(name.ctxt);
1553 }
1554 }
1555 }
1556 }
1557 _ => (),
1558 }
1559 }
1560
1561 result.push(item);
1562 }
1563 drop(span);
1564
1565 let span = tracing::trace_span!("hygiene").entered();
1566 let mut merged_ast = Program::Module(swc_core::ecma::ast::Module {
1567 body: result,
1568 span: DUMMY_SP,
1569 shebang: None,
1570 });
1571 merged_ast.visit_mut_with(&mut swc_core::ecma::transforms::base::hygiene::hygiene());
1572 drop(span);
1573
1574 Ok((merged_ast, inserted))
1575 });
1576
1577 let (merged_ast, inserted) = match result {
1578 Ok(v) => v,
1579 Err((content_idx, err)) => {
1580 return Err(err.context(format!(
1581 "Processing {}",
1582 contents[content_idx].0.ident().to_string().await?
1583 )));
1584 }
1585 };
1586
1587 debug_assert!(
1588 inserted.len() == contents.len(),
1589 "Not all merged modules were inserted: {:?}",
1590 contents
1591 .iter()
1592 .enumerate()
1593 .map(async |(i, m)| Ok((inserted.contains(&i), m.0.ident().to_string().await?)))
1594 .try_join()
1595 .await?,
1596 );
1597
1598 let comments = contents
1599 .iter_mut()
1600 .map(|(_, content)| content.comments.take())
1601 .collect::<Vec<_>>();
1602
1603 let source_maps = contents
1604 .iter_mut()
1605 .map(|(_, content)| std::mem::take(&mut content.source_map))
1606 .collect::<Vec<_>>();
1607
1608 let original_source_maps = contents
1609 .iter_mut()
1610 .flat_map(|(_, content)| match content.original_source_map {
1611 CodeGenResultOriginalSourceMap::ScopeHoisting(_) => unreachable!(
1612 "Didn't expect nested CodeGenResultOriginalSourceMap::ScopeHoisting: {:?}",
1613 content.original_source_map
1614 ),
1615 CodeGenResultOriginalSourceMap::Single(map) => map,
1616 })
1617 .collect();
1618
1619 Ok((
1620 merged_ast,
1621 comments,
1622 source_maps,
1623 original_source_maps,
1624 Arc::new(Mutex::new(lookup_table)),
1625 ))
1626}
1627
1628#[derive(Clone, Copy)]
1633pub enum ScopeHoistingContext<'a> {
1634 Some {
1635 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1637 modules:
1639 &'a FxIndexMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, MergeableModuleExposure>,
1640
1641 is_import_mark: Mark,
1642 globals: &'a Arc<Globals>,
1643 module_syntax_contexts_cache:
1645 &'a FxDashMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, SyntaxContext>,
1646 },
1647 None,
1648}
1649
1650impl<'a> ScopeHoistingContext<'a> {
1651 pub fn module(&self) -> Option<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>> {
1653 match self {
1654 ScopeHoistingContext::Some { module, .. } => Some(*module),
1655 ScopeHoistingContext::None => None,
1656 }
1657 }
1658
1659 pub fn skip_module_exports(&self) -> bool {
1661 match self {
1662 ScopeHoistingContext::Some {
1663 module, modules, ..
1664 } => match modules.get(module).unwrap() {
1665 MergeableModuleExposure::None => true,
1666 MergeableModuleExposure::Internal | MergeableModuleExposure::External => false,
1667 },
1668 ScopeHoistingContext::None => false,
1669 }
1670 }
1671
1672 pub fn get_module_syntax_context(
1674 &self,
1675 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1676 ) -> Option<SyntaxContext> {
1677 match self {
1678 ScopeHoistingContext::Some {
1679 modules,
1680 module_syntax_contexts_cache,
1681 globals,
1682 is_import_mark,
1683 ..
1684 } => {
1685 if !modules.contains_key(&module) {
1686 return None;
1687 }
1688
1689 Some(match module_syntax_contexts_cache.entry(module) {
1690 dashmap::Entry::Occupied(e) => *e.get(),
1691 dashmap::Entry::Vacant(e) => {
1692 let ctxt = GLOBALS.set(globals, || {
1693 let mark = Mark::fresh(*is_import_mark);
1694 SyntaxContext::empty()
1695 .apply_mark(*is_import_mark)
1696 .apply_mark(mark)
1697 });
1698
1699 e.insert(ctxt);
1700 ctxt
1701 }
1702 })
1703 }
1704 ScopeHoistingContext::None => None,
1705 }
1706 }
1707
1708 pub fn get_module_index(
1709 &self,
1710 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1711 ) -> Option<usize> {
1712 match self {
1713 ScopeHoistingContext::Some { modules, .. } => modules.get_index_of(&module),
1714 ScopeHoistingContext::None => None,
1715 }
1716 }
1717}
1718
1719struct CodeGenResult {
1720 program: Program,
1721 source_map: CodeGenResultSourceMap,
1722 comments: CodeGenResultComments,
1723 is_esm: bool,
1724 strict: bool,
1725 original_source_map: CodeGenResultOriginalSourceMap,
1726 minify: MinifyType,
1727 #[allow(clippy::type_complexity)]
1728 scope_hoisting_syntax_contexts: Option<(
1730 FxDashMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable + 'static>>, SyntaxContext>,
1731 FxHashMap<RcStr, Id>,
1732 )>,
1733}
1734
1735struct ScopeHoistingOptions<'a> {
1736 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1737 modules: &'a FxIndexMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, MergeableModuleExposure>,
1738}
1739
1740async fn process_parse_result(
1741 parsed: Option<ResolvedVc<ParseResult>>,
1742 ident: Vc<AssetIdent>,
1743 specified_module_type: SpecifiedModuleType,
1744 generate_source_map: bool,
1745 original_source_map: Option<ResolvedVc<Box<dyn GenerateSourceMap>>>,
1746 minify: MinifyType,
1747 options: Option<&EcmascriptModuleContentOptions>,
1748 scope_hoisting_options: Option<ScopeHoistingOptions<'_>>,
1749) -> Result<CodeGenResult> {
1750 with_consumed_parse_result(
1751 parsed,
1752 async |mut program, source_map, globals, eval_context, comments| -> Result<CodeGenResult> {
1753 let (top_level_mark, is_esm, strict) = eval_context
1754 .as_ref()
1755 .map_either(
1756 |e| {
1757 (
1758 e.top_level_mark,
1759 e.is_esm(specified_module_type),
1760 e.imports.strict,
1761 )
1762 },
1763 |e| {
1764 (
1765 e.top_level_mark,
1766 e.is_esm(specified_module_type),
1767 e.imports.strict,
1768 )
1769 },
1770 )
1771 .into_inner();
1772
1773 let (mut code_gens, retain_syntax_context, prepend_ident_comment) =
1774 if let Some(scope_hoisting_options) = scope_hoisting_options {
1775 let is_import_mark = GLOBALS.set(globals, || Mark::new());
1776
1777 let module_syntax_contexts_cache = FxDashMap::default();
1778 let ctx = ScopeHoistingContext::Some {
1779 module: scope_hoisting_options.module,
1780 modules: scope_hoisting_options.modules,
1781 module_syntax_contexts_cache: &module_syntax_contexts_cache,
1782 is_import_mark,
1783 globals,
1784 };
1785 let code_gens = options
1786 .unwrap()
1787 .merged_code_gens(
1788 ctx,
1789 match &eval_context {
1790 Either::Left(e) => e,
1791 Either::Right(e) => e,
1792 },
1793 )
1794 .await?;
1795
1796 let export_contexts = eval_context
1797 .map_either(
1798 |e| Cow::Owned(e.imports.exports),
1799 |e| Cow::Borrowed(&e.imports.exports),
1800 )
1801 .into_inner();
1802 let preserved_exports =
1803 match &*scope_hoisting_options.module.get_exports().await? {
1804 EcmascriptExports::EsmExports(exports) => exports
1805 .await?
1806 .exports
1807 .iter()
1808 .filter(|(_, e)| matches!(e, export::EsmExport::LocalBinding(_, _)))
1809 .map(|(name, e)| {
1810 if let Some((sym, ctxt)) = export_contexts.get(name) {
1811 Ok((sym.clone(), *ctxt))
1812 } else {
1813 bail!("Couldn't find export {} for binding {:?}", name, e);
1814 }
1815 })
1816 .collect::<Result<FxHashSet<_>>>()?,
1817 _ => Default::default(),
1818 };
1819
1820 let prepend_ident_comment = if matches!(minify, MinifyType::NoMinify) {
1821 Some(Comment {
1822 kind: CommentKind::Line,
1823 span: DUMMY_SP,
1824 text: format!(" MERGED MODULE: {}", ident.to_string().await?).into(),
1825 })
1826 } else {
1827 None
1828 };
1829
1830 (
1831 code_gens,
1832 Some((
1833 is_import_mark,
1834 module_syntax_contexts_cache,
1835 preserved_exports,
1836 export_contexts,
1837 )),
1838 prepend_ident_comment,
1839 )
1840 } else if let Some(options) = options {
1841 (
1842 options
1843 .merged_code_gens(
1844 ScopeHoistingContext::None,
1845 match &eval_context {
1846 Either::Left(e) => e,
1847 Either::Right(e) => e,
1848 },
1849 )
1850 .await?,
1851 None,
1852 None,
1853 )
1854 } else {
1855 (vec![], None, None)
1856 };
1857
1858 let extra_comments = SwcComments {
1859 leading: Default::default(),
1860 trailing: Default::default(),
1861 };
1862
1863 process_content_with_code_gens(&mut program, globals, &mut code_gens);
1864
1865 for comments in code_gens.iter_mut().flat_map(|cg| cg.comments.as_mut()) {
1866 let leading = Arc::unwrap_or_clone(take(&mut comments.leading));
1867 let trailing = Arc::unwrap_or_clone(take(&mut comments.trailing));
1868
1869 for (pos, v) in leading {
1870 extra_comments.leading.entry(pos).or_default().extend(v);
1871 }
1872
1873 for (pos, v) in trailing {
1874 extra_comments.trailing.entry(pos).or_default().extend(v);
1875 }
1876 }
1877
1878 GLOBALS.set(globals, || {
1879 if let Some(prepend_ident_comment) = prepend_ident_comment {
1880 let span = Span::dummy_with_cmt();
1881 extra_comments.add_leading(span.lo, prepend_ident_comment);
1882 let stmt = Stmt::Empty(EmptyStmt { span });
1883 match &mut program {
1884 Program::Module(module) => module.body.prepend_stmt(ModuleItem::Stmt(stmt)),
1885 Program::Script(script) => script.body.prepend_stmt(stmt),
1886 }
1887 }
1888
1889 if let Some((is_import_mark, _, preserved_exports, _)) = &retain_syntax_context {
1890 program.visit_mut_with(&mut hygiene_rename_only(
1891 Some(top_level_mark),
1892 *is_import_mark,
1893 preserved_exports,
1894 ));
1895 } else {
1896 program.visit_mut_with(
1897 &mut swc_core::ecma::transforms::base::hygiene::hygiene_with_config(
1898 swc_core::ecma::transforms::base::hygiene::Config {
1899 top_level_mark,
1900 ..Default::default()
1901 },
1902 ),
1903 );
1904 }
1905 program.visit_mut_with(&mut swc_core::ecma::transforms::base::fixer::fixer(None));
1906
1907 remove_shebang(&mut program);
1910 remove_directives(&mut program);
1911 });
1912
1913 Ok(CodeGenResult {
1914 program,
1915 source_map: if generate_source_map {
1916 CodeGenResultSourceMap::Single {
1917 source_map: source_map.clone(),
1918 }
1919 } else {
1920 CodeGenResultSourceMap::None
1921 },
1922 comments: CodeGenResultComments::Single {
1923 comments,
1924 extra_comments,
1925 },
1926 is_esm,
1927 strict,
1928 original_source_map: CodeGenResultOriginalSourceMap::Single(original_source_map),
1929 minify,
1930 scope_hoisting_syntax_contexts: retain_syntax_context
1931 .map(|(_, ctxts, _, export_contexts)| (ctxts, export_contexts.into_owned())),
1933 })
1934 },
1935 async |parse_result| -> Result<CodeGenResult> {
1936 Ok(match parse_result {
1937 ParseResult::Ok { .. } => unreachable!(),
1938 ParseResult::Unparsable { messages } => {
1939 let path = ident.path().to_string().await?;
1940 let error_messages = messages
1941 .as_ref()
1942 .and_then(|m| m.first().map(|f| format!("\n{f}")))
1943 .unwrap_or("".into());
1944 let msg = format!("Could not parse module '{path}'\n{error_messages}");
1945 let body = vec![
1946 quote!(
1947 "const e = new Error($msg);" as Stmt,
1948 msg: Expr = Expr::Lit(msg.into()),
1949 ),
1950 quote!("e.code = 'MODULE_UNPARSABLE';" as Stmt),
1951 quote!("throw e;" as Stmt),
1952 ];
1953
1954 CodeGenResult {
1955 program: Program::Script(Script {
1956 span: DUMMY_SP,
1957 body,
1958 shebang: None,
1959 }),
1960 source_map: CodeGenResultSourceMap::None,
1961 comments: CodeGenResultComments::Empty,
1962 is_esm: false,
1963 strict: false,
1964 original_source_map: CodeGenResultOriginalSourceMap::Single(None),
1965 minify: MinifyType::NoMinify,
1966 scope_hoisting_syntax_contexts: None,
1967 }
1968 }
1969 ParseResult::NotFound => {
1970 let path = ident.path().to_string().await?;
1971 let msg = format!("Could not parse module '{path}', file not found");
1972 let body = vec![
1973 quote!(
1974 "const e = new Error($msg);" as Stmt,
1975 msg: Expr = Expr::Lit(msg.into()),
1976 ),
1977 quote!("e.code = 'MODULE_UNPARSABLE';" as Stmt),
1978 quote!("throw e;" as Stmt),
1979 ];
1980 CodeGenResult {
1981 program: Program::Script(Script {
1982 span: DUMMY_SP,
1983 body,
1984 shebang: None,
1985 }),
1986 source_map: CodeGenResultSourceMap::None,
1987 comments: CodeGenResultComments::Empty,
1988 is_esm: false,
1989 strict: false,
1990 original_source_map: CodeGenResultOriginalSourceMap::Single(None),
1991 minify: MinifyType::NoMinify,
1992 scope_hoisting_syntax_contexts: None,
1993 }
1994 }
1995 })
1996 },
1997 )
1998 .instrument(tracing::trace_span!(
1999 "process parse result",
2000 ident = display(ident.to_string().await?),
2001 ))
2002 .await
2003}
2004
2005async fn with_consumed_parse_result<T>(
2007 parsed: Option<ResolvedVc<ParseResult>>,
2008 success: impl AsyncFnOnce(
2009 Program,
2010 &Arc<SourceMap>,
2011 &Arc<Globals>,
2012 Either<EvalContext, &'_ EvalContext>,
2013 Either<ImmutableComments, Arc<ImmutableComments>>,
2014 ) -> Result<T>,
2015 error: impl AsyncFnOnce(&ParseResult) -> Result<T>,
2016) -> Result<T> {
2017 let Some(parsed) = parsed else {
2018 let globals = Globals::new();
2019 let eval_context = GLOBALS.set(&globals, || EvalContext {
2020 unresolved_mark: Mark::new(),
2021 top_level_mark: Mark::new(),
2022 imports: Default::default(),
2023 force_free_values: Default::default(),
2024 });
2025 return success(
2026 Program::Module(swc_core::ecma::ast::Module::dummy()),
2027 &Default::default(),
2028 &Default::default(),
2029 Either::Left(eval_context),
2030 Either::Left(Default::default()),
2031 )
2032 .await;
2033 };
2034
2035 let parsed = parsed.final_read_hint().await?;
2036 match &*parsed {
2037 ParseResult::Ok { .. } => {
2038 let mut parsed = ReadRef::try_unwrap(parsed);
2039 let (program, source_map, globals, eval_context, comments) = match &mut parsed {
2040 Ok(ParseResult::Ok {
2041 program,
2042 source_map,
2043 globals,
2044 eval_context,
2045 comments,
2046 }) => (
2047 program.take(),
2048 &*source_map,
2049 &*globals,
2050 Either::Left(std::mem::replace(
2051 eval_context,
2052 EvalContext {
2053 unresolved_mark: eval_context.unresolved_mark,
2054 top_level_mark: eval_context.top_level_mark,
2055 imports: Default::default(),
2056 force_free_values: Default::default(),
2057 },
2058 )),
2059 match Arc::try_unwrap(take(comments)) {
2060 Ok(comments) => Either::Left(comments),
2061 Err(comments) => Either::Right(comments),
2062 },
2063 ),
2064 Err(parsed) => {
2065 let ParseResult::Ok {
2066 program,
2067 source_map,
2068 globals,
2069 eval_context,
2070 comments,
2071 } = &**parsed
2072 else {
2073 unreachable!();
2074 };
2075 (
2076 program.clone(),
2077 source_map,
2078 globals,
2079 Either::Right(eval_context),
2080 Either::Right(comments.clone()),
2081 )
2082 }
2083 _ => unreachable!(),
2084 };
2085
2086 success(program, source_map, globals, eval_context, comments).await
2087 }
2088 _ => error(&parsed).await,
2089 }
2090}
2091
2092async fn emit_content(
2093 content: CodeGenResult,
2094 additional_ids: SmallVec<[ResolvedVc<ModuleId>; 1]>,
2095) -> Result<Vc<EcmascriptModuleContent>> {
2096 let CodeGenResult {
2097 program,
2098 source_map,
2099 comments,
2100 is_esm,
2101 strict,
2102 original_source_map,
2103 minify,
2104 scope_hoisting_syntax_contexts: _,
2105 } = content;
2106
2107 let generate_source_map = source_map.is_some();
2108
2109 let source_map_names = if generate_source_map {
2111 let mut collector = IdentCollector::default();
2112 program.visit_with(&mut collector);
2113 collector.into_map()
2114 } else {
2115 Default::default()
2116 };
2117
2118 let mut bytes: Vec<u8> = vec![];
2119 let mut mappings = vec![];
2123
2124 let source_map = Arc::new(source_map);
2125
2126 {
2127 let mut wr = JsWriter::new(
2128 Default::default(),
2130 "\n",
2131 &mut bytes,
2132 generate_source_map.then_some(&mut mappings),
2133 );
2134 if matches!(minify, MinifyType::Minify { .. }) {
2135 wr.set_indent_str("");
2136 }
2137
2138 let comments = comments.consumable();
2139
2140 let mut emitter = Emitter {
2141 cfg: swc_core::ecma::codegen::Config::default(),
2142 cm: source_map.clone(),
2143 comments: Some(&comments as &dyn Comments),
2144 wr,
2145 };
2146
2147 emitter.emit_program(&program)?;
2148 drop(program);
2150 }
2151
2152 let source_map = if generate_source_map {
2153 let original_source_maps = original_source_map
2154 .iter()
2155 .map(|map| map.generate_source_map())
2156 .try_join()
2157 .await?;
2158 let original_source_maps = original_source_maps
2159 .iter()
2160 .filter_map(|map| map.as_content())
2161 .map(|map| map.content())
2162 .collect::<Vec<_>>();
2163
2164 Some(generate_js_source_map(
2165 &*source_map,
2166 mappings,
2167 original_source_maps,
2168 matches!(
2169 original_source_map,
2170 CodeGenResultOriginalSourceMap::Single(_)
2171 ),
2172 true,
2173 source_map_names,
2174 )?)
2175 } else {
2176 None
2177 };
2178
2179 Ok(EcmascriptModuleContent {
2180 inner_code: bytes.into(),
2181 source_map,
2182 is_esm,
2183 strict,
2184 additional_ids,
2185 }
2186 .cell())
2187}
2188
2189#[instrument(level = Level::TRACE, skip_all, name = "apply code generation")]
2190fn process_content_with_code_gens(
2191 program: &mut Program,
2192 globals: &Globals,
2193 code_gens: &mut Vec<CodeGeneration>,
2194) {
2195 let mut visitors = Vec::new();
2196 let mut root_visitors = Vec::new();
2197 let mut early_hoisted_stmts = FxIndexMap::default();
2198 let mut hoisted_stmts = FxIndexMap::default();
2199 let mut early_late_stmts = FxIndexMap::default();
2200 let mut late_stmts = FxIndexMap::default();
2201 for code_gen in code_gens {
2202 for CodeGenerationHoistedStmt { key, stmt } in code_gen.hoisted_stmts.drain(..) {
2203 hoisted_stmts.entry(key).or_insert(stmt);
2204 }
2205 for CodeGenerationHoistedStmt { key, stmt } in code_gen.early_hoisted_stmts.drain(..) {
2206 early_hoisted_stmts.insert(key.clone(), stmt);
2207 }
2208 for CodeGenerationHoistedStmt { key, stmt } in code_gen.late_stmts.drain(..) {
2209 late_stmts.insert(key.clone(), stmt);
2210 }
2211 for CodeGenerationHoistedStmt { key, stmt } in code_gen.early_late_stmts.drain(..) {
2212 early_late_stmts.insert(key.clone(), stmt);
2213 }
2214 for (path, visitor) in &code_gen.visitors {
2215 if path.is_empty() {
2216 root_visitors.push(&**visitor);
2217 } else {
2218 visitors.push((path, &**visitor));
2219 }
2220 }
2221 }
2222
2223 GLOBALS.set(globals, || {
2224 if !visitors.is_empty() {
2225 program.visit_mut_with_ast_path(
2226 &mut ApplyVisitors::new(visitors),
2227 &mut Default::default(),
2228 );
2229 }
2230 for pass in root_visitors {
2231 program.modify(pass);
2232 }
2233 });
2234
2235 match program {
2236 Program::Module(ast::Module { body, .. }) => {
2237 body.splice(
2238 0..0,
2239 early_hoisted_stmts
2240 .into_values()
2241 .chain(hoisted_stmts.into_values())
2242 .map(ModuleItem::Stmt),
2243 );
2244 body.extend(
2245 early_late_stmts
2246 .into_values()
2247 .chain(late_stmts.into_values())
2248 .map(ModuleItem::Stmt),
2249 );
2250 }
2251 Program::Script(Script { body, .. }) => {
2252 body.splice(
2253 0..0,
2254 early_hoisted_stmts
2255 .into_values()
2256 .chain(hoisted_stmts.into_values()),
2257 );
2258 body.extend(
2259 early_late_stmts
2260 .into_values()
2261 .chain(late_stmts.into_values()),
2262 );
2263 }
2264 };
2265}
2266
2267fn hygiene_rename_only(
2274 top_level_mark: Option<Mark>,
2275 is_import_mark: Mark,
2276 preserved_exports: &FxHashSet<Id>,
2277) -> impl VisitMut {
2278 struct HygieneRenamer<'a> {
2279 preserved_exports: &'a FxHashSet<Id>,
2280 is_import_mark: Mark,
2281 }
2282 impl swc_core::ecma::transforms::base::rename::Renamer for HygieneRenamer<'_> {
2284 type Target = Id;
2285
2286 const MANGLE: bool = false;
2287 const RESET_N: bool = true;
2288
2289 fn new_name_for(&self, orig: &Id, n: &mut usize) -> Atom {
2290 let res = if *n == 0 {
2291 orig.0.clone()
2292 } else {
2293 format!("{}{}", orig.0, n).into()
2294 };
2295 *n += 1;
2296 res
2297 }
2298
2299 fn preserve_name(&self, orig: &Id) -> bool {
2300 self.preserved_exports.contains(orig) || orig.1.has_mark(self.is_import_mark)
2301 }
2302 }
2303 swc_core::ecma::transforms::base::rename::renamer_keep_contexts(
2304 swc_core::ecma::transforms::base::hygiene::Config {
2305 top_level_mark: top_level_mark.unwrap_or_default(),
2306 ..Default::default()
2307 },
2308 HygieneRenamer {
2309 preserved_exports,
2310 is_import_mark,
2311 },
2312 )
2313}
2314
2315#[derive(Default)]
2316enum CodeGenResultSourceMap {
2317 #[default]
2318 None,
2320 Single {
2321 source_map: Arc<SourceMap>,
2322 },
2323 ScopeHoisting {
2324 modules_header_width: u32,
2327 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2328 source_maps: Vec<CodeGenResultSourceMap>,
2329 },
2330}
2331
2332impl CodeGenResultSourceMap {
2333 fn is_some(&self) -> bool {
2334 match self {
2335 CodeGenResultSourceMap::None => false,
2336 CodeGenResultSourceMap::Single { .. }
2337 | CodeGenResultSourceMap::ScopeHoisting { .. } => true,
2338 }
2339 }
2340}
2341
2342impl Debug for CodeGenResultSourceMap {
2343 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2344 match self {
2345 CodeGenResultSourceMap::None => write!(f, "CodeGenResultSourceMap::None"),
2346 CodeGenResultSourceMap::Single { source_map } => {
2347 write!(
2348 f,
2349 "CodeGenResultSourceMap::Single {{ source_map: {:?} }}",
2350 source_map.files().clone()
2351 )
2352 }
2353 CodeGenResultSourceMap::ScopeHoisting {
2354 modules_header_width,
2355 source_maps,
2356 ..
2357 } => write!(
2358 f,
2359 "CodeGenResultSourceMap::ScopeHoisting {{ modules_header_width: \
2360 {modules_header_width}, source_maps: {source_maps:?} }}",
2361 ),
2362 }
2363 }
2364}
2365
2366impl Files for CodeGenResultSourceMap {
2367 fn try_lookup_source_file(
2368 &self,
2369 pos: BytePos,
2370 ) -> Result<Option<Arc<SourceFile>>, SourceMapLookupError> {
2371 match self {
2372 CodeGenResultSourceMap::None => Ok(None),
2373 CodeGenResultSourceMap::Single { source_map } => source_map.try_lookup_source_file(pos),
2374 CodeGenResultSourceMap::ScopeHoisting {
2375 modules_header_width,
2376 lookup_table,
2377 source_maps,
2378 } => {
2379 let (module, pos) =
2380 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2381 source_maps[module].try_lookup_source_file(pos)
2382 }
2383 }
2384 }
2385
2386 fn is_in_file(&self, f: &Arc<SourceFile>, raw_pos: BytePos) -> bool {
2387 match self {
2388 CodeGenResultSourceMap::None => false,
2389 CodeGenResultSourceMap::Single { .. } => f.start_pos <= raw_pos && raw_pos < f.end_pos,
2390 CodeGenResultSourceMap::ScopeHoisting { .. } => {
2391 false
2397 }
2398 }
2399 }
2400
2401 fn map_raw_pos(&self, pos: BytePos) -> BytePos {
2402 match self {
2403 CodeGenResultSourceMap::None => BytePos::DUMMY,
2404 CodeGenResultSourceMap::Single { .. } => pos,
2405 CodeGenResultSourceMap::ScopeHoisting {
2406 modules_header_width,
2407 lookup_table,
2408 ..
2409 } => CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table).1,
2410 }
2411 }
2412}
2413
2414impl SourceMapper for CodeGenResultSourceMap {
2415 fn lookup_char_pos(&self, pos: BytePos) -> Loc {
2416 match self {
2417 CodeGenResultSourceMap::None => {
2418 panic!("CodeGenResultSourceMap::None cannot lookup_char_pos")
2419 }
2420 CodeGenResultSourceMap::Single { source_map } => source_map.lookup_char_pos(pos),
2421 CodeGenResultSourceMap::ScopeHoisting {
2422 modules_header_width,
2423 lookup_table,
2424 source_maps,
2425 } => {
2426 let (module, pos) =
2427 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2428 source_maps[module].lookup_char_pos(pos)
2429 }
2430 }
2431 }
2432 fn span_to_lines(&self, sp: Span) -> FileLinesResult {
2433 match self {
2434 CodeGenResultSourceMap::None => {
2435 panic!("CodeGenResultSourceMap::None cannot span_to_lines")
2436 }
2437 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_lines(sp),
2438 CodeGenResultSourceMap::ScopeHoisting {
2439 modules_header_width,
2440 lookup_table,
2441 source_maps,
2442 } => {
2443 let (module, lo) = CodeGenResultComments::decode_bytepos(
2444 *modules_header_width,
2445 sp.lo,
2446 lookup_table,
2447 );
2448 source_maps[module].span_to_lines(Span {
2449 lo,
2450 hi: CodeGenResultComments::decode_bytepos(
2451 *modules_header_width,
2452 sp.hi,
2453 lookup_table,
2454 )
2455 .1,
2456 })
2457 }
2458 }
2459 }
2460 fn span_to_string(&self, sp: Span) -> String {
2461 match self {
2462 CodeGenResultSourceMap::None => {
2463 panic!("CodeGenResultSourceMap::None cannot span_to_string")
2464 }
2465 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_string(sp),
2466 CodeGenResultSourceMap::ScopeHoisting {
2467 modules_header_width,
2468 lookup_table,
2469 source_maps,
2470 } => {
2471 let (module, lo) = CodeGenResultComments::decode_bytepos(
2472 *modules_header_width,
2473 sp.lo,
2474 lookup_table,
2475 );
2476 source_maps[module].span_to_string(Span {
2477 lo,
2478 hi: CodeGenResultComments::decode_bytepos(
2479 *modules_header_width,
2480 sp.hi,
2481 lookup_table,
2482 )
2483 .1,
2484 })
2485 }
2486 }
2487 }
2488 fn span_to_filename(&self, sp: Span) -> Arc<FileName> {
2489 match self {
2490 CodeGenResultSourceMap::None => {
2491 panic!("CodeGenResultSourceMap::None cannot span_to_filename")
2492 }
2493 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_filename(sp),
2494 CodeGenResultSourceMap::ScopeHoisting {
2495 modules_header_width,
2496 lookup_table,
2497 source_maps,
2498 } => {
2499 let (module, lo) = CodeGenResultComments::decode_bytepos(
2500 *modules_header_width,
2501 sp.lo,
2502 lookup_table,
2503 );
2504 source_maps[module].span_to_filename(Span {
2505 lo,
2506 hi: CodeGenResultComments::decode_bytepos(
2507 *modules_header_width,
2508 sp.hi,
2509 lookup_table,
2510 )
2511 .1,
2512 })
2513 }
2514 }
2515 }
2516 fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> {
2517 match self {
2518 CodeGenResultSourceMap::None => {
2519 panic!("CodeGenResultSourceMap::None cannot merge_spans")
2520 }
2521 CodeGenResultSourceMap::Single { source_map } => source_map.merge_spans(sp_lhs, sp_rhs),
2522 CodeGenResultSourceMap::ScopeHoisting {
2523 modules_header_width,
2524 lookup_table,
2525 source_maps,
2526 } => {
2527 let (module_lhs, lo_lhs) = CodeGenResultComments::decode_bytepos(
2528 *modules_header_width,
2529 sp_lhs.lo,
2530 lookup_table,
2531 );
2532 let (module_rhs, lo_rhs) = CodeGenResultComments::decode_bytepos(
2533 *modules_header_width,
2534 sp_rhs.lo,
2535 lookup_table,
2536 );
2537 if module_lhs != module_rhs {
2538 return None;
2539 }
2540 source_maps[module_lhs].merge_spans(
2541 Span {
2542 lo: lo_lhs,
2543 hi: CodeGenResultComments::decode_bytepos(
2544 *modules_header_width,
2545 sp_lhs.hi,
2546 lookup_table,
2547 )
2548 .1,
2549 },
2550 Span {
2551 lo: lo_rhs,
2552 hi: CodeGenResultComments::decode_bytepos(
2553 *modules_header_width,
2554 sp_rhs.hi,
2555 lookup_table,
2556 )
2557 .1,
2558 },
2559 )
2560 }
2561 }
2562 }
2563 fn call_span_if_macro(&self, sp: Span) -> Span {
2564 match self {
2565 CodeGenResultSourceMap::None => {
2566 panic!("CodeGenResultSourceMap::None cannot call_span_if_macro")
2567 }
2568 CodeGenResultSourceMap::Single { source_map } => source_map.call_span_if_macro(sp),
2569 CodeGenResultSourceMap::ScopeHoisting {
2570 modules_header_width,
2571 lookup_table,
2572 source_maps,
2573 } => {
2574 let (module, lo) = CodeGenResultComments::decode_bytepos(
2575 *modules_header_width,
2576 sp.lo,
2577 lookup_table,
2578 );
2579 source_maps[module].call_span_if_macro(Span {
2580 lo,
2581 hi: CodeGenResultComments::decode_bytepos(
2582 *modules_header_width,
2583 sp.hi,
2584 lookup_table,
2585 )
2586 .1,
2587 })
2588 }
2589 }
2590 }
2591 fn doctest_offset_line(&self, _line: usize) -> usize {
2592 panic!("doctest_offset_line is not implemented for CodeGenResultSourceMap");
2593 }
2594 fn span_to_snippet(&self, sp: Span) -> Result<String, Box<SpanSnippetError>> {
2595 match self {
2596 CodeGenResultSourceMap::None => Err(Box::new(SpanSnippetError::SourceNotAvailable {
2597 filename: FileName::Anon,
2598 })),
2599 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_snippet(sp),
2600 CodeGenResultSourceMap::ScopeHoisting {
2601 modules_header_width,
2602 lookup_table,
2603 source_maps,
2604 } => {
2605 let (module, lo) = CodeGenResultComments::decode_bytepos(
2606 *modules_header_width,
2607 sp.lo,
2608 lookup_table,
2609 );
2610 source_maps[module].span_to_snippet(Span {
2611 lo,
2612 hi: CodeGenResultComments::decode_bytepos(
2613 *modules_header_width,
2614 sp.hi,
2615 lookup_table,
2616 )
2617 .1,
2618 })
2619 }
2620 }
2621 }
2622}
2623impl SourceMapperExt for CodeGenResultSourceMap {
2624 fn get_code_map(&self) -> &dyn SourceMapper {
2625 self
2626 }
2627}
2628
2629#[derive(Debug)]
2630enum CodeGenResultOriginalSourceMap {
2631 Single(Option<ResolvedVc<Box<dyn GenerateSourceMap>>>),
2632 ScopeHoisting(SmallVec<[ResolvedVc<Box<dyn GenerateSourceMap>>; 1]>),
2633}
2634
2635impl CodeGenResultOriginalSourceMap {
2636 fn iter(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn GenerateSourceMap>>> {
2637 match self {
2638 CodeGenResultOriginalSourceMap::Single(map) => Either::Left(map.iter().copied()),
2639 CodeGenResultOriginalSourceMap::ScopeHoisting(maps) => {
2640 Either::Right(maps.iter().copied())
2641 }
2642 }
2643 }
2644}
2645
2646struct ModulePosition(u32, u32);
2648
2649enum CodeGenResultComments {
2650 Single {
2651 comments: Either<ImmutableComments, Arc<ImmutableComments>>,
2652 extra_comments: SwcComments,
2653 },
2654 ScopeHoisting {
2655 modules_header_width: u32,
2658 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2659 comments: Vec<CodeGenResultComments>,
2660 },
2661 Empty,
2662}
2663
2664unsafe impl Send for CodeGenResultComments {}
2665unsafe impl Sync for CodeGenResultComments {}
2666
2667impl CodeGenResultComments {
2668 const CONTINUATION_BIT: u32 = 1 << 31;
2669 const SIGN_EXTENSION_BIT: u32 = 1 << 30;
2670
2671 #[inline]
2672 fn encode_bytepos_impl(
2673 modules_header_width: u32,
2674 module: u32,
2675 pos: BytePos,
2676 push_into_lookup: &mut impl FnMut(u32, u32) -> Result<u32>,
2677 ) -> Result<BytePos> {
2678 if pos.is_dummy() {
2679 return Ok(pos);
2681 }
2682
2683 let header_width = modules_header_width + 2;
2714 let pos_width = 32 - header_width;
2715
2716 let pos = pos.0;
2717
2718 let old_high_bits = pos >> pos_width;
2719 let high_bits_set = if (2u32.pow(header_width) - 1) == old_high_bits {
2720 true
2721 } else if old_high_bits == 0 {
2722 false
2723 } else {
2724 let ix = push_into_lookup(module, pos)?;
2728 assert_eq!(ix & CodeGenResultComments::CONTINUATION_BIT, 0);
2730
2731 return Ok(BytePos(ix | CodeGenResultComments::CONTINUATION_BIT));
2732 };
2733
2734 let pos = pos & !((2u32.pow(header_width) - 1) << pos_width);
2735 let encoded_high_bits = if high_bits_set {
2736 CodeGenResultComments::SIGN_EXTENSION_BIT
2737 } else {
2738 0
2739 };
2740 let encoded_module = module << pos_width;
2741
2742 Ok(BytePos(encoded_module | encoded_high_bits | pos))
2743 }
2744
2745 fn take(&mut self) -> Self {
2746 std::mem::replace(self, CodeGenResultComments::Empty)
2747 }
2748
2749 fn consumable(&self) -> CodeGenResultCommentsConsumable<'_> {
2750 match self {
2751 CodeGenResultComments::Single {
2752 comments,
2753 extra_comments,
2754 } => CodeGenResultCommentsConsumable::Single {
2755 comments: match comments {
2756 Either::Left(comments) => comments.consumable(),
2757 Either::Right(comments) => comments.consumable(),
2758 },
2759 extra_comments,
2760 },
2761 CodeGenResultComments::ScopeHoisting {
2762 modules_header_width,
2763 lookup_table,
2764 comments,
2765 } => CodeGenResultCommentsConsumable::ScopeHoisting {
2766 modules_header_width: *modules_header_width,
2767 lookup_table: lookup_table.clone(),
2768 comments: comments.iter().map(|c| c.consumable()).collect(),
2769 },
2770 CodeGenResultComments::Empty => CodeGenResultCommentsConsumable::Empty,
2771 }
2772 }
2773
2774 fn encode_bytepos(
2775 modules_header_width: u32,
2776 module: u32,
2777 pos: BytePos,
2778 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2779 ) -> Result<BytePos> {
2780 let mut push = |module: u32, pos_u32: u32| -> Result<u32> {
2781 let mut lookup_table = lookup_table
2782 .lock()
2783 .map_err(|_| anyhow!("Failed to grab lock on the index map for byte positions"))?;
2784 let ix = lookup_table.len() as u32;
2785 if ix >= 1 << 30 {
2786 return Err(anyhow!("Too many byte positions being stored"));
2787 }
2788 lookup_table.push(ModulePosition(module, pos_u32));
2789 Ok(ix)
2790 };
2791 Self::encode_bytepos_impl(modules_header_width, module, pos, &mut push)
2792 }
2793
2794 fn encode_bytepos_with_vec(
2795 modules_header_width: u32,
2796 module: u32,
2797 pos: BytePos,
2798 lookup_table: &mut Vec<ModulePosition>,
2799 ) -> Result<BytePos> {
2800 let mut push = |module: u32, pos_u32: u32| -> Result<u32> {
2801 let ix = lookup_table.len() as u32;
2802 if ix >= 1 << 30 {
2803 return Err(anyhow!("Too many byte positions being stored"));
2804 }
2805 lookup_table.push(ModulePosition(module, pos_u32));
2806 Ok(ix)
2807 };
2808 Self::encode_bytepos_impl(modules_header_width, module, pos, &mut push)
2809 }
2810
2811 fn decode_bytepos(
2812 modules_header_width: u32,
2813 pos: BytePos,
2814 lookup_table: &Mutex<Vec<ModulePosition>>,
2815 ) -> (usize, BytePos) {
2816 if pos.is_dummy() {
2817 panic!("Cannot decode dummy BytePos");
2819 }
2820
2821 let header_width = modules_header_width + 2;
2822 let pos_width = 32 - header_width;
2823
2824 if (CodeGenResultComments::CONTINUATION_BIT & pos.0)
2825 == CodeGenResultComments::CONTINUATION_BIT
2826 {
2827 let lookup_table = lookup_table
2828 .lock()
2829 .expect("Failed to grab lock on the index map for byte position");
2830 let ix = pos.0 & !CodeGenResultComments::CONTINUATION_BIT;
2831 let ModulePosition(module, pos) = lookup_table[ix as usize];
2832
2833 return (module as usize, BytePos(pos));
2834 }
2835
2836 let high_bits_set = pos.0 >> 30 & 1 == 1;
2837 let module = (pos.0 << 2) >> (pos_width + 2);
2838 let pos = pos.0 & !((2u32.pow(header_width) - 1) << pos_width);
2839 let pos = if high_bits_set {
2840 pos | ((2u32.pow(header_width) - 1) << pos_width)
2841 } else {
2842 pos
2843 };
2844 (module as usize, BytePos(pos))
2845 }
2846}
2847
2848enum CodeGenResultCommentsConsumable<'a> {
2849 Single {
2850 comments: CowComments<'a>,
2851 extra_comments: &'a SwcComments,
2852 },
2853 ScopeHoisting {
2854 modules_header_width: u32,
2855 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2856 comments: Vec<CodeGenResultCommentsConsumable<'a>>,
2857 },
2858 Empty,
2859}
2860fn encode_module_into_comment_span(
2864 modules_header_width: u32,
2865 module: usize,
2866 mut comment: Comment,
2867 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2868) -> Comment {
2869 comment.span.lo = CodeGenResultComments::encode_bytepos(
2870 modules_header_width,
2871 module as u32,
2872 comment.span.lo,
2873 lookup_table.clone(),
2874 )
2875 .unwrap();
2876 comment.span.hi = CodeGenResultComments::encode_bytepos(
2877 modules_header_width,
2878 module as u32,
2879 comment.span.hi,
2880 lookup_table,
2881 )
2882 .unwrap();
2883 comment
2884}
2885
2886impl Comments for CodeGenResultCommentsConsumable<'_> {
2887 fn add_leading(&self, _pos: BytePos, _cmt: Comment) {
2888 unimplemented!("add_leading")
2889 }
2890
2891 fn add_leading_comments(&self, _pos: BytePos, _comments: Vec<Comment>) {
2892 unimplemented!("add_leading_comments")
2893 }
2894
2895 fn has_leading(&self, pos: BytePos) -> bool {
2896 if pos.is_dummy() {
2897 return false;
2898 }
2899 match self {
2900 Self::Single {
2901 comments,
2902 extra_comments,
2903 } => comments.has_leading(pos) || extra_comments.has_leading(pos),
2904 Self::ScopeHoisting {
2905 modules_header_width,
2906 lookup_table,
2907 comments,
2908 } => {
2909 let (module, pos) =
2910 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2911 comments[module].has_leading(pos)
2912 }
2913 Self::Empty => false,
2914 }
2915 }
2916
2917 fn move_leading(&self, _from: BytePos, _to: BytePos) {
2918 unimplemented!("move_leading")
2919 }
2920
2921 fn take_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
2922 if pos.is_dummy() {
2923 return None;
2924 }
2925 match self {
2926 Self::Single {
2927 comments,
2928 extra_comments,
2929 } => merge_option_vec(comments.take_leading(pos), extra_comments.take_leading(pos)),
2930 Self::ScopeHoisting {
2931 modules_header_width,
2932 lookup_table,
2933 comments,
2934 } => {
2935 let (module, pos) =
2936 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2937 comments[module].take_leading(pos).map(|comments| {
2938 comments
2939 .into_iter()
2940 .map(|c| {
2941 encode_module_into_comment_span(
2942 *modules_header_width,
2943 module,
2944 c,
2945 lookup_table.clone(),
2946 )
2947 })
2948 .collect()
2949 })
2950 }
2951 Self::Empty => None,
2952 }
2953 }
2954
2955 fn get_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
2956 if pos.is_dummy() {
2957 return None;
2958 }
2959 match self {
2960 Self::Single {
2961 comments,
2962 extra_comments,
2963 } => merge_option_vec(comments.get_leading(pos), extra_comments.get_leading(pos)),
2964 Self::ScopeHoisting {
2965 modules_header_width,
2966 lookup_table,
2967 comments,
2968 } => {
2969 let (module, pos) =
2970 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2971 comments[module].get_leading(pos).map(|comments| {
2972 comments
2973 .into_iter()
2974 .map(|c| {
2975 encode_module_into_comment_span(
2976 *modules_header_width,
2977 module,
2978 c,
2979 lookup_table.clone(),
2980 )
2981 })
2982 .collect()
2983 })
2984 }
2985 Self::Empty => None,
2986 }
2987 }
2988
2989 fn add_trailing(&self, _pos: BytePos, _cmt: Comment) {
2990 unimplemented!("add_trailing")
2991 }
2992
2993 fn add_trailing_comments(&self, _pos: BytePos, _comments: Vec<Comment>) {
2994 unimplemented!("add_trailing_comments")
2995 }
2996
2997 fn has_trailing(&self, pos: BytePos) -> bool {
2998 if pos.is_dummy() {
2999 return false;
3000 }
3001 match self {
3002 Self::Single {
3003 comments,
3004 extra_comments,
3005 } => comments.has_trailing(pos) || extra_comments.has_trailing(pos),
3006 Self::ScopeHoisting {
3007 modules_header_width,
3008 lookup_table,
3009 comments,
3010 } => {
3011 let (module, pos) =
3012 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
3013 comments[module].has_trailing(pos)
3014 }
3015 Self::Empty => false,
3016 }
3017 }
3018
3019 fn move_trailing(&self, _from: BytePos, _to: BytePos) {
3020 unimplemented!("move_trailing")
3021 }
3022
3023 fn take_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
3024 if pos.is_dummy() {
3025 return None;
3026 }
3027 match self {
3028 Self::Single {
3029 comments,
3030 extra_comments,
3031 } => merge_option_vec(
3032 comments.take_trailing(pos),
3033 extra_comments.take_trailing(pos),
3034 ),
3035 Self::ScopeHoisting {
3036 modules_header_width,
3037 lookup_table,
3038 comments,
3039 } => {
3040 let (module, pos) =
3041 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
3042 comments[module].take_trailing(pos).map(|comments| {
3043 comments
3044 .into_iter()
3045 .map(|c| {
3046 encode_module_into_comment_span(
3047 *modules_header_width,
3048 module,
3049 c,
3050 lookup_table.clone(),
3051 )
3052 })
3053 .collect()
3054 })
3055 }
3056 Self::Empty => None,
3057 }
3058 }
3059
3060 fn get_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
3061 if pos.is_dummy() {
3062 return None;
3063 }
3064 match self {
3065 Self::Single {
3066 comments,
3067 extra_comments,
3068 } => merge_option_vec(comments.get_leading(pos), extra_comments.get_leading(pos)),
3069 Self::ScopeHoisting {
3070 modules_header_width,
3071 lookup_table,
3072 comments,
3073 } => {
3074 let (module, pos) =
3075 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
3076 comments[module].get_leading(pos).map(|comments| {
3077 comments
3078 .into_iter()
3079 .map(|c| {
3080 encode_module_into_comment_span(
3081 *modules_header_width,
3082 module,
3083 c,
3084 lookup_table.clone(),
3085 )
3086 })
3087 .collect()
3088 })
3089 }
3090 Self::Empty => None,
3091 }
3092 }
3093
3094 fn add_pure_comment(&self, _pos: BytePos) {
3095 unimplemented!("add_pure_comment")
3096 }
3097}
3098
3099fn merge_option_vec<T>(a: Option<Vec<T>>, b: Option<Vec<T>>) -> Option<Vec<T>> {
3100 match (a, b) {
3101 (Some(a), Some(b)) => Some(a.into_iter().chain(b).collect()),
3102 (Some(a), None) => Some(a),
3103 (None, Some(b)) => Some(b),
3104 (None, None) => None,
3105 }
3106}
3107
3108#[cfg(test)]
3109mod tests {
3110 use super::*;
3111 fn bytepos_ensure_identical(modules_header_width: u32, pos: BytePos) {
3112 let module_count = 2u32.pow(modules_header_width);
3113 let lookup_table = Arc::new(Mutex::new(Vec::new()));
3114
3115 for module in [
3116 0,
3117 1,
3118 2,
3119 module_count / 2,
3120 module_count.wrapping_sub(5),
3121 module_count.wrapping_sub(1),
3122 ]
3123 .into_iter()
3124 .filter(|&m| m < module_count)
3125 {
3126 let encoded = CodeGenResultComments::encode_bytepos(
3127 modules_header_width,
3128 module,
3129 pos,
3130 lookup_table.clone(),
3131 )
3132 .unwrap();
3133 let (decoded_module, decoded_pos) =
3134 CodeGenResultComments::decode_bytepos(modules_header_width, encoded, &lookup_table);
3135 assert_eq!(
3136 decoded_module as u32, module,
3137 "Testing width {modules_header_width} and pos {pos:?}"
3138 );
3139 assert_eq!(
3140 decoded_pos, pos,
3141 "Testing width {modules_header_width} and pos {pos:?}"
3142 );
3143 }
3144 }
3145
3146 #[test]
3147 fn test_encode_decode_bytepos_format() {
3148 let table = Arc::new(Mutex::new(Vec::new()));
3149
3150 for (pos, module, modules_header_width, result) in [
3151 (
3152 0b00000000000000000000000000000101,
3153 0b1,
3154 1,
3155 0b00100000000000000000000000000101,
3156 ),
3157 (
3158 0b00000000000000000000000000000101,
3159 0b01,
3160 2,
3161 0b00010000000000000000000000000101,
3162 ),
3163 (
3164 0b11111111111111110000000000000101,
3165 0b0110,
3166 4,
3167 0b01011011111111110000000000000101,
3168 ),
3169 (
3170 BytePos::PLACEHOLDER.0,
3171 0b01111,
3172 5,
3173 0b01011111111111111111111111111101,
3174 ),
3175 (
3176 BytePos::PURE.0,
3177 0b01111,
3178 5,
3179 0b01011111111111111111111111111110,
3180 ),
3181 (
3182 BytePos::SYNTHESIZED.0,
3183 0b01111,
3184 5,
3185 0b01011111111111111111111111111111,
3186 ),
3187 (
3190 0b00000111111111110000000000000101,
3191 0b0001,
3192 4,
3193 0b10000000000000000000000000000000,
3194 ),
3195 (
3197 0b00000111111111110000000000111110,
3198 0b0001,
3199 4,
3200 0b10000000000000000000000000000001,
3201 ),
3202 (BytePos::DUMMY.0, 0b0001, 4, BytePos::DUMMY.0),
3204 ] {
3205 let encoded = CodeGenResultComments::encode_bytepos(
3206 modules_header_width,
3207 module,
3208 BytePos(pos),
3209 table.clone(),
3210 )
3211 .unwrap();
3212 assert_eq!(encoded.0, result);
3213
3214 if encoded.0 & CodeGenResultComments::CONTINUATION_BIT
3216 == CodeGenResultComments::CONTINUATION_BIT
3217 {
3218 let index = encoded.0 & !CodeGenResultComments::CONTINUATION_BIT;
3219 let ModulePosition(encoded_module, encoded_pos) =
3220 table.lock().unwrap()[index as usize];
3221 assert_eq!(encoded_module, module);
3222 assert_eq!(encoded_pos, pos);
3223 }
3224 }
3225 }
3226
3227 #[test]
3228 fn test_encode_decode_bytepos_lossless() {
3229 const DUMMY_RESERVE: u32 = u32::MAX - 2_u32.pow(16);
3231
3232 for modules_header_width in 1..=10 {
3233 for pos in [
3234 BytePos(1),
3236 BytePos(2),
3237 BytePos(100),
3238 BytePos(4_000_000),
3239 BytePos(600_000_000),
3240 BytePos(u32::MAX - 3), BytePos::PLACEHOLDER,
3242 BytePos::SYNTHESIZED,
3243 BytePos::PURE,
3244 BytePos(DUMMY_RESERVE),
3245 BytePos(DUMMY_RESERVE + 10),
3246 BytePos(DUMMY_RESERVE + 10000),
3247 ] {
3248 bytepos_ensure_identical(modules_header_width, pos);
3249 }
3250 }
3251 }
3252}