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