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