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