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