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