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