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 options(&self) -> Vc<EcmascriptOptions> {
783 *self.options
784 }
785}
786
787impl EcmascriptModuleAsset {
788 pub fn analyze(self: Vc<Self>) -> Vc<AnalyzeEcmascriptModuleResult> {
789 analyze_ecmascript_module(self, None)
790 }
791
792 pub async fn parse(&self) -> Result<Vc<ParseResult>> {
793 let options = self.options.await?;
794 let node_env = self
795 .compile_time_info
796 .await?
797 .defines
798 .read_process_env(rcstr!("NODE_ENV"))
799 .owned()
800 .await?
801 .unwrap_or_else(|| rcstr!("development"));
802 Ok(parse(
803 *self.source,
804 self.ty,
805 *self.transforms,
806 node_env,
807 options.analyze_mode == AnalyzeMode::Tracing,
808 options.inline_helpers,
809 ))
810 }
811
812 #[tracing::instrument(level = "trace", skip_all)]
813 pub(crate) async fn determine_module_type(self: Vc<Self>) -> Result<ReadRef<ModuleTypeResult>> {
814 let this = self.await?;
815
816 match this.options.await?.specified_module_type {
817 SpecifiedModuleType::EcmaScript => {
818 return ModuleTypeResult::new(SpecifiedModuleType::EcmaScript).await;
819 }
820 SpecifiedModuleType::CommonJs => {
821 return ModuleTypeResult::new(SpecifiedModuleType::CommonJs).await;
822 }
823 SpecifiedModuleType::Automatic => {}
824 }
825
826 determine_module_type_for_directory(self.origin_path().await?.parent()).await
827 }
828}
829
830#[turbo_tasks::value_impl]
831impl Module for EcmascriptModuleAsset {
832 #[turbo_tasks::function]
833 async fn ident(&self) -> Result<Vc<AssetIdent>> {
834 let mut ident = self.source.ident().owned().await?;
835 if let Some(inner_assets) = self.inner_assets {
836 for (name, asset) in inner_assets.await?.iter() {
837 ident = ident.with_asset(name.clone(), asset.ident().to_resolved().await?);
838 }
839 }
840 Ok(ident
841 .with_modifier(rcstr!("ecmascript"))
842 .with_layer(self.asset_context.into_trait_ref().await?.layer())
843 .into_vc())
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().await?.path.clone(),
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 async fn origin_path(&self) -> Result<Vc<FileSystemPath>> {
971 Ok(self.source.ident().await?.path.clone().cell())
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.await?.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 = &*turbofmt!(
2004 "Could not parse module '{}', file not found",
2005 ident.await?.path
2006 )
2007 .await?;
2008 let body = vec![
2009 quote!(
2010 "var e = new Error($msg);" as Stmt,
2011 msg: Expr = Expr::Lit(msg.into()),
2012 ),
2013 quote!("e.code = 'MODULE_UNPARSABLE';" as Stmt),
2014 quote!("throw e;" as Stmt),
2015 ];
2016 CodeGenResult {
2017 program: Program::Script(Script {
2018 span: DUMMY_SP,
2019 body,
2020 shebang: None,
2021 }),
2022 source_map: CodeGenResultSourceMap::None,
2023 comments: CodeGenResultComments::Empty,
2024 is_esm: false,
2025 strict: false,
2026 original_source_map: CodeGenResultOriginalSourceMap::Single(None),
2027 minify: MinifyType::NoMinify,
2028 scope_hoisting_syntax_contexts: None,
2029 }
2030 }
2031 })
2032 },
2033 )
2034 .instrument(tracing::trace_span!(
2035 "process parse result",
2036 ident = display(ident.to_string().await?),
2037 ))
2038 .await
2039}
2040
2041async fn with_consumed_parse_result<T>(
2043 parsed: Option<ResolvedVc<ParseResult>>,
2044 success: impl AsyncFnOnce(
2045 Program,
2046 &Arc<SourceMap>,
2047 &Arc<Globals>,
2048 Either<EvalContext, &'_ EvalContext>,
2049 Either<ImmutableComments, Arc<ImmutableComments>>,
2050 ) -> Result<T>,
2051 error: impl AsyncFnOnce(&ParseResult) -> Result<T>,
2052) -> Result<T> {
2053 let Some(parsed) = parsed else {
2054 let globals = Globals::new();
2055 let eval_context = GLOBALS.set(&globals, || EvalContext {
2056 unresolved_mark: Mark::new(),
2057 top_level_mark: Mark::new(),
2058 imports: Default::default(),
2059 force_free_values: Default::default(),
2060 });
2061 return success(
2062 Program::Module(swc_core::ecma::ast::Module::dummy()),
2063 &Default::default(),
2064 &Default::default(),
2065 Either::Left(eval_context),
2066 Either::Left(Default::default()),
2067 )
2068 .await;
2069 };
2070
2071 let parsed = parsed.final_read_hint().await?;
2072 match &*parsed {
2073 ParseResult::Ok { .. } => {
2074 let mut parsed = ReadRef::try_unwrap(parsed);
2075 let (program, source_map, globals, eval_context, comments) = match &mut parsed {
2076 Ok(ParseResult::Ok {
2077 program,
2078 source_map,
2079 globals,
2080 eval_context,
2081 comments,
2082 ..
2083 }) => (
2084 program.take(),
2085 &*source_map,
2086 &*globals,
2087 Either::Left(std::mem::replace(
2088 eval_context,
2089 EvalContext {
2090 unresolved_mark: eval_context.unresolved_mark,
2091 top_level_mark: eval_context.top_level_mark,
2092 imports: Default::default(),
2093 force_free_values: Default::default(),
2094 },
2095 )),
2096 match Arc::try_unwrap(take(comments)) {
2097 Ok(comments) => Either::Left(comments),
2098 Err(comments) => Either::Right(comments),
2099 },
2100 ),
2101 Err(parsed) => {
2102 let ParseResult::Ok {
2103 program,
2104 source_map,
2105 globals,
2106 eval_context,
2107 comments,
2108 ..
2109 } = &**parsed
2110 else {
2111 unreachable!();
2112 };
2113 (
2114 program.clone(),
2115 source_map,
2116 globals,
2117 Either::Right(eval_context),
2118 Either::Right(comments.clone()),
2119 )
2120 }
2121 _ => unreachable!(),
2122 };
2123
2124 success(program, source_map, globals, eval_context, comments).await
2125 }
2126 _ => error(&parsed).await,
2127 }
2128}
2129
2130async fn emit_content(
2131 content: CodeGenResult,
2132 additional_ids: SmallVec<[ModuleId; 1]>,
2133) -> Result<Vc<EcmascriptModuleContent>> {
2134 let CodeGenResult {
2135 program,
2136 source_map,
2137 comments,
2138 is_esm,
2139 strict,
2140 original_source_map,
2141 minify,
2142 scope_hoisting_syntax_contexts: _,
2143 } = content;
2144
2145 let generate_source_map = source_map.is_some();
2146
2147 let source_map_names = if generate_source_map {
2149 let mut collector = IdentCollector::default();
2150 program.visit_with(&mut collector);
2151 collector.into_map()
2152 } else {
2153 Default::default()
2154 };
2155
2156 let mut bytes: Vec<u8> = vec![];
2157 let mut mappings = vec![];
2161
2162 let source_map = Arc::new(source_map);
2163
2164 {
2165 let mut wr = JsWriter::new(
2166 Default::default(),
2168 "\n",
2169 &mut bytes,
2170 generate_source_map.then_some(&mut mappings),
2171 );
2172 if matches!(minify, MinifyType::Minify { .. }) {
2173 wr.set_indent_str("");
2174 }
2175
2176 let comments = comments.consumable();
2177
2178 let mut emitter = Emitter {
2179 cfg: swc_core::ecma::codegen::Config::default(),
2180 cm: source_map.clone(),
2181 comments: Some(&comments as &dyn Comments),
2182 wr,
2183 };
2184
2185 emitter.emit_program(&program)?;
2186 drop(program);
2188 }
2189
2190 let source_map = if generate_source_map {
2191 let original_source_maps = original_source_map
2192 .iter()
2193 .map(|map| map.generate_source_map())
2194 .try_join()
2195 .await?;
2196 let original_source_maps = original_source_maps
2197 .iter()
2198 .filter_map(|map| map.as_content())
2199 .map(|map| map.content())
2200 .collect::<Vec<_>>();
2201
2202 Some(generate_js_source_map(
2203 &*source_map,
2204 mappings,
2205 original_source_maps,
2206 matches!(
2207 original_source_map,
2208 CodeGenResultOriginalSourceMap::Single(_)
2209 ),
2210 true,
2211 source_map_names,
2212 )?)
2213 } else {
2214 None
2215 };
2216
2217 Ok(EcmascriptModuleContent {
2218 inner_code: bytes.into(),
2219 source_map,
2220 is_esm,
2221 strict,
2222 additional_ids,
2223 }
2224 .cell())
2225}
2226
2227#[instrument(level = Level::TRACE, skip_all, name = "apply code generation")]
2228fn process_content_with_code_gens(
2229 program: &mut Program,
2230 globals: &Globals,
2231 code_gens: &mut Vec<CodeGeneration>,
2232) {
2233 let mut visitors = Vec::new();
2234 let mut root_visitors = Vec::new();
2235 let mut early_hoisted_stmts = FxIndexMap::default();
2236 let mut hoisted_stmts = FxIndexMap::default();
2237 let mut early_late_stmts = FxIndexMap::default();
2238 let mut late_stmts = FxIndexMap::default();
2239 for code_gen in code_gens {
2240 for CodeGenerationHoistedStmt { key, stmt } in code_gen.hoisted_stmts.drain(..) {
2241 hoisted_stmts.entry(key).or_insert(stmt);
2242 }
2243 for CodeGenerationHoistedStmt { key, stmt } in code_gen.early_hoisted_stmts.drain(..) {
2244 early_hoisted_stmts.insert(key.clone(), stmt);
2245 }
2246 for CodeGenerationHoistedStmt { key, stmt } in code_gen.late_stmts.drain(..) {
2247 late_stmts.insert(key.clone(), stmt);
2248 }
2249 for CodeGenerationHoistedStmt { key, stmt } in code_gen.early_late_stmts.drain(..) {
2250 early_late_stmts.insert(key.clone(), stmt);
2251 }
2252 for (path, visitor) in &code_gen.visitors {
2253 if path.is_empty() {
2254 root_visitors.push(&**visitor);
2255 } else {
2256 visitors.push((path, &**visitor));
2257 }
2258 }
2259 }
2260
2261 GLOBALS.set(globals, || {
2262 if !visitors.is_empty() {
2263 program.visit_mut_with_ast_path(
2264 &mut ApplyVisitors::new(visitors),
2265 &mut Default::default(),
2266 );
2267 }
2268 for pass in root_visitors {
2269 program.modify(pass);
2270 }
2271 });
2272
2273 match program {
2274 Program::Module(ast::Module { body, .. }) => {
2275 body.splice(
2276 0..0,
2277 early_hoisted_stmts
2278 .into_values()
2279 .chain(hoisted_stmts.into_values())
2280 .map(ModuleItem::Stmt),
2281 );
2282 body.extend(
2283 early_late_stmts
2284 .into_values()
2285 .chain(late_stmts.into_values())
2286 .map(ModuleItem::Stmt),
2287 );
2288 }
2289 Program::Script(Script { body, .. }) => {
2290 body.splice(
2291 0..0,
2292 early_hoisted_stmts
2293 .into_values()
2294 .chain(hoisted_stmts.into_values()),
2295 );
2296 body.extend(
2297 early_late_stmts
2298 .into_values()
2299 .chain(late_stmts.into_values()),
2300 );
2301 }
2302 };
2303}
2304
2305fn hygiene_rename_only(
2312 top_level_mark: Option<Mark>,
2313 is_import_mark: Mark,
2314 preserved_exports: &FxHashSet<Id>,
2315) -> impl VisitMut {
2316 struct HygieneRenamer<'a> {
2317 preserved_exports: &'a FxHashSet<Id>,
2318 is_import_mark: Mark,
2319 }
2320 impl swc_core::ecma::transforms::base::rename::Renamer for HygieneRenamer<'_> {
2322 type Target = Id;
2323
2324 const MANGLE: bool = false;
2325 const RESET_N: bool = true;
2326
2327 fn new_name_for(&self, orig: &Id, n: &mut usize) -> Atom {
2328 let res = if *n == 0 {
2329 orig.0.clone()
2330 } else {
2331 format!("{}{}", orig.0, n).into()
2332 };
2333 *n += 1;
2334 res
2335 }
2336
2337 fn preserve_name(&self, orig: &Id) -> bool {
2338 self.preserved_exports.contains(orig) || orig.1.has_mark(self.is_import_mark)
2339 }
2340 }
2341 swc_core::ecma::transforms::base::rename::renamer_keep_contexts(
2342 swc_core::ecma::transforms::base::hygiene::Config {
2343 top_level_mark: top_level_mark.unwrap_or_default(),
2344 ..Default::default()
2345 },
2346 HygieneRenamer {
2347 preserved_exports,
2348 is_import_mark,
2349 },
2350 )
2351}
2352
2353#[derive(Default)]
2354enum CodeGenResultSourceMap {
2355 #[default]
2356 None,
2358 Single {
2359 source_map: Arc<SourceMap>,
2360 },
2361 ScopeHoisting {
2362 modules_header_width: u32,
2365 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2366 source_maps: Vec<CodeGenResultSourceMap>,
2367 },
2368}
2369
2370impl CodeGenResultSourceMap {
2371 fn is_some(&self) -> bool {
2372 match self {
2373 CodeGenResultSourceMap::None => false,
2374 CodeGenResultSourceMap::Single { .. }
2375 | CodeGenResultSourceMap::ScopeHoisting { .. } => true,
2376 }
2377 }
2378}
2379
2380impl Debug for CodeGenResultSourceMap {
2381 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2382 match self {
2383 CodeGenResultSourceMap::None => write!(f, "CodeGenResultSourceMap::None"),
2384 CodeGenResultSourceMap::Single { source_map } => {
2385 write!(
2386 f,
2387 "CodeGenResultSourceMap::Single {{ source_map: {:?} }}",
2388 source_map.files().clone()
2389 )
2390 }
2391 CodeGenResultSourceMap::ScopeHoisting {
2392 modules_header_width,
2393 source_maps,
2394 ..
2395 } => write!(
2396 f,
2397 "CodeGenResultSourceMap::ScopeHoisting {{ modules_header_width: \
2398 {modules_header_width}, source_maps: {source_maps:?} }}",
2399 ),
2400 }
2401 }
2402}
2403
2404impl Files for CodeGenResultSourceMap {
2405 fn try_lookup_source_file(
2406 &self,
2407 pos: BytePos,
2408 ) -> Result<Option<Arc<SourceFile>>, SourceMapLookupError> {
2409 match self {
2410 CodeGenResultSourceMap::None => Ok(None),
2411 CodeGenResultSourceMap::Single { source_map } => source_map.try_lookup_source_file(pos),
2412 CodeGenResultSourceMap::ScopeHoisting {
2413 modules_header_width,
2414 lookup_table,
2415 source_maps,
2416 } => {
2417 let (module, pos) =
2418 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2419 source_maps[module].try_lookup_source_file(pos)
2420 }
2421 }
2422 }
2423
2424 fn is_in_file(&self, f: &Arc<SourceFile>, raw_pos: BytePos) -> bool {
2425 match self {
2426 CodeGenResultSourceMap::None => false,
2427 CodeGenResultSourceMap::Single { .. } => f.start_pos <= raw_pos && raw_pos < f.end_pos,
2428 CodeGenResultSourceMap::ScopeHoisting { .. } => {
2429 false
2435 }
2436 }
2437 }
2438
2439 fn map_raw_pos(&self, pos: BytePos) -> BytePos {
2440 match self {
2441 CodeGenResultSourceMap::None => BytePos::DUMMY,
2442 CodeGenResultSourceMap::Single { .. } => pos,
2443 CodeGenResultSourceMap::ScopeHoisting {
2444 modules_header_width,
2445 lookup_table,
2446 ..
2447 } => CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table).1,
2448 }
2449 }
2450}
2451
2452impl SourceMapper for CodeGenResultSourceMap {
2453 fn lookup_char_pos(&self, pos: BytePos) -> Loc {
2454 match self {
2455 CodeGenResultSourceMap::None => {
2456 panic!("CodeGenResultSourceMap::None cannot lookup_char_pos")
2457 }
2458 CodeGenResultSourceMap::Single { source_map } => source_map.lookup_char_pos(pos),
2459 CodeGenResultSourceMap::ScopeHoisting {
2460 modules_header_width,
2461 lookup_table,
2462 source_maps,
2463 } => {
2464 let (module, pos) =
2465 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2466 source_maps[module].lookup_char_pos(pos)
2467 }
2468 }
2469 }
2470 fn span_to_lines(&self, sp: Span) -> FileLinesResult {
2471 match self {
2472 CodeGenResultSourceMap::None => {
2473 panic!("CodeGenResultSourceMap::None cannot span_to_lines")
2474 }
2475 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_lines(sp),
2476 CodeGenResultSourceMap::ScopeHoisting {
2477 modules_header_width,
2478 lookup_table,
2479 source_maps,
2480 } => {
2481 let (module, lo) = CodeGenResultComments::decode_bytepos(
2482 *modules_header_width,
2483 sp.lo,
2484 lookup_table,
2485 );
2486 source_maps[module].span_to_lines(Span {
2487 lo,
2488 hi: CodeGenResultComments::decode_bytepos(
2489 *modules_header_width,
2490 sp.hi,
2491 lookup_table,
2492 )
2493 .1,
2494 })
2495 }
2496 }
2497 }
2498 fn span_to_string(&self, sp: Span) -> String {
2499 match self {
2500 CodeGenResultSourceMap::None => {
2501 panic!("CodeGenResultSourceMap::None cannot span_to_string")
2502 }
2503 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_string(sp),
2504 CodeGenResultSourceMap::ScopeHoisting {
2505 modules_header_width,
2506 lookup_table,
2507 source_maps,
2508 } => {
2509 let (module, lo) = CodeGenResultComments::decode_bytepos(
2510 *modules_header_width,
2511 sp.lo,
2512 lookup_table,
2513 );
2514 source_maps[module].span_to_string(Span {
2515 lo,
2516 hi: CodeGenResultComments::decode_bytepos(
2517 *modules_header_width,
2518 sp.hi,
2519 lookup_table,
2520 )
2521 .1,
2522 })
2523 }
2524 }
2525 }
2526 fn span_to_filename(&self, sp: Span) -> Arc<FileName> {
2527 match self {
2528 CodeGenResultSourceMap::None => {
2529 panic!("CodeGenResultSourceMap::None cannot span_to_filename")
2530 }
2531 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_filename(sp),
2532 CodeGenResultSourceMap::ScopeHoisting {
2533 modules_header_width,
2534 lookup_table,
2535 source_maps,
2536 } => {
2537 let (module, lo) = CodeGenResultComments::decode_bytepos(
2538 *modules_header_width,
2539 sp.lo,
2540 lookup_table,
2541 );
2542 source_maps[module].span_to_filename(Span {
2543 lo,
2544 hi: CodeGenResultComments::decode_bytepos(
2545 *modules_header_width,
2546 sp.hi,
2547 lookup_table,
2548 )
2549 .1,
2550 })
2551 }
2552 }
2553 }
2554 fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> {
2555 match self {
2556 CodeGenResultSourceMap::None => {
2557 panic!("CodeGenResultSourceMap::None cannot merge_spans")
2558 }
2559 CodeGenResultSourceMap::Single { source_map } => source_map.merge_spans(sp_lhs, sp_rhs),
2560 CodeGenResultSourceMap::ScopeHoisting {
2561 modules_header_width,
2562 lookup_table,
2563 source_maps,
2564 } => {
2565 let (module_lhs, lo_lhs) = CodeGenResultComments::decode_bytepos(
2566 *modules_header_width,
2567 sp_lhs.lo,
2568 lookup_table,
2569 );
2570 let (module_rhs, lo_rhs) = CodeGenResultComments::decode_bytepos(
2571 *modules_header_width,
2572 sp_rhs.lo,
2573 lookup_table,
2574 );
2575 if module_lhs != module_rhs {
2576 return None;
2577 }
2578 source_maps[module_lhs].merge_spans(
2579 Span {
2580 lo: lo_lhs,
2581 hi: CodeGenResultComments::decode_bytepos(
2582 *modules_header_width,
2583 sp_lhs.hi,
2584 lookup_table,
2585 )
2586 .1,
2587 },
2588 Span {
2589 lo: lo_rhs,
2590 hi: CodeGenResultComments::decode_bytepos(
2591 *modules_header_width,
2592 sp_rhs.hi,
2593 lookup_table,
2594 )
2595 .1,
2596 },
2597 )
2598 }
2599 }
2600 }
2601 fn call_span_if_macro(&self, sp: Span) -> Span {
2602 match self {
2603 CodeGenResultSourceMap::None => {
2604 panic!("CodeGenResultSourceMap::None cannot call_span_if_macro")
2605 }
2606 CodeGenResultSourceMap::Single { source_map } => source_map.call_span_if_macro(sp),
2607 CodeGenResultSourceMap::ScopeHoisting {
2608 modules_header_width,
2609 lookup_table,
2610 source_maps,
2611 } => {
2612 let (module, lo) = CodeGenResultComments::decode_bytepos(
2613 *modules_header_width,
2614 sp.lo,
2615 lookup_table,
2616 );
2617 source_maps[module].call_span_if_macro(Span {
2618 lo,
2619 hi: CodeGenResultComments::decode_bytepos(
2620 *modules_header_width,
2621 sp.hi,
2622 lookup_table,
2623 )
2624 .1,
2625 })
2626 }
2627 }
2628 }
2629 fn doctest_offset_line(&self, _line: usize) -> usize {
2630 panic!("doctest_offset_line is not implemented for CodeGenResultSourceMap");
2631 }
2632 fn span_to_snippet(&self, sp: Span) -> Result<String, Box<SpanSnippetError>> {
2633 match self {
2634 CodeGenResultSourceMap::None => Err(Box::new(SpanSnippetError::SourceNotAvailable {
2635 filename: FileName::Anon,
2636 })),
2637 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_snippet(sp),
2638 CodeGenResultSourceMap::ScopeHoisting {
2639 modules_header_width,
2640 lookup_table,
2641 source_maps,
2642 } => {
2643 let (module, lo) = CodeGenResultComments::decode_bytepos(
2644 *modules_header_width,
2645 sp.lo,
2646 lookup_table,
2647 );
2648 source_maps[module].span_to_snippet(Span {
2649 lo,
2650 hi: CodeGenResultComments::decode_bytepos(
2651 *modules_header_width,
2652 sp.hi,
2653 lookup_table,
2654 )
2655 .1,
2656 })
2657 }
2658 }
2659 }
2660 fn map_raw_pos(&self, pos: BytePos) -> BytePos {
2661 match self {
2662 CodeGenResultSourceMap::None => BytePos::DUMMY,
2663 CodeGenResultSourceMap::Single { .. } => pos,
2664 CodeGenResultSourceMap::ScopeHoisting {
2665 modules_header_width,
2666 lookup_table,
2667 ..
2668 } => CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table).1,
2669 }
2670 }
2671}
2672impl SourceMapperExt for CodeGenResultSourceMap {
2673 fn get_code_map(&self) -> &dyn SourceMapper {
2674 self
2675 }
2676}
2677
2678#[derive(Debug)]
2679enum CodeGenResultOriginalSourceMap {
2680 Single(Option<ResolvedVc<Box<dyn GenerateSourceMap>>>),
2681 ScopeHoisting(SmallVec<[ResolvedVc<Box<dyn GenerateSourceMap>>; 1]>),
2682}
2683
2684impl CodeGenResultOriginalSourceMap {
2685 fn iter(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn GenerateSourceMap>>> {
2686 match self {
2687 CodeGenResultOriginalSourceMap::Single(map) => Either::Left(map.iter().copied()),
2688 CodeGenResultOriginalSourceMap::ScopeHoisting(maps) => {
2689 Either::Right(maps.iter().copied())
2690 }
2691 }
2692 }
2693}
2694
2695struct ModulePosition(u32, u32);
2697
2698enum CodeGenResultComments {
2699 Single {
2700 comments: Either<ImmutableComments, Arc<ImmutableComments>>,
2701 extra_comments: SwcComments,
2702 },
2703 ScopeHoisting {
2704 modules_header_width: u32,
2707 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2708 comments: Vec<CodeGenResultComments>,
2709 },
2710 Empty,
2711}
2712
2713unsafe impl Send for CodeGenResultComments {}
2714unsafe impl Sync for CodeGenResultComments {}
2715
2716impl CodeGenResultComments {
2717 const CONTINUATION_BIT: u32 = 1 << 31;
2718 const SIGN_EXTENSION_BIT: u32 = 1 << 30;
2719
2720 #[inline]
2721 fn encode_bytepos_impl(
2722 modules_header_width: u32,
2723 module: u32,
2724 pos: BytePos,
2725 push_into_lookup: &mut impl FnMut(u32, u32) -> Result<u32>,
2726 ) -> Result<BytePos> {
2727 if pos.is_dummy() {
2728 return Ok(pos);
2730 }
2731
2732 let header_width = modules_header_width + 2;
2763 let pos_width = 32 - header_width;
2764
2765 let pos = pos.0;
2766
2767 let old_high_bits = pos >> pos_width;
2768 let high_bits_set = if (2u32.pow(header_width) - 1) == old_high_bits {
2769 true
2770 } else if old_high_bits == 0 {
2771 false
2772 } else {
2773 let ix = push_into_lookup(module, pos)?;
2777 assert_eq!(ix & CodeGenResultComments::CONTINUATION_BIT, 0);
2779
2780 return Ok(BytePos(ix | CodeGenResultComments::CONTINUATION_BIT));
2781 };
2782
2783 let pos = pos & !((2u32.pow(header_width) - 1) << pos_width);
2784 let encoded_high_bits = if high_bits_set {
2785 CodeGenResultComments::SIGN_EXTENSION_BIT
2786 } else {
2787 0
2788 };
2789 let encoded_module = module << pos_width;
2790
2791 Ok(BytePos(encoded_module | encoded_high_bits | pos))
2792 }
2793
2794 fn take(&mut self) -> Self {
2795 std::mem::replace(self, CodeGenResultComments::Empty)
2796 }
2797
2798 fn consumable(&self) -> CodeGenResultCommentsConsumable<'_> {
2799 match self {
2800 CodeGenResultComments::Single {
2801 comments,
2802 extra_comments,
2803 } => CodeGenResultCommentsConsumable::Single {
2804 comments: match comments {
2805 Either::Left(comments) => comments.consumable(),
2806 Either::Right(comments) => comments.consumable(),
2807 },
2808 extra_comments,
2809 },
2810 CodeGenResultComments::ScopeHoisting {
2811 modules_header_width,
2812 lookup_table,
2813 comments,
2814 } => CodeGenResultCommentsConsumable::ScopeHoisting {
2815 modules_header_width: *modules_header_width,
2816 lookup_table: lookup_table.clone(),
2817 comments: comments.iter().map(|c| c.consumable()).collect(),
2818 },
2819 CodeGenResultComments::Empty => CodeGenResultCommentsConsumable::Empty,
2820 }
2821 }
2822
2823 fn encode_bytepos(
2824 modules_header_width: u32,
2825 module: u32,
2826 pos: BytePos,
2827 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2828 ) -> Result<BytePos> {
2829 let mut push = |module: u32, pos_u32: u32| -> Result<u32> {
2830 let mut lookup_table = lookup_table
2831 .lock()
2832 .map_err(|_| anyhow!("Failed to grab lock on the index map for byte positions"))?;
2833 let ix = lookup_table.len() as u32;
2834 if ix >= 1 << 30 {
2835 bail!("Too many byte positions being stored");
2836 }
2837 lookup_table.push(ModulePosition(module, pos_u32));
2838 Ok(ix)
2839 };
2840 Self::encode_bytepos_impl(modules_header_width, module, pos, &mut push)
2841 }
2842
2843 fn encode_bytepos_with_vec(
2844 modules_header_width: u32,
2845 module: u32,
2846 pos: BytePos,
2847 lookup_table: &mut Vec<ModulePosition>,
2848 ) -> Result<BytePos> {
2849 let mut push = |module: u32, pos_u32: u32| -> Result<u32> {
2850 let ix = lookup_table.len() as u32;
2851 if ix >= 1 << 30 {
2852 bail!("Too many byte positions being stored");
2853 }
2854 lookup_table.push(ModulePosition(module, pos_u32));
2855 Ok(ix)
2856 };
2857 Self::encode_bytepos_impl(modules_header_width, module, pos, &mut push)
2858 }
2859
2860 fn decode_bytepos(
2861 modules_header_width: u32,
2862 pos: BytePos,
2863 lookup_table: &Mutex<Vec<ModulePosition>>,
2864 ) -> (usize, BytePos) {
2865 if pos.is_dummy() {
2866 panic!("Cannot decode dummy BytePos");
2868 }
2869
2870 let header_width = modules_header_width + 2;
2871 let pos_width = 32 - header_width;
2872
2873 if (CodeGenResultComments::CONTINUATION_BIT & pos.0)
2874 == CodeGenResultComments::CONTINUATION_BIT
2875 {
2876 let lookup_table = lookup_table
2877 .lock()
2878 .expect("Failed to grab lock on the index map for byte position");
2879 let ix = pos.0 & !CodeGenResultComments::CONTINUATION_BIT;
2880 let ModulePosition(module, pos) = lookup_table[ix as usize];
2881
2882 return (module as usize, BytePos(pos));
2883 }
2884
2885 let high_bits_set = pos.0 >> 30 & 1 == 1;
2886 let module = (pos.0 << 2) >> (pos_width + 2);
2887 let pos = pos.0 & !((2u32.pow(header_width) - 1) << pos_width);
2888 let pos = if high_bits_set {
2889 pos | ((2u32.pow(header_width) - 1) << pos_width)
2890 } else {
2891 pos
2892 };
2893 (module as usize, BytePos(pos))
2894 }
2895}
2896
2897enum CodeGenResultCommentsConsumable<'a> {
2898 Single {
2899 comments: CowComments<'a>,
2900 extra_comments: &'a SwcComments,
2901 },
2902 ScopeHoisting {
2903 modules_header_width: u32,
2904 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2905 comments: Vec<CodeGenResultCommentsConsumable<'a>>,
2906 },
2907 Empty,
2908}
2909fn encode_module_into_comment_span(
2913 modules_header_width: u32,
2914 module: usize,
2915 mut comment: Comment,
2916 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2917) -> Comment {
2918 comment.span.lo = CodeGenResultComments::encode_bytepos(
2919 modules_header_width,
2920 module as u32,
2921 comment.span.lo,
2922 lookup_table.clone(),
2923 )
2924 .unwrap();
2925 comment.span.hi = CodeGenResultComments::encode_bytepos(
2926 modules_header_width,
2927 module as u32,
2928 comment.span.hi,
2929 lookup_table,
2930 )
2931 .unwrap();
2932 comment
2933}
2934
2935impl Comments for CodeGenResultCommentsConsumable<'_> {
2936 fn add_leading(&self, _pos: BytePos, _cmt: Comment) {
2937 unimplemented!("add_leading")
2938 }
2939
2940 fn add_leading_comments(&self, _pos: BytePos, _comments: Vec<Comment>) {
2941 unimplemented!("add_leading_comments")
2942 }
2943
2944 fn has_leading(&self, pos: BytePos) -> bool {
2945 if pos.is_dummy() {
2946 return false;
2947 }
2948 match self {
2949 Self::Single {
2950 comments,
2951 extra_comments,
2952 } => comments.has_leading(pos) || extra_comments.has_leading(pos),
2953 Self::ScopeHoisting {
2954 modules_header_width,
2955 lookup_table,
2956 comments,
2957 } => {
2958 let (module, pos) =
2959 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2960 comments[module].has_leading(pos)
2961 }
2962 Self::Empty => false,
2963 }
2964 }
2965
2966 fn move_leading(&self, _from: BytePos, _to: BytePos) {
2967 unimplemented!("move_leading")
2968 }
2969
2970 fn take_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
2971 if pos.is_dummy() {
2972 return None;
2973 }
2974 match self {
2975 Self::Single {
2976 comments,
2977 extra_comments,
2978 } => merge_option_vec(comments.take_leading(pos), extra_comments.take_leading(pos)),
2979 Self::ScopeHoisting {
2980 modules_header_width,
2981 lookup_table,
2982 comments,
2983 } => {
2984 let (module, pos) =
2985 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2986 comments[module].take_leading(pos).map(|comments| {
2987 comments
2988 .into_iter()
2989 .map(|c| {
2990 encode_module_into_comment_span(
2991 *modules_header_width,
2992 module,
2993 c,
2994 lookup_table.clone(),
2995 )
2996 })
2997 .collect()
2998 })
2999 }
3000 Self::Empty => None,
3001 }
3002 }
3003
3004 fn get_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
3005 if pos.is_dummy() {
3006 return None;
3007 }
3008 match self {
3009 Self::Single {
3010 comments,
3011 extra_comments,
3012 } => merge_option_vec(comments.get_leading(pos), extra_comments.get_leading(pos)),
3013 Self::ScopeHoisting {
3014 modules_header_width,
3015 lookup_table,
3016 comments,
3017 } => {
3018 let (module, pos) =
3019 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
3020 comments[module].get_leading(pos).map(|comments| {
3021 comments
3022 .into_iter()
3023 .map(|c| {
3024 encode_module_into_comment_span(
3025 *modules_header_width,
3026 module,
3027 c,
3028 lookup_table.clone(),
3029 )
3030 })
3031 .collect()
3032 })
3033 }
3034 Self::Empty => None,
3035 }
3036 }
3037
3038 fn add_trailing(&self, _pos: BytePos, _cmt: Comment) {
3039 unimplemented!("add_trailing")
3040 }
3041
3042 fn add_trailing_comments(&self, _pos: BytePos, _comments: Vec<Comment>) {
3043 unimplemented!("add_trailing_comments")
3044 }
3045
3046 fn has_trailing(&self, pos: BytePos) -> bool {
3047 if pos.is_dummy() {
3048 return false;
3049 }
3050 match self {
3051 Self::Single {
3052 comments,
3053 extra_comments,
3054 } => comments.has_trailing(pos) || extra_comments.has_trailing(pos),
3055 Self::ScopeHoisting {
3056 modules_header_width,
3057 lookup_table,
3058 comments,
3059 } => {
3060 let (module, pos) =
3061 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
3062 comments[module].has_trailing(pos)
3063 }
3064 Self::Empty => false,
3065 }
3066 }
3067
3068 fn move_trailing(&self, _from: BytePos, _to: BytePos) {
3069 unimplemented!("move_trailing")
3070 }
3071
3072 fn take_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
3073 if pos.is_dummy() {
3074 return None;
3075 }
3076 match self {
3077 Self::Single {
3078 comments,
3079 extra_comments,
3080 } => merge_option_vec(
3081 comments.take_trailing(pos),
3082 extra_comments.take_trailing(pos),
3083 ),
3084 Self::ScopeHoisting {
3085 modules_header_width,
3086 lookup_table,
3087 comments,
3088 } => {
3089 let (module, pos) =
3090 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
3091 comments[module].take_trailing(pos).map(|comments| {
3092 comments
3093 .into_iter()
3094 .map(|c| {
3095 encode_module_into_comment_span(
3096 *modules_header_width,
3097 module,
3098 c,
3099 lookup_table.clone(),
3100 )
3101 })
3102 .collect()
3103 })
3104 }
3105 Self::Empty => None,
3106 }
3107 }
3108
3109 fn get_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
3110 if pos.is_dummy() {
3111 return None;
3112 }
3113 match self {
3114 Self::Single {
3115 comments,
3116 extra_comments,
3117 } => merge_option_vec(comments.get_leading(pos), extra_comments.get_leading(pos)),
3118 Self::ScopeHoisting {
3119 modules_header_width,
3120 lookup_table,
3121 comments,
3122 } => {
3123 let (module, pos) =
3124 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
3125 comments[module].get_leading(pos).map(|comments| {
3126 comments
3127 .into_iter()
3128 .map(|c| {
3129 encode_module_into_comment_span(
3130 *modules_header_width,
3131 module,
3132 c,
3133 lookup_table.clone(),
3134 )
3135 })
3136 .collect()
3137 })
3138 }
3139 Self::Empty => None,
3140 }
3141 }
3142
3143 fn add_pure_comment(&self, _pos: BytePos) {
3144 unimplemented!("add_pure_comment")
3145 }
3146}
3147
3148fn merge_option_vec<T>(a: Option<Vec<T>>, b: Option<Vec<T>>) -> Option<Vec<T>> {
3149 match (a, b) {
3150 (Some(a), Some(b)) => Some(a.into_iter().chain(b).collect()),
3151 (Some(a), None) => Some(a),
3152 (None, Some(b)) => Some(b),
3153 (None, None) => None,
3154 }
3155}
3156
3157#[cfg(test)]
3158mod tests {
3159 use super::*;
3160 fn bytepos_ensure_identical(modules_header_width: u32, pos: BytePos) {
3161 let module_count = 2u32.pow(modules_header_width);
3162 let lookup_table = Arc::new(Mutex::new(Vec::new()));
3163
3164 for module in [
3165 0,
3166 1,
3167 2,
3168 module_count / 2,
3169 module_count.wrapping_sub(5),
3170 module_count.wrapping_sub(1),
3171 ]
3172 .into_iter()
3173 .filter(|&m| m < module_count)
3174 {
3175 let encoded = CodeGenResultComments::encode_bytepos(
3176 modules_header_width,
3177 module,
3178 pos,
3179 lookup_table.clone(),
3180 )
3181 .unwrap();
3182 let (decoded_module, decoded_pos) =
3183 CodeGenResultComments::decode_bytepos(modules_header_width, encoded, &lookup_table);
3184 assert_eq!(
3185 decoded_module as u32, module,
3186 "Testing width {modules_header_width} and pos {pos:?}"
3187 );
3188 assert_eq!(
3189 decoded_pos, pos,
3190 "Testing width {modules_header_width} and pos {pos:?}"
3191 );
3192 }
3193 }
3194
3195 #[test]
3196 fn test_encode_decode_bytepos_format() {
3197 let table = Arc::new(Mutex::new(Vec::new()));
3198
3199 for (pos, module, modules_header_width, result) in [
3200 (
3201 0b00000000000000000000000000000101,
3202 0b1,
3203 1,
3204 0b00100000000000000000000000000101,
3205 ),
3206 (
3207 0b00000000000000000000000000000101,
3208 0b01,
3209 2,
3210 0b00010000000000000000000000000101,
3211 ),
3212 (
3213 0b11111111111111110000000000000101,
3214 0b0110,
3215 4,
3216 0b01011011111111110000000000000101,
3217 ),
3218 (
3219 BytePos::PLACEHOLDER.0,
3220 0b01111,
3221 5,
3222 0b01011111111111111111111111111101,
3223 ),
3224 (
3225 BytePos::PURE.0,
3226 0b01111,
3227 5,
3228 0b01011111111111111111111111111110,
3229 ),
3230 (
3231 BytePos::SYNTHESIZED.0,
3232 0b01111,
3233 5,
3234 0b01011111111111111111111111111111,
3235 ),
3236 (
3239 0b00000111111111110000000000000101,
3240 0b0001,
3241 4,
3242 0b10000000000000000000000000000000,
3243 ),
3244 (
3246 0b00000111111111110000000000111110,
3247 0b0001,
3248 4,
3249 0b10000000000000000000000000000001,
3250 ),
3251 (BytePos::DUMMY.0, 0b0001, 4, BytePos::DUMMY.0),
3253 ] {
3254 let encoded = CodeGenResultComments::encode_bytepos(
3255 modules_header_width,
3256 module,
3257 BytePos(pos),
3258 table.clone(),
3259 )
3260 .unwrap();
3261 assert_eq!(encoded.0, result);
3262
3263 if encoded.0 & CodeGenResultComments::CONTINUATION_BIT
3265 == CodeGenResultComments::CONTINUATION_BIT
3266 {
3267 let index = encoded.0 & !CodeGenResultComments::CONTINUATION_BIT;
3268 let ModulePosition(encoded_module, encoded_pos) =
3269 table.lock().unwrap()[index as usize];
3270 assert_eq!(encoded_module, module);
3271 assert_eq!(encoded_pos, pos);
3272 }
3273 }
3274 }
3275
3276 #[test]
3277 fn test_encode_decode_bytepos_lossless() {
3278 const DUMMY_RESERVE: u32 = u32::MAX - 2_u32.pow(16);
3280
3281 for modules_header_width in 1..=10 {
3282 for pos in [
3283 BytePos(1),
3285 BytePos(2),
3286 BytePos(100),
3287 BytePos(4_000_000),
3288 BytePos(600_000_000),
3289 BytePos(u32::MAX - 3), BytePos::PLACEHOLDER,
3291 BytePos::SYNTHESIZED,
3292 BytePos::PURE,
3293 BytePos(DUMMY_RESERVE),
3294 BytePos(DUMMY_RESERVE + 10),
3295 BytePos(DUMMY_RESERVE + 10000),
3296 ] {
3297 bytepos_ensure_identical(modules_header_width, pos);
3298 }
3299 }
3300 }
3301}