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(true));
1006 }
1007
1008 Ok(Vc::cell(false))
1009 }
1010
1011 #[turbo_tasks::function]
1012 async fn merge(
1013 self: Vc<Self>,
1014 modules: Vc<MergeableModulesExposed>,
1015 entry_points: Vc<MergeableModules>,
1016 ) -> Result<Vc<Box<dyn ChunkableModule>>> {
1017 Ok(Vc::upcast(
1018 *MergedEcmascriptModule::new(
1019 modules,
1020 entry_points,
1021 self.options().to_resolved().await?,
1022 )
1023 .await?,
1024 ))
1025 }
1026}
1027
1028#[turbo_tasks::value_impl]
1029impl EvaluatableAsset for EcmascriptModuleAsset {}
1030
1031#[turbo_tasks::value_impl]
1032impl ResolveOrigin for EcmascriptModuleAsset {
1033 fn origin_path(&self) -> FileSystemPath {
1034 self.origin_path.clone()
1035 }
1036
1037 fn asset_context(&self) -> ResolvedVc<Box<dyn AssetContext>> {
1038 self.asset_context
1039 }
1040}
1041
1042#[turbo_tasks::value(shared)]
1044pub struct EcmascriptModuleContent {
1045 pub inner_code: Rope,
1046 pub source_map: Option<StructuredSourceMap>,
1047 pub is_esm: bool,
1048 pub strict: bool,
1049 pub additional_ids: SmallVec<[ModuleId; 1]>,
1050}
1051
1052#[turbo_tasks::value(shared)]
1053#[derive(Clone, Debug, Hash)]
1054pub struct EcmascriptModuleContentOptions {
1055 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1056 parsed: Option<ResolvedVc<ParseResult>>,
1057 specified_module_type: SpecifiedModuleType,
1058 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
1059 references: ResolvedVc<ModuleReferences>,
1060 part_references: Vec<ResolvedVc<EcmascriptModulePartReference>>,
1061 esm_references: ResolvedVc<EsmAssetReferences>,
1062 code_generation: ResolvedVc<CodeGens>,
1063 async_module: ResolvedVc<OptionAsyncModule>,
1064 generate_source_map: bool,
1065 original_source_map: Option<ResolvedVc<Box<dyn GenerateSourceMap>>>,
1066 exports: ResolvedVc<EcmascriptExports>,
1067 export_registration_mode: Option<ExportRegistrationMode>,
1070 async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
1071}
1072
1073#[turbo_tasks::function]
1077pub async fn chunk_item_content_with_code_from(
1078 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1079 code_source: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1080 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
1081 async_module_info: Option<Vc<AsyncModuleInfo>>,
1082) -> Result<Vc<EcmascriptChunkItemContent>> {
1083 let Some(analyzable) = ResolvedVc::try_sidecast::<Box<dyn EcmascriptAnalyzable>>(code_source)
1084 else {
1085 bail!("virtual ECMAScript code source must be analyzable");
1086 };
1087 let source_options = analyzable
1088 .module_content_options(*chunking_context, async_module_info)
1089 .await?;
1090 let mut options = (*source_options).clone();
1091 options.module = module;
1092 let content = EcmascriptModuleContent::new(options.cell());
1093 let async_module_options = code_source
1094 .get_async_module()
1095 .module_options(async_module_info);
1096 Ok(EcmascriptChunkItemContent::new(
1097 content,
1098 *chunking_context,
1099 async_module_options,
1100 ))
1101}
1102
1103impl EcmascriptModuleContentOptions {
1104 async fn merged_code_gens(
1105 &self,
1106 scope_hoisting_context: ScopeHoistingContext<'_>,
1107 eval_context: &EvalContext,
1108 ) -> Result<(Vec<CodeGeneration>, ReadRef<CodeGens>)> {
1109 let EcmascriptModuleContentOptions {
1112 module,
1113 chunking_context,
1114 references,
1115 part_references,
1116 esm_references,
1117 code_generation,
1118 async_module,
1119 exports,
1120 export_registration_mode,
1121 async_module_info,
1122 ..
1123 } = self;
1124
1125 async {
1126 let async_module_ref = async_module.await?;
1127 let esm_references_ref = esm_references.await?;
1128 let (exports_code_gen, subsumed_imports) =
1132 if let EcmascriptExports::EsmExports(exports) = *exports.await? {
1133 let (code_gen, subsumed) = exports
1134 .code_generation(
1135 **chunking_context,
1136 scope_hoisting_context,
1137 eval_context,
1138 *module,
1139 export_registration_mode
1140 .unwrap_or_else(|| eval_context.imports.export_registration_mode()),
1141 export_registration_mode
1142 .map(|_| (&part_references[..], &esm_references_ref[..])),
1143 async_module_info.is_some(),
1144 )
1145 .await?;
1146 (Some(code_gen), subsumed)
1147 } else {
1148 (None, Default::default())
1149 };
1150
1151 let additional_code_gens = [
1152 if let Some(async_module) = &*async_module_ref {
1153 Some(
1154 async_module
1155 .code_generation(
1156 async_module_info.map(|info| *info),
1157 **references,
1158 **chunking_context,
1159 )
1160 .await?,
1161 )
1162 } else {
1163 None
1164 },
1165 exports_code_gen,
1166 ];
1167
1168 let part_code_gens = part_references
1169 .iter()
1170 .map(|r| {
1171 r.code_generation(
1172 **chunking_context,
1173 scope_hoisting_context,
1174 &subsumed_imports.namespaces,
1175 )
1176 })
1177 .try_join()
1178 .await?;
1179
1180 let esm_code_gens = esm_references_ref
1181 .iter()
1182 .map(|r| {
1183 r.code_generation(
1184 **chunking_context,
1185 scope_hoisting_context,
1186 &subsumed_imports,
1187 )
1188 })
1189 .try_join()
1190 .await?;
1191
1192 let code_generation = code_generation.await?;
1193 let code_gens = code_generation
1194 .code_gens
1195 .iter()
1196 .map(|c| {
1197 c.code_generation(
1198 &code_generation.ast_paths,
1199 **chunking_context,
1200 scope_hoisting_context,
1201 *module,
1202 *exports,
1203 )
1204 })
1205 .try_join()
1206 .await?;
1207
1208 anyhow::Ok((
1209 part_code_gens
1210 .into_iter()
1211 .chain(esm_code_gens)
1212 .chain(additional_code_gens.into_iter().flatten())
1213 .chain(code_gens)
1214 .collect(),
1215 code_generation,
1216 ))
1217 }
1218 .instrument(tracing::info_span!("precompute code generation"))
1219 .await
1220 }
1221}
1222
1223#[turbo_tasks::value_impl]
1224impl EcmascriptModuleContent {
1225 #[turbo_tasks::function]
1227 pub async fn new(input: Vc<EcmascriptModuleContentOptions>) -> Result<Vc<Self>> {
1228 let input = input.await?;
1229 let EcmascriptModuleContentOptions {
1230 parsed,
1231 module,
1232 specified_module_type,
1233 generate_source_map,
1234 original_source_map,
1235 chunking_context,
1236 ..
1237 } = &*input;
1238
1239 let minify = chunking_context.minify_type().await?;
1240
1241 let content = process_parse_result(
1242 *parsed,
1243 module.ident(),
1244 *specified_module_type,
1245 *generate_source_map,
1246 *original_source_map,
1247 *minify,
1248 Some(&*input),
1249 None,
1250 )
1251 .await?;
1252 emit_content(content, Default::default()).await
1253 }
1254
1255 #[turbo_tasks::function]
1257 pub async fn new_without_analysis(
1258 parsed: Vc<ParseResult>,
1259 ident: Vc<AssetIdent>,
1260 specified_module_type: SpecifiedModuleType,
1261 generate_source_map: bool,
1262 ) -> Result<Vc<Self>> {
1263 let content = process_parse_result(
1264 Some(parsed.to_resolved().await?),
1265 ident,
1266 specified_module_type,
1267 generate_source_map,
1268 None,
1269 MinifyType::NoMinify,
1270 None,
1271 None,
1272 )
1273 .await?;
1274 emit_content(content, Default::default()).await
1275 }
1276
1277 #[turbo_tasks::function]
1284 pub async fn new_merged(
1285 modules: Vec<(
1286 ResolvedVc<Box<dyn EcmascriptAnalyzable>>,
1287 MergeableModuleExposure,
1288 )>,
1289 module_options: Vec<Vc<EcmascriptModuleContentOptions>>,
1290 entry_points: Vec<ResolvedVc<Box<dyn EcmascriptAnalyzable>>>,
1291 ) -> Result<Vc<Self>> {
1292 async {
1293 let modules = modules
1294 .into_iter()
1295 .map(|(m, exposed)| {
1296 (
1297 ResolvedVc::try_sidecast::<Box<dyn EcmascriptChunkPlaceable>>(m).unwrap(),
1298 exposed,
1299 )
1300 })
1301 .collect::<FxIndexMap<_, _>>();
1302 let entry_points = entry_points
1303 .into_iter()
1304 .map(|m| {
1305 let m =
1306 ResolvedVc::try_sidecast::<Box<dyn EcmascriptChunkPlaceable>>(m).unwrap();
1307 (m, modules.get_index_of(&m).unwrap())
1308 })
1309 .collect::<Vec<_>>();
1310
1311 let globals_merged = Globals::default();
1312
1313 let contents = module_options
1314 .iter()
1315 .map(async |options| {
1316 let options = options.await?;
1317 let EcmascriptModuleContentOptions {
1318 chunking_context,
1319 parsed,
1320 module,
1321 specified_module_type,
1322 generate_source_map,
1323 original_source_map,
1324 ..
1325 } = &*options;
1326
1327 let result = process_parse_result(
1328 *parsed,
1329 module.ident(),
1330 *specified_module_type,
1331 *generate_source_map,
1332 *original_source_map,
1333 *chunking_context.minify_type().await?,
1334 Some(&*options),
1335 Some(ScopeHoistingOptions {
1336 module: *module,
1337 modules: &modules,
1338 }),
1339 )
1340 .await?;
1341
1342 Ok((*module, result))
1343 })
1344 .try_join()
1345 .await?;
1346
1347 let (merged_ast, comments, source_maps, original_source_maps, lookup_table) =
1348 merge_modules(contents, &entry_points, &globals_merged).await?;
1349
1350 let options = module_options.last().unwrap().await?;
1353
1354 let modules_header_width = modules.len().next_power_of_two().trailing_zeros();
1355 let content = CodeGenResult {
1356 program: merged_ast,
1357 source_map: CodeGenResultSourceMap::ScopeHoisting {
1358 modules_header_width,
1359 lookup_table: lookup_table.clone(),
1360 source_maps,
1361 },
1362 comments: CodeGenResultComments::ScopeHoisting {
1363 modules_header_width,
1364 lookup_table,
1365 comments,
1366 },
1367 is_esm: true,
1368 strict: true,
1369 original_source_map: CodeGenResultOriginalSourceMap::ScopeHoisting(
1370 original_source_maps,
1371 ),
1372 minify: *options.chunking_context.minify_type().await?,
1373 scope_hoisting_syntax_contexts: None,
1374 };
1375
1376 let first_entry = entry_points.first().unwrap().0;
1377 let additional_ids = modules
1378 .keys()
1379 .filter(|m| {
1386 **m != first_entry
1387 && *modules.get(*m).unwrap() == MergeableModuleExposure::External
1388 })
1389 .map(|m| m.chunk_item_id(*options.chunking_context))
1390 .try_join()
1391 .await?
1392 .into();
1393
1394 emit_content(content, additional_ids)
1395 .instrument(tracing::info_span!("emit code"))
1396 .await
1397 }
1398 .instrument(tracing::info_span!(
1399 "generate merged code",
1400 modules = module_options.len()
1401 ))
1402 .await
1403 }
1404}
1405
1406const EARLY_HOIST_START: &str = " TURBOPACK EARLY HOIST START";
1409const EARLY_HOIST_END: &str = " TURBOPACK EARLY HOIST END";
1410
1411fn early_hoist_comment(text: &str) -> Comment {
1412 Comment {
1413 kind: CommentKind::Line,
1414 span: DUMMY_SP,
1415 text: text.into(),
1416 }
1417}
1418
1419fn early_hoist_range(
1422 comments: &SwcComments,
1423 body: impl Iterator<Item = Span>,
1424) -> Option<(usize, usize)> {
1425 let (mut start, mut end) = (None, None);
1426 for (i, span) in body.enumerate() {
1427 let Some(leading) = comments.get_leading(span.lo) else {
1428 continue;
1429 };
1430 for comment in leading {
1431 if comment.text == EARLY_HOIST_START {
1432 start = Some(i);
1433 } else if comment.text == EARLY_HOIST_END {
1434 end = Some(i);
1435 }
1436 }
1437 }
1438 Some((start?, end?))
1439}
1440
1441#[instrument(level = Level::TRACE, skip_all, name = "merge")]
1450#[allow(clippy::type_complexity)]
1451async fn merge_modules(
1452 mut contents: Vec<(ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, CodeGenResult)>,
1453 entry_points: &Vec<(ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, usize)>,
1454 globals_merged: &'_ Globals,
1455) -> Result<(
1456 Program,
1457 Vec<CodeGenResultComments>,
1458 Vec<CodeGenResultSourceMap>,
1459 SmallVec<[ResolvedVc<Box<dyn GenerateSourceMap>>; 1]>,
1460 Arc<Mutex<Vec<ModulePosition>>>,
1461)> {
1462 struct SetSyntaxContextVisitor<'a> {
1463 modules_header_width: u32,
1464 current_module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1465 current_module_idx: u32,
1466 lookup_table: &'a mut Vec<ModulePosition>,
1467 reverse_module_contexts:
1469 FxHashMap<SyntaxContext, ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
1470 export_contexts: &'a FxHashMap<
1473 ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1474 &'a FxHashMap<RcStr, (Id, Span)>,
1475 >,
1476 unique_contexts_cache: &'a mut FxHashMap<
1479 (ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, SyntaxContext),
1480 SyntaxContext,
1481 >,
1482
1483 error: anyhow::Result<()>,
1484 }
1485
1486 impl<'a> SetSyntaxContextVisitor<'a> {
1487 fn get_context_for(
1488 &mut self,
1489 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1490 local_ctxt: SyntaxContext,
1491 ) -> SyntaxContext {
1492 if let Some(&global_ctxt) = self.unique_contexts_cache.get(&(module, local_ctxt)) {
1493 global_ctxt
1494 } else {
1495 let global_ctxt = SyntaxContext::empty().apply_mark(Mark::new());
1496 self.unique_contexts_cache
1497 .insert((module, local_ctxt), global_ctxt);
1498 global_ctxt
1499 }
1500 }
1501 }
1502
1503 impl VisitMut for SetSyntaxContextVisitor<'_> {
1504 fn visit_mut_ident(&mut self, ident: &mut Ident) {
1505 let Ident {
1506 sym, ctxt, span, ..
1507 } = ident;
1508
1509 if let Some(&module) = self.reverse_module_contexts.get(ctxt) {
1512 let eval_context_exports = self.export_contexts.get(&module).unwrap();
1513 let sym_rc_str: RcStr = sym.as_str().into();
1516 let (local, local_ctxt) = if let Some(((local, local_ctxt), _)) =
1517 eval_context_exports.get(&sym_rc_str)
1518 {
1519 (Some(local), *local_ctxt)
1520 } else if sym.starts_with("__TURBOPACK__imported__module__") {
1521 (None, SyntaxContext::empty())
1526 } else {
1527 self.error = Err(anyhow::anyhow!(
1528 "Expected to find a local export for {sym} with ctxt {ctxt:#?} in \
1529 {eval_context_exports:?}",
1530 ));
1531 return;
1532 };
1533
1534 let global_ctxt = self.get_context_for(module, local_ctxt);
1535
1536 if let Some(local) = local {
1537 *sym = local.clone();
1538 }
1539 *ctxt = global_ctxt;
1540 span.visit_mut_with(self);
1541 } else {
1542 ident.visit_mut_children_with(self);
1543 }
1544 }
1545
1546 fn visit_mut_syntax_context(&mut self, local_ctxt: &mut SyntaxContext) {
1547 let module = self
1550 .reverse_module_contexts
1551 .get(local_ctxt)
1552 .copied()
1553 .unwrap_or(self.current_module);
1554
1555 let global_ctxt = self.get_context_for(module, *local_ctxt);
1556 *local_ctxt = global_ctxt;
1557 }
1558 fn visit_mut_span(&mut self, span: &mut Span) {
1559 span.lo = CodeGenResultComments::encode_bytepos_with_vec(
1562 self.modules_header_width,
1563 self.current_module_idx,
1564 span.lo,
1565 self.lookup_table,
1566 )
1567 .unwrap_or_else(|err| {
1568 self.error = Err(err);
1569 span.lo
1570 });
1571 span.hi = CodeGenResultComments::encode_bytepos_with_vec(
1572 self.modules_header_width,
1573 self.current_module_idx,
1574 span.hi,
1575 self.lookup_table,
1576 )
1577 .unwrap_or_else(|err| {
1578 self.error = Err(err);
1579 span.hi
1580 });
1581 }
1582 }
1583
1584 let mut programs = contents
1587 .iter_mut()
1588 .map(|(_, content)| content.program.take())
1589 .collect::<Vec<_>>();
1590
1591 let export_contexts = contents
1592 .iter()
1593 .map(|(module, content)| {
1594 Ok((
1595 *module,
1596 content
1597 .scope_hoisting_syntax_contexts
1598 .as_ref()
1599 .map(|(_, export_contexts)| export_contexts)
1600 .context("expected exports contexts")?,
1601 ))
1602 })
1603 .collect::<Result<FxHashMap<_, _>>>()?;
1604
1605 let mut lookup_table = Vec::new();
1606 let result = GLOBALS.set(globals_merged, || {
1607 let _ = tracing::trace_span!("merge inner").entered();
1608 let mut unique_contexts_cache =
1610 FxHashMap::with_capacity_and_hasher(contents.len() * 5, Default::default());
1611
1612 let mut merged_prelude = Vec::new();
1613 let mut prepare_module =
1614 |module_count: usize,
1615 current_module_idx: usize,
1616 (module, content): &(ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, CodeGenResult),
1617 program: &mut Program,
1618 merged_prelude: &mut Vec<ModuleItem>,
1619 lookup_table: &mut Vec<ModulePosition>| {
1620 let _ = tracing::trace_span!("prepare module").entered();
1621 if let CodeGenResult {
1622 scope_hoisting_syntax_contexts: Some((module_contexts, _)),
1623 comments: CodeGenResultComments::Single { extra_comments, .. },
1624 ..
1625 } = content
1626 {
1627 let early_hoisted = match &*program {
1630 Program::Module(module) => {
1631 early_hoist_range(extra_comments, module.body.iter().map(|i| i.span()))
1632 }
1633 Program::Script(script) => {
1634 early_hoist_range(extra_comments, script.body.iter().map(|s| s.span()))
1635 }
1636 };
1637
1638 let modules_header_width = module_count.next_power_of_two().trailing_zeros();
1639 GLOBALS.set(globals_merged, || {
1640 let mut visitor = SetSyntaxContextVisitor {
1641 modules_header_width,
1642 current_module: *module,
1643 current_module_idx: current_module_idx as u32,
1644 lookup_table,
1645 reverse_module_contexts: module_contexts
1646 .iter()
1647 .map(|e| (*e.value(), *e.key()))
1648 .collect(),
1649 export_contexts: &export_contexts,
1650 unique_contexts_cache: &mut unique_contexts_cache,
1651 error: Ok(()),
1652 };
1653 program.visit_mut_with(&mut visitor);
1654 visitor.error
1655 })?;
1656
1657 if let Some((start, end)) = early_hoisted {
1659 let mut hoisted: Vec<ModuleItem> = match program {
1660 Program::Module(module) => module.body.drain(start..=end).collect(),
1661 Program::Script(script) => script
1662 .body
1663 .drain(start..=end)
1664 .map(ModuleItem::Stmt)
1665 .collect(),
1666 };
1667 hoisted.pop();
1668 hoisted.remove(0);
1669 merged_prelude.extend(hoisted);
1670 }
1671
1672 Ok(match program.take() {
1673 Program::Module(module) => Either::Left(module.body.into_iter()),
1674 Program::Script(script) => {
1677 Either::Right(script.body.into_iter().map(ModuleItem::Stmt))
1678 }
1679 })
1680 } else {
1681 bail!("Expected scope_hosting_syntax_contexts");
1682 }
1683 };
1684
1685 let mut inserted = FxHashSet::with_capacity_and_hasher(contents.len(), Default::default());
1686 inserted.extend(entry_points.iter().map(|(_, i)| *i));
1688
1689 let mut inserted_imports = FxHashMap::default();
1690
1691 let span = tracing::trace_span!("merge ASTs");
1692 let mut queue = entry_points
1695 .iter()
1696 .map(|&(_, i)| {
1697 prepare_module(
1698 contents.len(),
1699 i,
1700 &contents[i],
1701 &mut programs[i],
1702 &mut merged_prelude,
1703 &mut lookup_table,
1704 )
1705 .map_err(|err| (i, err))
1706 })
1707 .flatten_ok()
1708 .rev()
1709 .collect::<Result<Vec<_>, _>>()?;
1710 let mut result = vec![];
1711 while let Some(item) = queue.pop() {
1712 if let ModuleItem::Stmt(stmt) = &item {
1713 match stmt {
1714 Stmt::Expr(ExprStmt { expr, .. }) => {
1715 if let Expr::Call(CallExpr {
1716 callee: Callee::Expr(callee),
1717 args,
1718 ..
1719 }) = &**expr
1720 && callee.is_ident_ref_to("__turbopack_merged_esm__")
1721 {
1722 let index =
1723 args[0].expr.as_lit().unwrap().as_num().unwrap().value as usize;
1724
1725 if inserted.insert(index) {
1727 queue.extend(
1728 prepare_module(
1729 contents.len(),
1730 index,
1731 &contents[index],
1732 &mut programs[index],
1733 &mut merged_prelude,
1734 &mut lookup_table,
1735 )
1736 .map_err(|err| (index, err))?
1737 .into_iter()
1738 .rev(),
1739 );
1740 }
1741 continue;
1742 }
1743 }
1744 Stmt::Decl(Decl::Var(var)) => {
1745 if let [decl] = &*var.decls
1746 && let Some(name) = decl.name.as_ident()
1747 && name.sym.starts_with("__TURBOPACK__imported__module__")
1748 {
1749 match inserted_imports.entry(name.sym.clone()) {
1754 Entry::Occupied(entry) => {
1755 let entry_ctxt = *entry.get();
1760 let new = Ident::new(name.sym.clone(), DUMMY_SP, name.ctxt);
1761 let old = Ident::new(name.sym.clone(), DUMMY_SP, entry_ctxt);
1762 result.push(ModuleItem::Stmt(
1763 quote!("var $new = $old;" as Stmt,
1764 new: Ident = new,
1765 old: Ident = old
1766 ),
1767 ));
1768 continue;
1769 }
1770 Entry::Vacant(entry) => {
1771 entry.insert(name.ctxt);
1772 }
1773 }
1774 }
1775 }
1776 _ => (),
1777 }
1778 }
1779
1780 result.push(item);
1781 }
1782 drop(span);
1783
1784 let span = tracing::trace_span!("hygiene").entered();
1785 let mut merged_ast = Program::Module(swc_core::ecma::ast::Module {
1786 body: merged_prelude.into_iter().chain(result).collect(),
1787 span: DUMMY_SP,
1788 shebang: None,
1789 });
1790 merged_ast.visit_mut_with(&mut swc_core::ecma::transforms::base::hygiene::hygiene());
1791 drop(span);
1792
1793 Ok((merged_ast, inserted))
1794 });
1795
1796 let (merged_ast, inserted) = match result {
1797 Ok(v) => v,
1798 Err((content_idx, err)) => {
1799 return Err(
1800 err.context(turbofmt!("Processing {}", contents[content_idx].0.ident()).await?),
1802 );
1803 }
1804 };
1805
1806 if cfg!(debug_assertions) && inserted.len() != contents.len() {
1807 bail!(
1808 "Not all merged modules were inserted: {:?}",
1809 contents
1810 .iter()
1811 .enumerate()
1812 .map(async |(i, m)| Ok((inserted.contains(&i), m.0.ident().to_string().await?)))
1813 .try_join()
1814 .await?,
1815 );
1816 }
1817
1818 let comments = contents
1819 .iter_mut()
1820 .map(|(_, content)| content.comments.take())
1821 .collect::<Vec<_>>();
1822
1823 let source_maps = contents
1824 .iter_mut()
1825 .map(|(_, content)| std::mem::take(&mut content.source_map))
1826 .collect::<Vec<_>>();
1827
1828 let original_source_maps = contents
1829 .iter_mut()
1830 .flat_map(|(_, content)| match content.original_source_map {
1831 CodeGenResultOriginalSourceMap::ScopeHoisting(_) => unreachable!(
1832 "Didn't expect nested CodeGenResultOriginalSourceMap::ScopeHoisting: {:?}",
1833 content.original_source_map
1834 ),
1835 CodeGenResultOriginalSourceMap::Single(map) => map,
1836 })
1837 .collect();
1838
1839 Ok((
1840 merged_ast,
1841 comments,
1842 source_maps,
1843 original_source_maps,
1844 Arc::new(Mutex::new(lookup_table)),
1845 ))
1846}
1847
1848#[derive(Clone, Copy)]
1853pub enum ScopeHoistingContext<'a> {
1854 Some {
1855 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1857 modules:
1859 &'a FxIndexMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, MergeableModuleExposure>,
1860
1861 is_import_mark: Mark,
1862 globals: &'a Arc<Globals>,
1863 module_syntax_contexts_cache:
1865 &'a FxDashMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, SyntaxContext>,
1866 },
1867 None,
1868}
1869
1870impl<'a> ScopeHoistingContext<'a> {
1871 pub fn module(&self) -> Option<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>> {
1873 match self {
1874 ScopeHoistingContext::Some { module, .. } => Some(*module),
1875 ScopeHoistingContext::None => None,
1876 }
1877 }
1878
1879 pub fn skip_module_exports(&self) -> bool {
1881 match self {
1882 ScopeHoistingContext::Some {
1883 module, modules, ..
1884 } => match modules.get(module).unwrap() {
1885 MergeableModuleExposure::None => true,
1886 MergeableModuleExposure::Internal | MergeableModuleExposure::External => false,
1887 },
1888 ScopeHoistingContext::None => false,
1889 }
1890 }
1891
1892 pub fn get_module_syntax_context(
1894 &self,
1895 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1896 ) -> Option<SyntaxContext> {
1897 match self {
1898 ScopeHoistingContext::Some {
1899 modules,
1900 module_syntax_contexts_cache,
1901 globals,
1902 is_import_mark,
1903 ..
1904 } => {
1905 if !modules.contains_key(&module) {
1906 return None;
1907 }
1908
1909 Some(match module_syntax_contexts_cache.entry(module) {
1910 dashmap::Entry::Occupied(e) => *e.get(),
1911 dashmap::Entry::Vacant(e) => {
1912 let ctxt = GLOBALS.set(globals, || {
1913 let mark = Mark::fresh(*is_import_mark);
1914 SyntaxContext::empty()
1915 .apply_mark(*is_import_mark)
1916 .apply_mark(mark)
1917 });
1918
1919 e.insert(ctxt);
1920 ctxt
1921 }
1922 })
1923 }
1924 ScopeHoistingContext::None => None,
1925 }
1926 }
1927
1928 pub fn get_module_index(
1929 &self,
1930 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1931 ) -> Option<usize> {
1932 match self {
1933 ScopeHoistingContext::Some { modules, .. } => modules.get_index_of(&module),
1934 ScopeHoistingContext::None => None,
1935 }
1936 }
1937}
1938
1939struct CodeGenResult {
1940 program: Program,
1941 source_map: CodeGenResultSourceMap,
1942 comments: CodeGenResultComments,
1943 is_esm: bool,
1944 strict: bool,
1945 original_source_map: CodeGenResultOriginalSourceMap,
1946 minify: MinifyType,
1947 #[allow(clippy::type_complexity)]
1948 scope_hoisting_syntax_contexts: Option<(
1950 FxDashMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable + 'static>>, SyntaxContext>,
1951 FxHashMap<RcStr, (Id, Span)>,
1952 )>,
1953}
1954
1955struct ScopeHoistingOptions<'a> {
1956 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1957 modules: &'a FxIndexMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, MergeableModuleExposure>,
1958}
1959
1960async fn process_parse_result(
1961 parsed: Option<ResolvedVc<ParseResult>>,
1962 ident: Vc<AssetIdent>,
1963 specified_module_type: SpecifiedModuleType,
1964 generate_source_map: bool,
1965 original_source_map: Option<ResolvedVc<Box<dyn GenerateSourceMap>>>,
1966 minify: MinifyType,
1967 options: Option<&EcmascriptModuleContentOptions>,
1968 scope_hoisting_options: Option<ScopeHoistingOptions<'_>>,
1969) -> Result<CodeGenResult> {
1970 with_consumed_parse_result(
1971 parsed,
1972 async |mut program, source_map, globals, eval_context, comments| -> Result<CodeGenResult> {
1973 let (top_level_mark, is_esm, strict) = eval_context
1974 .as_ref()
1975 .map_either(
1976 |e| {
1977 (
1978 e.top_level_mark,
1979 e.is_esm(specified_module_type),
1980 e.imports.strict,
1981 )
1982 },
1983 |e| {
1984 (
1985 e.top_level_mark,
1986 e.is_esm(specified_module_type),
1987 e.imports.strict,
1988 )
1989 },
1990 )
1991 .into_inner();
1992
1993 let (mut code_gens, ast_paths, retain_syntax_context, prepend_ident_comment) =
1994 if let Some(scope_hoisting_options) = scope_hoisting_options {
1995 let is_import_mark = GLOBALS.set(globals, || Mark::new());
1996
1997 let module_syntax_contexts_cache = FxDashMap::default();
1998 let ctx = ScopeHoistingContext::Some {
1999 module: scope_hoisting_options.module,
2000 modules: scope_hoisting_options.modules,
2001 module_syntax_contexts_cache: &module_syntax_contexts_cache,
2002 is_import_mark,
2003 globals,
2004 };
2005 let (code_gens, ast_paths) = options
2006 .unwrap()
2007 .merged_code_gens(
2008 ctx,
2009 match &eval_context {
2010 Either::Left(e) => e,
2011 Either::Right(e) => e,
2012 },
2013 )
2014 .await?;
2015
2016 let export_contexts = eval_context
2017 .map_either(
2018 |e| Cow::Owned(e.imports.exports_ids),
2019 |e| Cow::Borrowed(&e.imports.exports_ids),
2020 )
2021 .into_inner();
2022 let preserved_exports =
2023 match &*scope_hoisting_options.module.get_exports().await? {
2024 EcmascriptExports::EsmExports(exports) => exports
2025 .await?
2026 .exports
2027 .iter()
2028 .filter(|(_, e)| matches!(e, export::EsmExport::LocalBinding(_, _)))
2029 .map(|(name, e)| {
2030 if let Some(((sym, ctxt), _)) = export_contexts.get(name) {
2031 Ok((sym.clone(), *ctxt))
2032 } else {
2033 bail!("Couldn't find export {} for binding {:?}", name, e);
2034 }
2035 })
2036 .collect::<Result<FxHashSet<_>>>()?,
2037 _ => Default::default(),
2038 };
2039
2040 let prepend_ident_comment = if matches!(minify, MinifyType::NoMinify) {
2041 Some(Comment {
2042 kind: CommentKind::Line,
2043 span: DUMMY_SP,
2044 text: (&*turbofmt!(" MERGED MODULE: {}", ident).await?).into(),
2045 })
2046 } else {
2047 None
2048 };
2049
2050 (
2051 code_gens,
2052 Some(ast_paths),
2053 Some((
2054 is_import_mark,
2055 module_syntax_contexts_cache,
2056 preserved_exports,
2057 export_contexts,
2058 )),
2059 prepend_ident_comment,
2060 )
2061 } else if let Some(options) = options {
2062 let (code_gens, ast_paths) = options
2063 .merged_code_gens(
2064 ScopeHoistingContext::None,
2065 match &eval_context {
2066 Either::Left(e) => e,
2067 Either::Right(e) => e,
2068 },
2069 )
2070 .await?;
2071 (code_gens, Some(ast_paths), None, None)
2072 } else {
2073 (vec![], None, None, None)
2074 };
2075
2076 let extra_comments = SwcComments {
2077 leading: Default::default(),
2078 trailing: Default::default(),
2079 };
2080
2081 let early_hoisted_count = process_content_with_code_gens(
2082 &mut program,
2083 globals,
2084 ast_paths.as_deref().map(|p| &p.ast_paths),
2085 &mut code_gens,
2086 );
2087
2088 for comments in code_gens.iter_mut().flat_map(|cg| cg.comments.as_mut()) {
2089 let leading = Arc::unwrap_or_clone(take(&mut comments.leading));
2090 let trailing = Arc::unwrap_or_clone(take(&mut comments.trailing));
2091
2092 for (pos, v) in leading {
2093 extra_comments.leading.entry(pos).or_default().extend(v);
2094 }
2095
2096 for (pos, v) in trailing {
2097 extra_comments.trailing.entry(pos).or_default().extend(v);
2098 }
2099 }
2100
2101 GLOBALS.set(globals, || {
2102 if retain_syntax_context.is_some() && early_hoisted_count > 0 {
2105 let end = Span::dummy_with_cmt();
2106 extra_comments.add_leading(end.lo, early_hoist_comment(EARLY_HOIST_END));
2107 let start = Span::dummy_with_cmt();
2108 extra_comments.add_leading(start.lo, early_hoist_comment(EARLY_HOIST_START));
2109 let (end, start) = (
2110 Stmt::Empty(EmptyStmt { span: end }),
2111 Stmt::Empty(EmptyStmt { span: start }),
2112 );
2113 match &mut program {
2114 Program::Module(module) => {
2115 module
2116 .body
2117 .insert(early_hoisted_count, ModuleItem::Stmt(end));
2118 module.body.insert(0, ModuleItem::Stmt(start));
2119 }
2120 Program::Script(script) => {
2121 script.body.insert(early_hoisted_count, end);
2122 script.body.insert(0, start);
2123 }
2124 }
2125 }
2126
2127 if let Some(prepend_ident_comment) = prepend_ident_comment {
2128 let span = Span::dummy_with_cmt();
2129 extra_comments.add_leading(span.lo, prepend_ident_comment);
2130 let stmt = Stmt::Empty(EmptyStmt { span });
2131 match &mut program {
2132 Program::Module(module) => module.body.prepend_stmt(ModuleItem::Stmt(stmt)),
2133 Program::Script(script) => script.body.prepend_stmt(stmt),
2134 }
2135 }
2136
2137 if let Some((is_import_mark, _, preserved_exports, _)) = &retain_syntax_context {
2138 program.visit_mut_with(&mut hygiene_rename_only(
2139 Some(top_level_mark),
2140 *is_import_mark,
2141 preserved_exports,
2142 ));
2143 } else {
2144 program.visit_mut_with(
2145 &mut swc_core::ecma::transforms::base::hygiene::hygiene_with_config(
2146 swc_core::ecma::transforms::base::hygiene::Config {
2147 top_level_mark,
2148 ..Default::default()
2149 },
2150 ),
2151 );
2152 }
2153 program.visit_mut_with(&mut swc_core::ecma::transforms::base::fixer::fixer(None));
2154
2155 remove_shebang(&mut program);
2158 remove_directives(&mut program);
2159 });
2160
2161 Ok(CodeGenResult {
2162 program,
2163 source_map: if generate_source_map {
2164 CodeGenResultSourceMap::Single {
2165 source_map: source_map.clone(),
2166 }
2167 } else {
2168 CodeGenResultSourceMap::None
2169 },
2170 comments: CodeGenResultComments::Single {
2171 comments,
2172 extra_comments,
2173 },
2174 is_esm,
2175 strict,
2176 original_source_map: CodeGenResultOriginalSourceMap::Single(original_source_map),
2177 minify,
2178 scope_hoisting_syntax_contexts: retain_syntax_context
2179 .map(|(_, ctxts, _, export_contexts)| (ctxts, export_contexts.into_owned())),
2181 })
2182 },
2183 async |parse_result| -> Result<CodeGenResult> {
2184 Ok(match parse_result {
2185 ParseResult::Ok { .. } => unreachable!(),
2186 ParseResult::Unparsable { messages } => {
2187 let error_messages = messages
2188 .as_ref()
2189 .and_then(|m| m.first().map(|f| format!("\n{f}")))
2190 .unwrap_or("".into());
2191 let msg = &*turbofmt!(
2192 "Could not parse module '{}'\n{error_messages}",
2193 ident.await?.path
2194 )
2195 .await?;
2196 let body = vec![
2197 quote!(
2198 "var e = new Error($msg);" as Stmt,
2199 msg: Expr = Expr::Lit(msg.into()),
2200 ),
2201 quote!("e.code = 'MODULE_UNPARSABLE';" as Stmt),
2202 quote!("throw e;" as Stmt),
2203 ];
2204
2205 CodeGenResult {
2206 program: Program::Script(Script {
2207 span: DUMMY_SP,
2208 body,
2209 shebang: None,
2210 }),
2211 source_map: CodeGenResultSourceMap::None,
2212 comments: CodeGenResultComments::Empty,
2213 is_esm: false,
2214 strict: false,
2215 original_source_map: CodeGenResultOriginalSourceMap::Single(None),
2216 minify: MinifyType::NoMinify,
2217 scope_hoisting_syntax_contexts: None,
2218 }
2219 }
2220 ParseResult::NotFound => {
2221 let msg = &*turbofmt!(
2222 "Could not parse module '{}', file not found",
2223 ident.await?.path
2224 )
2225 .await?;
2226 let body = vec![
2227 quote!(
2228 "var e = new Error($msg);" as Stmt,
2229 msg: Expr = Expr::Lit(msg.into()),
2230 ),
2231 quote!("e.code = 'MODULE_UNPARSABLE';" as Stmt),
2232 quote!("throw e;" as Stmt),
2233 ];
2234 CodeGenResult {
2235 program: Program::Script(Script {
2236 span: DUMMY_SP,
2237 body,
2238 shebang: None,
2239 }),
2240 source_map: CodeGenResultSourceMap::None,
2241 comments: CodeGenResultComments::Empty,
2242 is_esm: false,
2243 strict: false,
2244 original_source_map: CodeGenResultOriginalSourceMap::Single(None),
2245 minify: MinifyType::NoMinify,
2246 scope_hoisting_syntax_contexts: None,
2247 }
2248 }
2249 })
2250 },
2251 )
2252 .instrument(tracing::trace_span!(
2253 "process parse result",
2254 ident = display(ident.to_string().await?),
2255 ))
2256 .await
2257}
2258
2259async fn with_consumed_parse_result<T>(
2261 parsed: Option<ResolvedVc<ParseResult>>,
2262 success: impl AsyncFnOnce(
2263 Program,
2264 &Arc<SourceMap>,
2265 &Arc<Globals>,
2266 Either<EvalContext, &'_ EvalContext>,
2267 Either<ImmutableComments, Arc<ImmutableComments>>,
2268 ) -> Result<T>,
2269 error: impl AsyncFnOnce(&ParseResult) -> Result<T>,
2270) -> Result<T> {
2271 let Some(parsed) = parsed else {
2272 let globals = Globals::new();
2273 let eval_context = GLOBALS.set(&globals, || EvalContext {
2274 unresolved_mark: Mark::new(),
2275 top_level_mark: Mark::new(),
2276 imports: Default::default(),
2277 force_free_values: Default::default(),
2278 });
2279 return success(
2280 Program::Module(swc_core::ecma::ast::Module::dummy()),
2281 &Default::default(),
2282 &Default::default(),
2283 Either::Left(eval_context),
2284 Either::Left(Default::default()),
2285 )
2286 .await;
2287 };
2288
2289 let parsed = parsed.final_read_hint().await?;
2290 match &*parsed {
2291 ParseResult::Ok { .. } => {
2292 let mut parsed = ReadRef::try_unwrap(parsed);
2293 let (program, source_map, globals, eval_context, comments) = match &mut parsed {
2294 Ok(ParseResult::Ok {
2295 program,
2296 source_map,
2297 globals,
2298 eval_context,
2299 comments,
2300 ..
2301 }) => (
2302 program.take(),
2303 &*source_map,
2304 &*globals,
2305 Either::Left(std::mem::replace(
2306 eval_context,
2307 EvalContext {
2308 unresolved_mark: eval_context.unresolved_mark,
2309 top_level_mark: eval_context.top_level_mark,
2310 imports: Default::default(),
2311 force_free_values: Default::default(),
2312 },
2313 )),
2314 match Arc::try_unwrap(take(comments)) {
2315 Ok(comments) => Either::Left(comments),
2316 Err(comments) => Either::Right(comments),
2317 },
2318 ),
2319 Err(parsed) => {
2320 let ParseResult::Ok {
2321 program,
2322 source_map,
2323 globals,
2324 eval_context,
2325 comments,
2326 ..
2327 } = &**parsed
2328 else {
2329 unreachable!();
2330 };
2331 (
2332 program.clone(),
2333 source_map,
2334 globals,
2335 Either::Right(eval_context),
2336 Either::Right(comments.clone()),
2337 )
2338 }
2339 _ => unreachable!(),
2340 };
2341
2342 success(program, source_map, globals, eval_context, comments).await
2343 }
2344 _ => error(&parsed).await,
2345 }
2346}
2347
2348async fn emit_content(
2349 content: CodeGenResult,
2350 additional_ids: SmallVec<[ModuleId; 1]>,
2351) -> Result<Vc<EcmascriptModuleContent>> {
2352 let CodeGenResult {
2353 program,
2354 source_map,
2355 comments,
2356 is_esm,
2357 strict,
2358 original_source_map,
2359 minify,
2360 scope_hoisting_syntax_contexts: _,
2361 } = content;
2362
2363 let generate_source_map = source_map.is_some();
2364
2365 let source_map_names = if generate_source_map {
2367 let mut collector = IdentCollector::default();
2368 program.visit_with(&mut collector);
2369 collector.into_map()
2370 } else {
2371 Default::default()
2372 };
2373
2374 let mut bytes: Vec<u8> = vec![];
2375 let mut mappings = vec![];
2379
2380 let source_map = Arc::new(source_map);
2381
2382 {
2383 let mut wr = JsWriter::new(
2384 Default::default(),
2386 "\n",
2387 &mut bytes,
2388 generate_source_map.then_some(&mut mappings),
2389 );
2390 if matches!(minify, MinifyType::Minify { .. }) {
2391 wr.set_indent_str("");
2392 }
2393
2394 let comments = comments.consumable();
2395
2396 let mut emitter = Emitter {
2397 cfg: swc_core::ecma::codegen::Config::default(),
2398 cm: source_map.clone(),
2399 comments: Some(&comments as &dyn Comments),
2400 wr,
2401 };
2402
2403 emitter.emit_program(&program)?;
2404 drop(program);
2406 }
2407
2408 let source_map = if generate_source_map {
2409 let original_source_maps = original_source_map
2410 .iter()
2411 .map(|map| map.generate_source_map())
2412 .try_join()
2413 .await?;
2414 let original_source_maps = original_source_maps
2415 .iter()
2416 .filter_map(|map| map.as_content())
2417 .map(|map| map.content())
2418 .collect::<Vec<_>>();
2419
2420 Some(generate_js_source_map(
2421 &*source_map,
2422 mappings,
2423 original_source_maps,
2424 matches!(
2425 original_source_map,
2426 CodeGenResultOriginalSourceMap::Single(_)
2427 ),
2428 true,
2429 source_map_names,
2430 )?)
2431 } else {
2432 None
2433 };
2434
2435 Ok(EcmascriptModuleContent {
2436 inner_code: bytes.into(),
2437 source_map,
2438 is_esm,
2439 strict,
2440 additional_ids,
2441 }
2442 .cell())
2443}
2444
2445#[instrument(level = Level::TRACE, skip_all, name = "apply code generation")]
2447fn process_content_with_code_gens(
2448 program: &mut Program,
2449 globals: &Globals,
2450 trie: Option<&AstPathTrie>,
2451 code_gens: &mut Vec<CodeGeneration>,
2452) -> usize {
2453 let mut visitors = Vec::new();
2454 let mut root_visitors = Vec::new();
2455 let mut early_hoisted_stmts = FxIndexMap::default();
2456 let mut hoisted_stmts = FxIndexMap::default();
2457 let mut early_late_stmts = FxIndexMap::default();
2458 let mut late_stmts = FxIndexMap::default();
2459 for code_gen in code_gens {
2460 for CodeGenerationHoistedStmt { key, stmt } in code_gen.hoisted_stmts.drain(..) {
2461 hoisted_stmts.entry(key).or_insert(stmt);
2462 }
2463 for CodeGenerationHoistedStmt { key, stmt } in code_gen.early_hoisted_stmts.drain(..) {
2464 early_hoisted_stmts.insert(key.clone(), stmt);
2465 }
2466 for CodeGenerationHoistedStmt { key, stmt } in code_gen.late_stmts.drain(..) {
2467 late_stmts.insert(key.clone(), stmt);
2468 }
2469 for CodeGenerationHoistedStmt { key, stmt } in code_gen.early_late_stmts.drain(..) {
2470 early_late_stmts.insert(key.clone(), stmt);
2471 }
2472 for (path, visitor) in &code_gen.visitors {
2473 if path.is_root() {
2474 root_visitors.push(&**visitor);
2475 } else {
2476 visitors.push((*path, &**visitor));
2477 }
2478 }
2479 }
2480
2481 GLOBALS.set(globals, || {
2482 if !visitors.is_empty() {
2483 let trie = trie.expect("code gens with visitors always come with their trie");
2484 let visitors = Visitors::new(trie, visitors);
2485 if !visitors.is_empty() {
2486 program.visit_mut_with_ast_path(
2487 &mut ApplyVisitors::new(&visitors),
2488 &mut Default::default(),
2489 );
2490 }
2491 }
2492 for pass in root_visitors {
2493 program.modify(pass);
2494 }
2495 });
2496
2497 let early_hoisted_count = early_hoisted_stmts.len();
2498 match program {
2499 Program::Module(ast::Module { body, .. }) => {
2500 body.splice(
2501 0..0,
2502 early_hoisted_stmts
2503 .into_values()
2504 .chain(hoisted_stmts.into_values())
2505 .map(ModuleItem::Stmt),
2506 );
2507 body.extend(
2508 early_late_stmts
2509 .into_values()
2510 .chain(late_stmts.into_values())
2511 .map(ModuleItem::Stmt),
2512 );
2513 }
2514 Program::Script(Script { body, .. }) => {
2515 body.splice(
2516 0..0,
2517 early_hoisted_stmts
2518 .into_values()
2519 .chain(hoisted_stmts.into_values()),
2520 );
2521 body.extend(
2522 early_late_stmts
2523 .into_values()
2524 .chain(late_stmts.into_values()),
2525 );
2526 }
2527 };
2528 early_hoisted_count
2529}
2530
2531fn hygiene_rename_only(
2538 top_level_mark: Option<Mark>,
2539 is_import_mark: Mark,
2540 preserved_exports: &FxHashSet<Id>,
2541) -> impl VisitMut {
2542 struct HygieneRenamer<'a> {
2543 preserved_exports: &'a FxHashSet<Id>,
2544 is_import_mark: Mark,
2545 }
2546 impl swc_core::ecma::transforms::base::rename::Renamer for HygieneRenamer<'_> {
2548 type Target = Id;
2549
2550 const MANGLE: bool = false;
2551 const RESET_N: bool = true;
2552
2553 fn new_name_for(&self, orig: &Id, n: &mut usize) -> Atom {
2554 let res = if *n == 0 {
2555 orig.0.clone()
2556 } else {
2557 format!("{}{}", orig.0, n).into()
2558 };
2559 *n += 1;
2560 res
2561 }
2562
2563 fn preserve_name(&self, orig: &Id) -> bool {
2564 self.preserved_exports.contains(orig) || orig.1.has_mark(self.is_import_mark)
2565 }
2566 }
2567 swc_core::ecma::transforms::base::rename::renamer_keep_contexts(
2568 swc_core::ecma::transforms::base::hygiene::Config {
2569 top_level_mark: top_level_mark.unwrap_or_default(),
2570 ..Default::default()
2571 },
2572 HygieneRenamer {
2573 preserved_exports,
2574 is_import_mark,
2575 },
2576 )
2577}
2578
2579#[derive(Default)]
2580enum CodeGenResultSourceMap {
2581 #[default]
2582 None,
2584 Single {
2585 source_map: Arc<SourceMap>,
2586 },
2587 ScopeHoisting {
2588 modules_header_width: u32,
2591 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2592 source_maps: Vec<CodeGenResultSourceMap>,
2593 },
2594}
2595
2596impl CodeGenResultSourceMap {
2597 fn is_some(&self) -> bool {
2598 match self {
2599 CodeGenResultSourceMap::None => false,
2600 CodeGenResultSourceMap::Single { .. }
2601 | CodeGenResultSourceMap::ScopeHoisting { .. } => true,
2602 }
2603 }
2604}
2605
2606impl Debug for CodeGenResultSourceMap {
2607 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2608 match self {
2609 CodeGenResultSourceMap::None => write!(f, "CodeGenResultSourceMap::None"),
2610 CodeGenResultSourceMap::Single { source_map } => {
2611 write!(
2612 f,
2613 "CodeGenResultSourceMap::Single {{ source_map: {:?} }}",
2614 source_map.files().clone()
2615 )
2616 }
2617 CodeGenResultSourceMap::ScopeHoisting {
2618 modules_header_width,
2619 source_maps,
2620 ..
2621 } => write!(
2622 f,
2623 "CodeGenResultSourceMap::ScopeHoisting {{ modules_header_width: \
2624 {modules_header_width}, source_maps: {source_maps:?} }}",
2625 ),
2626 }
2627 }
2628}
2629
2630impl Files for CodeGenResultSourceMap {
2631 fn try_lookup_source_file(
2632 &self,
2633 pos: BytePos,
2634 ) -> Result<Option<Arc<SourceFile>>, SourceMapLookupError> {
2635 match self {
2636 CodeGenResultSourceMap::None => Ok(None),
2637 CodeGenResultSourceMap::Single { source_map } => source_map.try_lookup_source_file(pos),
2638 CodeGenResultSourceMap::ScopeHoisting {
2639 modules_header_width,
2640 lookup_table,
2641 source_maps,
2642 } => {
2643 let (module, pos) =
2644 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2645 source_maps[module].try_lookup_source_file(pos)
2646 }
2647 }
2648 }
2649
2650 fn is_in_file(&self, f: &Arc<SourceFile>, raw_pos: BytePos) -> bool {
2651 match self {
2652 CodeGenResultSourceMap::None => false,
2653 CodeGenResultSourceMap::Single { .. } => f.start_pos <= raw_pos && raw_pos < f.end_pos,
2654 CodeGenResultSourceMap::ScopeHoisting { .. } => {
2655 false
2661 }
2662 }
2663 }
2664
2665 fn map_raw_pos(&self, pos: BytePos) -> BytePos {
2666 match self {
2667 CodeGenResultSourceMap::None => BytePos::DUMMY,
2668 CodeGenResultSourceMap::Single { .. } => pos,
2669 CodeGenResultSourceMap::ScopeHoisting {
2670 modules_header_width,
2671 lookup_table,
2672 ..
2673 } => CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table).1,
2674 }
2675 }
2676}
2677
2678impl SourceMapper for CodeGenResultSourceMap {
2679 fn lookup_char_pos(&self, pos: BytePos) -> Loc {
2680 match self {
2681 CodeGenResultSourceMap::None => {
2682 panic!("CodeGenResultSourceMap::None cannot lookup_char_pos")
2683 }
2684 CodeGenResultSourceMap::Single { source_map } => source_map.lookup_char_pos(pos),
2685 CodeGenResultSourceMap::ScopeHoisting {
2686 modules_header_width,
2687 lookup_table,
2688 source_maps,
2689 } => {
2690 let (module, pos) =
2691 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2692 source_maps[module].lookup_char_pos(pos)
2693 }
2694 }
2695 }
2696 fn span_to_lines(&self, sp: Span) -> FileLinesResult {
2697 match self {
2698 CodeGenResultSourceMap::None => {
2699 panic!("CodeGenResultSourceMap::None cannot span_to_lines")
2700 }
2701 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_lines(sp),
2702 CodeGenResultSourceMap::ScopeHoisting {
2703 modules_header_width,
2704 lookup_table,
2705 source_maps,
2706 } => {
2707 let (module, lo) = CodeGenResultComments::decode_bytepos(
2708 *modules_header_width,
2709 sp.lo,
2710 lookup_table,
2711 );
2712 source_maps[module].span_to_lines(Span {
2713 lo,
2714 hi: CodeGenResultComments::decode_bytepos(
2715 *modules_header_width,
2716 sp.hi,
2717 lookup_table,
2718 )
2719 .1,
2720 })
2721 }
2722 }
2723 }
2724 fn span_to_string(&self, sp: Span) -> String {
2725 match self {
2726 CodeGenResultSourceMap::None => {
2727 panic!("CodeGenResultSourceMap::None cannot span_to_string")
2728 }
2729 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_string(sp),
2730 CodeGenResultSourceMap::ScopeHoisting {
2731 modules_header_width,
2732 lookup_table,
2733 source_maps,
2734 } => {
2735 let (module, lo) = CodeGenResultComments::decode_bytepos(
2736 *modules_header_width,
2737 sp.lo,
2738 lookup_table,
2739 );
2740 source_maps[module].span_to_string(Span {
2741 lo,
2742 hi: CodeGenResultComments::decode_bytepos(
2743 *modules_header_width,
2744 sp.hi,
2745 lookup_table,
2746 )
2747 .1,
2748 })
2749 }
2750 }
2751 }
2752 fn span_to_filename(&self, sp: Span) -> Arc<FileName> {
2753 match self {
2754 CodeGenResultSourceMap::None => {
2755 panic!("CodeGenResultSourceMap::None cannot span_to_filename")
2756 }
2757 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_filename(sp),
2758 CodeGenResultSourceMap::ScopeHoisting {
2759 modules_header_width,
2760 lookup_table,
2761 source_maps,
2762 } => {
2763 let (module, lo) = CodeGenResultComments::decode_bytepos(
2764 *modules_header_width,
2765 sp.lo,
2766 lookup_table,
2767 );
2768 source_maps[module].span_to_filename(Span {
2769 lo,
2770 hi: CodeGenResultComments::decode_bytepos(
2771 *modules_header_width,
2772 sp.hi,
2773 lookup_table,
2774 )
2775 .1,
2776 })
2777 }
2778 }
2779 }
2780 fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> {
2781 match self {
2782 CodeGenResultSourceMap::None => {
2783 panic!("CodeGenResultSourceMap::None cannot merge_spans")
2784 }
2785 CodeGenResultSourceMap::Single { source_map } => source_map.merge_spans(sp_lhs, sp_rhs),
2786 CodeGenResultSourceMap::ScopeHoisting {
2787 modules_header_width,
2788 lookup_table,
2789 source_maps,
2790 } => {
2791 let (module_lhs, lo_lhs) = CodeGenResultComments::decode_bytepos(
2792 *modules_header_width,
2793 sp_lhs.lo,
2794 lookup_table,
2795 );
2796 let (module_rhs, lo_rhs) = CodeGenResultComments::decode_bytepos(
2797 *modules_header_width,
2798 sp_rhs.lo,
2799 lookup_table,
2800 );
2801 if module_lhs != module_rhs {
2802 return None;
2803 }
2804 source_maps[module_lhs].merge_spans(
2805 Span {
2806 lo: lo_lhs,
2807 hi: CodeGenResultComments::decode_bytepos(
2808 *modules_header_width,
2809 sp_lhs.hi,
2810 lookup_table,
2811 )
2812 .1,
2813 },
2814 Span {
2815 lo: lo_rhs,
2816 hi: CodeGenResultComments::decode_bytepos(
2817 *modules_header_width,
2818 sp_rhs.hi,
2819 lookup_table,
2820 )
2821 .1,
2822 },
2823 )
2824 }
2825 }
2826 }
2827 fn call_span_if_macro(&self, sp: Span) -> Span {
2828 match self {
2829 CodeGenResultSourceMap::None => {
2830 panic!("CodeGenResultSourceMap::None cannot call_span_if_macro")
2831 }
2832 CodeGenResultSourceMap::Single { source_map } => source_map.call_span_if_macro(sp),
2833 CodeGenResultSourceMap::ScopeHoisting {
2834 modules_header_width,
2835 lookup_table,
2836 source_maps,
2837 } => {
2838 let (module, lo) = CodeGenResultComments::decode_bytepos(
2839 *modules_header_width,
2840 sp.lo,
2841 lookup_table,
2842 );
2843 source_maps[module].call_span_if_macro(Span {
2844 lo,
2845 hi: CodeGenResultComments::decode_bytepos(
2846 *modules_header_width,
2847 sp.hi,
2848 lookup_table,
2849 )
2850 .1,
2851 })
2852 }
2853 }
2854 }
2855 fn doctest_offset_line(&self, _line: usize) -> usize {
2856 panic!("doctest_offset_line is not implemented for CodeGenResultSourceMap");
2857 }
2858 fn span_to_snippet(&self, sp: Span) -> Result<String, Box<SpanSnippetError>> {
2859 match self {
2860 CodeGenResultSourceMap::None => Err(Box::new(SpanSnippetError::SourceNotAvailable {
2861 filename: FileName::Anon,
2862 })),
2863 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_snippet(sp),
2864 CodeGenResultSourceMap::ScopeHoisting {
2865 modules_header_width,
2866 lookup_table,
2867 source_maps,
2868 } => {
2869 let (module, lo) = CodeGenResultComments::decode_bytepos(
2870 *modules_header_width,
2871 sp.lo,
2872 lookup_table,
2873 );
2874 source_maps[module].span_to_snippet(Span {
2875 lo,
2876 hi: CodeGenResultComments::decode_bytepos(
2877 *modules_header_width,
2878 sp.hi,
2879 lookup_table,
2880 )
2881 .1,
2882 })
2883 }
2884 }
2885 }
2886 fn map_raw_pos(&self, pos: BytePos) -> BytePos {
2887 match self {
2888 CodeGenResultSourceMap::None => BytePos::DUMMY,
2889 CodeGenResultSourceMap::Single { .. } => pos,
2890 CodeGenResultSourceMap::ScopeHoisting {
2891 modules_header_width,
2892 lookup_table,
2893 ..
2894 } => CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table).1,
2895 }
2896 }
2897}
2898impl SourceMapperExt for CodeGenResultSourceMap {
2899 fn get_code_map(&self) -> &dyn SourceMapper {
2900 self
2901 }
2902}
2903
2904#[derive(Debug)]
2905enum CodeGenResultOriginalSourceMap {
2906 Single(Option<ResolvedVc<Box<dyn GenerateSourceMap>>>),
2907 ScopeHoisting(SmallVec<[ResolvedVc<Box<dyn GenerateSourceMap>>; 1]>),
2908}
2909
2910impl CodeGenResultOriginalSourceMap {
2911 fn iter(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn GenerateSourceMap>>> {
2912 match self {
2913 CodeGenResultOriginalSourceMap::Single(map) => Either::Left(map.iter().copied()),
2914 CodeGenResultOriginalSourceMap::ScopeHoisting(maps) => {
2915 Either::Right(maps.iter().copied())
2916 }
2917 }
2918 }
2919}
2920
2921struct ModulePosition(u32, u32);
2923
2924enum CodeGenResultComments {
2925 Single {
2926 comments: Either<ImmutableComments, Arc<ImmutableComments>>,
2927 extra_comments: SwcComments,
2928 },
2929 ScopeHoisting {
2930 modules_header_width: u32,
2933 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2934 comments: Vec<CodeGenResultComments>,
2935 },
2936 Empty,
2937}
2938
2939unsafe impl Send for CodeGenResultComments {}
2940unsafe impl Sync for CodeGenResultComments {}
2941
2942impl CodeGenResultComments {
2943 const CONTINUATION_BIT: u32 = 1 << 31;
2944 const SIGN_EXTENSION_BIT: u32 = 1 << 30;
2945
2946 #[inline]
2947 fn encode_bytepos_impl(
2948 modules_header_width: u32,
2949 module: u32,
2950 pos: BytePos,
2951 push_into_lookup: &mut impl FnMut(u32, u32) -> Result<u32>,
2952 ) -> Result<BytePos> {
2953 if pos.is_dummy() {
2954 return Ok(pos);
2956 }
2957
2958 let header_width = modules_header_width + 2;
2989 let pos_width = 32 - header_width;
2990
2991 let pos = pos.0;
2992
2993 let old_high_bits = pos >> pos_width;
2994 let high_bits_set = if (2u32.pow(header_width) - 1) == old_high_bits {
2995 true
2996 } else if old_high_bits == 0 {
2997 false
2998 } else {
2999 let ix = push_into_lookup(module, pos)?;
3003 assert_eq!(ix & CodeGenResultComments::CONTINUATION_BIT, 0);
3005
3006 return Ok(BytePos(ix | CodeGenResultComments::CONTINUATION_BIT));
3007 };
3008
3009 let pos = pos & !((2u32.pow(header_width) - 1) << pos_width);
3010 let encoded_high_bits = if high_bits_set {
3011 CodeGenResultComments::SIGN_EXTENSION_BIT
3012 } else {
3013 0
3014 };
3015 let encoded_module = module << pos_width;
3016
3017 Ok(BytePos(encoded_module | encoded_high_bits | pos))
3018 }
3019
3020 fn take(&mut self) -> Self {
3021 std::mem::replace(self, CodeGenResultComments::Empty)
3022 }
3023
3024 fn consumable(&self) -> CodeGenResultCommentsConsumable<'_> {
3025 match self {
3026 CodeGenResultComments::Single {
3027 comments,
3028 extra_comments,
3029 } => CodeGenResultCommentsConsumable::Single {
3030 comments: match comments {
3031 Either::Left(comments) => comments.consumable(),
3032 Either::Right(comments) => comments.consumable(),
3033 },
3034 extra_comments,
3035 },
3036 CodeGenResultComments::ScopeHoisting {
3037 modules_header_width,
3038 lookup_table,
3039 comments,
3040 } => CodeGenResultCommentsConsumable::ScopeHoisting {
3041 modules_header_width: *modules_header_width,
3042 lookup_table: lookup_table.clone(),
3043 comments: comments.iter().map(|c| c.consumable()).collect(),
3044 },
3045 CodeGenResultComments::Empty => CodeGenResultCommentsConsumable::Empty,
3046 }
3047 }
3048
3049 fn encode_bytepos(
3050 modules_header_width: u32,
3051 module: u32,
3052 pos: BytePos,
3053 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
3054 ) -> Result<BytePos> {
3055 let mut push = |module: u32, pos_u32: u32| -> Result<u32> {
3056 let mut lookup_table = lookup_table
3057 .lock()
3058 .map_err(|_| anyhow!("Failed to grab lock on the index map for byte positions"))?;
3059 let ix = lookup_table.len() as u32;
3060 if ix >= 1 << 30 {
3061 bail!("Too many byte positions being stored");
3062 }
3063 lookup_table.push(ModulePosition(module, pos_u32));
3064 Ok(ix)
3065 };
3066 Self::encode_bytepos_impl(modules_header_width, module, pos, &mut push)
3067 }
3068
3069 fn encode_bytepos_with_vec(
3070 modules_header_width: u32,
3071 module: u32,
3072 pos: BytePos,
3073 lookup_table: &mut Vec<ModulePosition>,
3074 ) -> Result<BytePos> {
3075 let mut push = |module: u32, pos_u32: u32| -> Result<u32> {
3076 let ix = lookup_table.len() as u32;
3077 if ix >= 1 << 30 {
3078 bail!("Too many byte positions being stored");
3079 }
3080 lookup_table.push(ModulePosition(module, pos_u32));
3081 Ok(ix)
3082 };
3083 Self::encode_bytepos_impl(modules_header_width, module, pos, &mut push)
3084 }
3085
3086 fn decode_bytepos(
3087 modules_header_width: u32,
3088 pos: BytePos,
3089 lookup_table: &Mutex<Vec<ModulePosition>>,
3090 ) -> (usize, BytePos) {
3091 if pos.is_dummy() {
3092 panic!("Cannot decode dummy BytePos");
3094 }
3095
3096 let header_width = modules_header_width + 2;
3097 let pos_width = 32 - header_width;
3098
3099 if (CodeGenResultComments::CONTINUATION_BIT & pos.0)
3100 == CodeGenResultComments::CONTINUATION_BIT
3101 {
3102 let lookup_table = lookup_table
3103 .lock()
3104 .expect("Failed to grab lock on the index map for byte position");
3105 let ix = pos.0 & !CodeGenResultComments::CONTINUATION_BIT;
3106 let ModulePosition(module, pos) = lookup_table[ix as usize];
3107
3108 return (module as usize, BytePos(pos));
3109 }
3110
3111 let high_bits_set = pos.0 >> 30 & 1 == 1;
3112 let module = (pos.0 << 2) >> (pos_width + 2);
3113 let pos = pos.0 & !((2u32.pow(header_width) - 1) << pos_width);
3114 let pos = if high_bits_set {
3115 pos | ((2u32.pow(header_width) - 1) << pos_width)
3116 } else {
3117 pos
3118 };
3119 (module as usize, BytePos(pos))
3120 }
3121}
3122
3123enum CodeGenResultCommentsConsumable<'a> {
3124 Single {
3125 comments: CowComments<'a>,
3126 extra_comments: &'a SwcComments,
3127 },
3128 ScopeHoisting {
3129 modules_header_width: u32,
3130 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
3131 comments: Vec<CodeGenResultCommentsConsumable<'a>>,
3132 },
3133 Empty,
3134}
3135fn encode_module_into_comment_span(
3139 modules_header_width: u32,
3140 module: usize,
3141 mut comment: Comment,
3142 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
3143) -> Comment {
3144 comment.span.lo = CodeGenResultComments::encode_bytepos(
3145 modules_header_width,
3146 module as u32,
3147 comment.span.lo,
3148 lookup_table.clone(),
3149 )
3150 .unwrap();
3151 comment.span.hi = CodeGenResultComments::encode_bytepos(
3152 modules_header_width,
3153 module as u32,
3154 comment.span.hi,
3155 lookup_table,
3156 )
3157 .unwrap();
3158 comment
3159}
3160
3161impl Comments for CodeGenResultCommentsConsumable<'_> {
3162 fn add_leading(&self, _pos: BytePos, _cmt: Comment) {
3163 unimplemented!("add_leading")
3164 }
3165
3166 fn add_leading_comments(&self, _pos: BytePos, _comments: Vec<Comment>) {
3167 unimplemented!("add_leading_comments")
3168 }
3169
3170 fn has_leading(&self, pos: BytePos) -> bool {
3171 if pos.is_dummy() {
3172 return false;
3173 }
3174 match self {
3175 Self::Single {
3176 comments,
3177 extra_comments,
3178 } => comments.has_leading(pos) || extra_comments.has_leading(pos),
3179 Self::ScopeHoisting {
3180 modules_header_width,
3181 lookup_table,
3182 comments,
3183 } => {
3184 let (module, pos) =
3185 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
3186 comments[module].has_leading(pos)
3187 }
3188 Self::Empty => false,
3189 }
3190 }
3191
3192 fn move_leading(&self, _from: BytePos, _to: BytePos) {
3193 unimplemented!("move_leading")
3194 }
3195
3196 fn take_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
3197 if pos.is_dummy() {
3198 return None;
3199 }
3200 match self {
3201 Self::Single {
3202 comments,
3203 extra_comments,
3204 } => merge_option_vec(comments.take_leading(pos), extra_comments.take_leading(pos)),
3205 Self::ScopeHoisting {
3206 modules_header_width,
3207 lookup_table,
3208 comments,
3209 } => {
3210 let (module, pos) =
3211 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
3212 comments[module].take_leading(pos).map(|comments| {
3213 comments
3214 .into_iter()
3215 .map(|c| {
3216 encode_module_into_comment_span(
3217 *modules_header_width,
3218 module,
3219 c,
3220 lookup_table.clone(),
3221 )
3222 })
3223 .collect()
3224 })
3225 }
3226 Self::Empty => None,
3227 }
3228 }
3229
3230 fn get_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
3231 if pos.is_dummy() {
3232 return None;
3233 }
3234 match self {
3235 Self::Single {
3236 comments,
3237 extra_comments,
3238 } => merge_option_vec(comments.get_leading(pos), extra_comments.get_leading(pos)),
3239 Self::ScopeHoisting {
3240 modules_header_width,
3241 lookup_table,
3242 comments,
3243 } => {
3244 let (module, pos) =
3245 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
3246 comments[module].get_leading(pos).map(|comments| {
3247 comments
3248 .into_iter()
3249 .map(|c| {
3250 encode_module_into_comment_span(
3251 *modules_header_width,
3252 module,
3253 c,
3254 lookup_table.clone(),
3255 )
3256 })
3257 .collect()
3258 })
3259 }
3260 Self::Empty => None,
3261 }
3262 }
3263
3264 fn add_trailing(&self, _pos: BytePos, _cmt: Comment) {
3265 unimplemented!("add_trailing")
3266 }
3267
3268 fn add_trailing_comments(&self, _pos: BytePos, _comments: Vec<Comment>) {
3269 unimplemented!("add_trailing_comments")
3270 }
3271
3272 fn has_trailing(&self, pos: BytePos) -> bool {
3273 if pos.is_dummy() {
3274 return false;
3275 }
3276 match self {
3277 Self::Single {
3278 comments,
3279 extra_comments,
3280 } => comments.has_trailing(pos) || extra_comments.has_trailing(pos),
3281 Self::ScopeHoisting {
3282 modules_header_width,
3283 lookup_table,
3284 comments,
3285 } => {
3286 let (module, pos) =
3287 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
3288 comments[module].has_trailing(pos)
3289 }
3290 Self::Empty => false,
3291 }
3292 }
3293
3294 fn move_trailing(&self, _from: BytePos, _to: BytePos) {
3295 unimplemented!("move_trailing")
3296 }
3297
3298 fn take_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
3299 if pos.is_dummy() {
3300 return None;
3301 }
3302 match self {
3303 Self::Single {
3304 comments,
3305 extra_comments,
3306 } => merge_option_vec(
3307 comments.take_trailing(pos),
3308 extra_comments.take_trailing(pos),
3309 ),
3310 Self::ScopeHoisting {
3311 modules_header_width,
3312 lookup_table,
3313 comments,
3314 } => {
3315 let (module, pos) =
3316 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
3317 comments[module].take_trailing(pos).map(|comments| {
3318 comments
3319 .into_iter()
3320 .map(|c| {
3321 encode_module_into_comment_span(
3322 *modules_header_width,
3323 module,
3324 c,
3325 lookup_table.clone(),
3326 )
3327 })
3328 .collect()
3329 })
3330 }
3331 Self::Empty => None,
3332 }
3333 }
3334
3335 fn get_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
3336 if pos.is_dummy() {
3337 return None;
3338 }
3339 match self {
3340 Self::Single {
3341 comments,
3342 extra_comments,
3343 } => merge_option_vec(comments.get_leading(pos), extra_comments.get_leading(pos)),
3344 Self::ScopeHoisting {
3345 modules_header_width,
3346 lookup_table,
3347 comments,
3348 } => {
3349 let (module, pos) =
3350 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
3351 comments[module].get_leading(pos).map(|comments| {
3352 comments
3353 .into_iter()
3354 .map(|c| {
3355 encode_module_into_comment_span(
3356 *modules_header_width,
3357 module,
3358 c,
3359 lookup_table.clone(),
3360 )
3361 })
3362 .collect()
3363 })
3364 }
3365 Self::Empty => None,
3366 }
3367 }
3368
3369 fn add_pure_comment(&self, _pos: BytePos) {
3370 unimplemented!("add_pure_comment")
3371 }
3372}
3373
3374fn merge_option_vec<T>(a: Option<Vec<T>>, b: Option<Vec<T>>) -> Option<Vec<T>> {
3375 match (a, b) {
3376 (Some(a), Some(b)) => Some(a.into_iter().chain(b).collect()),
3377 (Some(a), None) => Some(a),
3378 (None, Some(b)) => Some(b),
3379 (None, None) => None,
3380 }
3381}
3382
3383#[cfg(test)]
3384mod tests {
3385 use super::*;
3386 fn bytepos_ensure_identical(modules_header_width: u32, pos: BytePos) {
3387 let module_count = 2u32.pow(modules_header_width);
3388 let lookup_table = Arc::new(Mutex::new(Vec::new()));
3389
3390 for module in [
3391 0,
3392 1,
3393 2,
3394 module_count / 2,
3395 module_count.wrapping_sub(5),
3396 module_count.wrapping_sub(1),
3397 ]
3398 .into_iter()
3399 .filter(|&m| m < module_count)
3400 {
3401 let encoded = CodeGenResultComments::encode_bytepos(
3402 modules_header_width,
3403 module,
3404 pos,
3405 lookup_table.clone(),
3406 )
3407 .unwrap();
3408 let (decoded_module, decoded_pos) =
3409 CodeGenResultComments::decode_bytepos(modules_header_width, encoded, &lookup_table);
3410 assert_eq!(
3411 decoded_module as u32, module,
3412 "Testing width {modules_header_width} and pos {pos:?}"
3413 );
3414 assert_eq!(
3415 decoded_pos, pos,
3416 "Testing width {modules_header_width} and pos {pos:?}"
3417 );
3418 }
3419 }
3420
3421 #[test]
3422 fn test_encode_decode_bytepos_format() {
3423 let table = Arc::new(Mutex::new(Vec::new()));
3424
3425 for (pos, module, modules_header_width, result) in [
3426 (
3427 0b00000000000000000000000000000101,
3428 0b1,
3429 1,
3430 0b00100000000000000000000000000101,
3431 ),
3432 (
3433 0b00000000000000000000000000000101,
3434 0b01,
3435 2,
3436 0b00010000000000000000000000000101,
3437 ),
3438 (
3439 0b11111111111111110000000000000101,
3440 0b0110,
3441 4,
3442 0b01011011111111110000000000000101,
3443 ),
3444 (
3445 BytePos::PLACEHOLDER.0,
3446 0b01111,
3447 5,
3448 0b01011111111111111111111111111101,
3449 ),
3450 (
3451 BytePos::PURE.0,
3452 0b01111,
3453 5,
3454 0b01011111111111111111111111111110,
3455 ),
3456 (
3457 BytePos::SYNTHESIZED.0,
3458 0b01111,
3459 5,
3460 0b01011111111111111111111111111111,
3461 ),
3462 (
3465 0b00000111111111110000000000000101,
3466 0b0001,
3467 4,
3468 0b10000000000000000000000000000000,
3469 ),
3470 (
3472 0b00000111111111110000000000111110,
3473 0b0001,
3474 4,
3475 0b10000000000000000000000000000001,
3476 ),
3477 (BytePos::DUMMY.0, 0b0001, 4, BytePos::DUMMY.0),
3479 ] {
3480 let encoded = CodeGenResultComments::encode_bytepos(
3481 modules_header_width,
3482 module,
3483 BytePos(pos),
3484 table.clone(),
3485 )
3486 .unwrap();
3487 assert_eq!(encoded.0, result);
3488
3489 if encoded.0 & CodeGenResultComments::CONTINUATION_BIT
3491 == CodeGenResultComments::CONTINUATION_BIT
3492 {
3493 let index = encoded.0 & !CodeGenResultComments::CONTINUATION_BIT;
3494 let ModulePosition(encoded_module, encoded_pos) =
3495 table.lock().unwrap()[index as usize];
3496 assert_eq!(encoded_module, module);
3497 assert_eq!(encoded_pos, pos);
3498 }
3499 }
3500 }
3501
3502 #[test]
3503 fn test_encode_decode_bytepos_lossless() {
3504 const DUMMY_RESERVE: u32 = u32::MAX - 2_u32.pow(16);
3506
3507 for modules_header_width in 1..=10 {
3508 for pos in [
3509 BytePos(1),
3511 BytePos(2),
3512 BytePos(100),
3513 BytePos(4_000_000),
3514 BytePos(600_000_000),
3515 BytePos(u32::MAX - 3), BytePos::PLACEHOLDER,
3517 BytePos::SYNTHESIZED,
3518 BytePos::PURE,
3519 BytePos(DUMMY_RESERVE),
3520 BytePos(DUMMY_RESERVE + 10),
3521 BytePos(DUMMY_RESERVE + 10000),
3522 ] {
3523 bytepos_ensure_identical(modules_header_width, pos);
3524 }
3525 }
3526 }
3527}