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