1#![allow(non_local_definitions)]
3#![feature(box_patterns)]
4#![feature(min_specialization)]
5#![feature(iter_intersperse)]
6#![feature(int_roundings)]
7#![feature(arbitrary_self_types)]
8#![feature(arbitrary_self_types_pointers)]
9#![recursion_limit = "256"]
10
11pub mod analyzer;
12pub mod annotations;
13pub mod async_chunk;
14pub mod chunk;
15pub mod code_gen;
16mod errors;
17pub mod magic_identifier;
18pub mod manifest;
19pub mod minify;
20pub mod parse;
21mod path_visitor;
22pub mod references;
23pub mod runtime_functions;
24pub mod side_effect_optimization;
25pub(crate) mod special_cases;
26pub(crate) mod static_code;
27mod swc_comments;
28pub mod text;
29pub(crate) mod transform;
30pub mod tree_shake;
31pub mod typescript;
32pub mod utils;
33pub mod webpack;
34pub mod worker_chunk;
35
36use std::{
37 fmt::{Display, Formatter},
38 mem::take,
39 sync::Arc,
40};
41
42use anyhow::Result;
43use chunk::EcmascriptChunkItem;
44use code_gen::{CodeGeneration, CodeGenerationHoistedStmt};
45use either::Either;
46use parse::{ParseResult, parse};
47use path_visitor::ApplyVisitors;
48use references::esm::UrlRewriteBehavior;
49pub use references::{AnalyzeEcmascriptModuleResult, TURBOPACK_HELPER};
50use serde::{Deserialize, Serialize};
51pub use static_code::StaticEcmascriptCode;
52use swc_core::{
53 common::{DUMMY_SP, GLOBALS, Globals, Mark, SourceMap, comments::Comments, util::take::Take},
54 ecma::{
55 ast::{self, Expr, ModuleItem, Program, Script},
56 codegen::{Emitter, text_writer::JsWriter},
57 visit::{VisitMutWith, VisitMutWithAstPath},
58 },
59 quote,
60};
61use tracing::Instrument;
62pub use transform::{
63 CustomTransformer, EcmascriptInputTransform, EcmascriptInputTransforms, TransformContext,
64 TransformPlugin, UnsupportedServerActionIssue,
65};
66use turbo_rcstr::RcStr;
67use turbo_tasks::{
68 FxIndexMap, NonLocalValue, ReadRef, ResolvedVc, TaskInput, TryJoinIterExt, Value,
69 ValueToString, Vc, trace::TraceRawVcs,
70};
71use turbo_tasks_fs::{FileJsonContent, FileSystemPath, glob::Glob, rope::Rope};
72use turbopack_core::{
73 asset::{Asset, AssetContent},
74 chunk::{
75 AsyncModuleInfo, ChunkItem, ChunkType, ChunkableModule, ChunkingContext, EvaluatableAsset,
76 },
77 compile_time_info::CompileTimeInfo,
78 context::AssetContext,
79 ident::AssetIdent,
80 module::{Module, OptionModule},
81 module_graph::ModuleGraph,
82 reference::ModuleReferences,
83 reference_type::InnerAssets,
84 resolve::{
85 FindContextFileResult, find_context_file, origin::ResolveOrigin, package_json,
86 parse::Request,
87 },
88 source::Source,
89 source_map::GenerateSourceMap,
90};
91pub use turbopack_resolve::ecmascript as resolve;
93
94use self::chunk::{EcmascriptChunkItemContent, EcmascriptChunkType, EcmascriptExports};
95use crate::{
96 chunk::{EcmascriptChunkPlaceable, placeable::is_marked_as_side_effect_free},
97 code_gen::{CodeGens, ModifiableAst},
98 parse::generate_js_source_map,
99 references::{
100 analyse_ecmascript_module, async_module::OptionAsyncModule, esm::base::EsmAssetReferences,
101 },
102 side_effect_optimization::reference::EcmascriptModulePartReference,
103 swc_comments::ImmutableComments,
104 transform::remove_shebang,
105};
106
107#[turbo_tasks::value(serialization = "auto_for_input")]
108#[derive(Hash, Debug, Clone, Copy, Default, TaskInput)]
109pub enum SpecifiedModuleType {
110 #[default]
111 Automatic,
112 CommonJs,
113 EcmaScript,
114}
115
116#[derive(
117 PartialOrd,
118 Ord,
119 PartialEq,
120 Eq,
121 Hash,
122 Debug,
123 Clone,
124 Copy,
125 Default,
126 Serialize,
127 Deserialize,
128 TraceRawVcs,
129 NonLocalValue,
130 TaskInput,
131)]
132#[serde(rename_all = "kebab-case")]
133pub enum TreeShakingMode {
134 #[default]
135 ModuleFragments,
136 ReexportsOnly,
137}
138
139#[turbo_tasks::value(transparent)]
140pub struct OptionTreeShaking(pub Option<TreeShakingMode>);
141
142#[turbo_tasks::value(shared, serialization = "auto_for_input")]
143#[derive(Hash, Debug, Default, Copy, Clone)]
144pub struct EcmascriptOptions {
145 pub refresh: bool,
146 pub tree_shaking_mode: Option<TreeShakingMode>,
148 pub specified_module_type: SpecifiedModuleType,
150 pub url_rewrite_behavior: Option<UrlRewriteBehavior>,
154 pub import_externals: bool,
157 pub ignore_dynamic_requests: bool,
161 pub extract_source_map: bool,
164 pub keep_last_successful_parse: bool,
168}
169
170#[turbo_tasks::value(serialization = "auto_for_input")]
171#[derive(Hash, Debug, Copy, Clone)]
172pub enum EcmascriptModuleAssetType {
173 Ecmascript,
175 Typescript {
177 tsx: bool,
179 analyze_types: bool,
181 },
182 TypescriptDeclaration,
184}
185
186impl Display for EcmascriptModuleAssetType {
187 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
188 match self {
189 EcmascriptModuleAssetType::Ecmascript => write!(f, "ecmascript"),
190 EcmascriptModuleAssetType::Typescript { tsx, analyze_types } => {
191 write!(f, "typescript")?;
192 if *tsx {
193 write!(f, "with JSX")?;
194 }
195 if *analyze_types {
196 write!(f, "with types")?;
197 }
198 Ok(())
199 }
200 EcmascriptModuleAssetType::TypescriptDeclaration => write!(f, "typescript declaration"),
201 }
202 }
203}
204
205#[turbo_tasks::function]
206fn modifier() -> Vc<RcStr> {
207 Vc::cell("ecmascript".into())
208}
209
210#[derive(Clone)]
211pub struct EcmascriptModuleAssetBuilder {
212 source: ResolvedVc<Box<dyn Source>>,
213 asset_context: ResolvedVc<Box<dyn AssetContext>>,
214 ty: EcmascriptModuleAssetType,
215 transforms: ResolvedVc<EcmascriptInputTransforms>,
216 options: ResolvedVc<EcmascriptOptions>,
217 compile_time_info: ResolvedVc<CompileTimeInfo>,
218 inner_assets: Option<ResolvedVc<InnerAssets>>,
219}
220
221impl EcmascriptModuleAssetBuilder {
222 pub fn with_inner_assets(mut self, inner_assets: ResolvedVc<InnerAssets>) -> Self {
223 self.inner_assets = Some(inner_assets);
224 self
225 }
226
227 pub fn with_type(mut self, ty: EcmascriptModuleAssetType) -> Self {
228 self.ty = ty;
229 self
230 }
231
232 pub fn build(self) -> Vc<EcmascriptModuleAsset> {
233 if let Some(inner_assets) = self.inner_assets {
234 EcmascriptModuleAsset::new_with_inner_assets(
235 *self.source,
236 *self.asset_context,
237 Value::new(self.ty),
238 *self.transforms,
239 *self.options,
240 *self.compile_time_info,
241 *inner_assets,
242 )
243 } else {
244 EcmascriptModuleAsset::new(
245 *self.source,
246 *self.asset_context,
247 Value::new(self.ty),
248 *self.transforms,
249 *self.options,
250 *self.compile_time_info,
251 )
252 }
253 }
254}
255
256#[turbo_tasks::value]
257pub struct EcmascriptModuleAsset {
258 pub source: ResolvedVc<Box<dyn Source>>,
259 pub asset_context: ResolvedVc<Box<dyn AssetContext>>,
260 pub ty: EcmascriptModuleAssetType,
261 pub transforms: ResolvedVc<EcmascriptInputTransforms>,
262 pub options: ResolvedVc<EcmascriptOptions>,
263 pub compile_time_info: ResolvedVc<CompileTimeInfo>,
264 pub inner_assets: Option<ResolvedVc<InnerAssets>>,
265 #[turbo_tasks(debug_ignore)]
266 last_successful_parse: turbo_tasks::TransientState<ReadRef<ParseResult>>,
267}
268impl core::fmt::Debug for EcmascriptModuleAsset {
269 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
270 f.debug_struct("EcmascriptModuleAsset")
271 .field("source", &self.source)
272 .field("asset_context", &self.asset_context)
273 .field("ty", &self.ty)
274 .field("transforms", &self.transforms)
275 .field("options", &self.options)
276 .field("compile_time_info", &self.compile_time_info)
277 .field("inner_assets", &self.inner_assets)
278 .finish()
279 }
280}
281
282#[turbo_tasks::value_trait]
283pub trait EcmascriptParsable {
284 fn failsafe_parse(self: Vc<Self>) -> Result<Vc<ParseResult>>;
285
286 fn parse_original(self: Vc<Self>) -> Result<Vc<ParseResult>>;
287
288 fn ty(self: Vc<Self>) -> Result<Vc<EcmascriptModuleAssetType>>;
289}
290
291#[turbo_tasks::value_trait]
292pub trait EcmascriptAnalyzable: Module + Asset {
293 fn analyze(self: Vc<Self>) -> Vc<AnalyzeEcmascriptModuleResult>;
294
295 async fn module_content_without_analysis(
298 self: Vc<Self>,
299 generate_source_map: bool,
300 ) -> Result<Vc<EcmascriptModuleContent>>;
301
302 async fn module_content_options(
303 self: Vc<Self>,
304 module_graph: Vc<ModuleGraph>,
305 chunking_context: Vc<Box<dyn ChunkingContext>>,
306 async_module_info: Option<Vc<AsyncModuleInfo>>,
307 ) -> Result<Vc<EcmascriptModuleContentOptions>>;
308
309 async fn module_content(
310 self: Vc<Self>,
311 module_graph: Vc<ModuleGraph>,
312 chunking_context: Vc<Box<dyn ChunkingContext>>,
313 async_module_info: Option<Vc<AsyncModuleInfo>>,
314 ) -> Vc<EcmascriptModuleContent> {
315 EcmascriptModuleContent::new(self.module_content_options(
316 module_graph,
317 chunking_context,
318 async_module_info,
319 ))
320 }
321}
322
323impl EcmascriptModuleAsset {
324 pub fn builder(
325 source: ResolvedVc<Box<dyn Source>>,
326 asset_context: ResolvedVc<Box<dyn AssetContext>>,
327 transforms: ResolvedVc<EcmascriptInputTransforms>,
328 options: ResolvedVc<EcmascriptOptions>,
329 compile_time_info: ResolvedVc<CompileTimeInfo>,
330 ) -> EcmascriptModuleAssetBuilder {
331 EcmascriptModuleAssetBuilder {
332 source,
333 asset_context,
334 ty: EcmascriptModuleAssetType::Ecmascript,
335 transforms,
336 options,
337 compile_time_info,
338 inner_assets: None,
339 }
340 }
341}
342
343#[turbo_tasks::value]
344#[derive(Copy, Clone)]
345pub(crate) struct ModuleTypeResult {
346 pub module_type: SpecifiedModuleType,
347 pub referenced_package_json: Option<ResolvedVc<FileSystemPath>>,
348}
349
350#[turbo_tasks::value_impl]
351impl ModuleTypeResult {
352 #[turbo_tasks::function]
353 fn new(module_type: SpecifiedModuleType) -> Vc<Self> {
354 Self::cell(ModuleTypeResult {
355 module_type,
356 referenced_package_json: None,
357 })
358 }
359
360 #[turbo_tasks::function]
361 fn new_with_package_json(
362 module_type: SpecifiedModuleType,
363 package_json: ResolvedVc<FileSystemPath>,
364 ) -> Vc<Self> {
365 Self::cell(ModuleTypeResult {
366 module_type,
367 referenced_package_json: Some(package_json),
368 })
369 }
370}
371
372#[turbo_tasks::value_impl]
373impl EcmascriptParsable for EcmascriptModuleAsset {
374 #[turbo_tasks::function]
375 async fn failsafe_parse(self: Vc<Self>) -> Result<Vc<ParseResult>> {
376 let real_result = self.parse();
377 let this = self.await?;
378 if this.options.await?.keep_last_successful_parse {
379 let real_result_value = real_result.await?;
380 let result_value = if matches!(*real_result_value, ParseResult::Ok { .. }) {
381 this.last_successful_parse.set(real_result_value.clone());
382 real_result_value
383 } else {
384 let state_ref = this.last_successful_parse.get();
385 state_ref.as_ref().unwrap_or(&real_result_value).clone()
386 };
387 Ok(ReadRef::cell(result_value))
388 } else {
389 Ok(real_result)
390 }
391 }
392
393 #[turbo_tasks::function]
394 fn parse_original(self: Vc<Self>) -> Vc<ParseResult> {
395 self.failsafe_parse()
396 }
397
398 #[turbo_tasks::function]
399 fn ty(&self) -> Vc<EcmascriptModuleAssetType> {
400 self.ty.cell()
401 }
402}
403
404#[turbo_tasks::value_impl]
405impl EcmascriptAnalyzable for EcmascriptModuleAsset {
406 #[turbo_tasks::function]
407 fn analyze(self: Vc<Self>) -> Vc<AnalyzeEcmascriptModuleResult> {
408 analyse_ecmascript_module(self, None)
409 }
410
411 #[turbo_tasks::function]
414 async fn module_content_without_analysis(
415 self: Vc<Self>,
416 generate_source_map: bool,
417 ) -> Result<Vc<EcmascriptModuleContent>> {
418 let this = self.await?;
419
420 let parsed = self.parse();
421
422 Ok(EcmascriptModuleContent::new_without_analysis(
423 parsed,
424 self.ident(),
425 this.options.await?.specified_module_type,
426 generate_source_map,
427 ))
428 }
429
430 #[turbo_tasks::function]
431 async fn module_content_options(
432 self: Vc<Self>,
433 module_graph: ResolvedVc<ModuleGraph>,
434 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
435 async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
436 ) -> Result<Vc<EcmascriptModuleContentOptions>> {
437 let parsed = self.parse().to_resolved().await?;
438
439 let analyze = self.analyze();
440 let analyze_ref = analyze.await?;
441
442 let module_type_result = *self.determine_module_type().await?;
443 let generate_source_map = *chunking_context
444 .reference_module_source_maps(Vc::upcast(self))
445 .await?;
446
447 Ok(EcmascriptModuleContentOptions {
448 parsed,
449 ident: self.ident().to_resolved().await?,
450 specified_module_type: module_type_result.module_type,
451 module_graph,
452 chunking_context,
453 references: analyze.references().to_resolved().await?,
454 esm_references: analyze_ref.esm_references,
455 part_references: vec![],
456 code_generation: analyze_ref.code_generation,
457 async_module: analyze_ref.async_module,
458 generate_source_map,
459 original_source_map: analyze_ref.source_map,
460 exports: analyze_ref.exports,
461 async_module_info,
462 }
463 .cell())
464 }
465}
466
467#[turbo_tasks::function]
468async fn determine_module_type_for_directory(
469 context_path: Vc<FileSystemPath>,
470) -> Result<Vc<ModuleTypeResult>> {
471 let find_package_json =
472 find_context_file(context_path, package_json().resolve().await?).await?;
473 let FindContextFileResult::Found(package_json, _) = *find_package_json else {
474 return Ok(ModuleTypeResult::new(SpecifiedModuleType::Automatic));
475 };
476
477 if let FileJsonContent::Content(content) = &*package_json.read_json().await? {
479 if let Some(r#type) = content.get("type") {
480 return Ok(ModuleTypeResult::new_with_package_json(
481 match r#type.as_str() {
482 Some("module") => SpecifiedModuleType::EcmaScript,
483 Some("commonjs") => SpecifiedModuleType::CommonJs,
484 _ => SpecifiedModuleType::Automatic,
485 },
486 *package_json,
487 ));
488 }
489 }
490
491 Ok(ModuleTypeResult::new_with_package_json(
492 SpecifiedModuleType::Automatic,
493 *package_json,
494 ))
495}
496
497#[turbo_tasks::value_impl]
498impl EcmascriptModuleAsset {
499 #[turbo_tasks::function]
500 pub fn new(
501 source: ResolvedVc<Box<dyn Source>>,
502 asset_context: ResolvedVc<Box<dyn AssetContext>>,
503
504 ty: Value<EcmascriptModuleAssetType>,
505 transforms: ResolvedVc<EcmascriptInputTransforms>,
506 options: ResolvedVc<EcmascriptOptions>,
507 compile_time_info: ResolvedVc<CompileTimeInfo>,
508 ) -> Vc<Self> {
509 Self::cell(EcmascriptModuleAsset {
510 source,
511 asset_context,
512 ty: ty.into_value(),
513 transforms,
514 options,
515
516 compile_time_info,
517 inner_assets: None,
518 last_successful_parse: Default::default(),
519 })
520 }
521
522 #[turbo_tasks::function]
523 pub async fn new_with_inner_assets(
524 source: ResolvedVc<Box<dyn Source>>,
525 asset_context: ResolvedVc<Box<dyn AssetContext>>,
526 ty: Value<EcmascriptModuleAssetType>,
527 transforms: ResolvedVc<EcmascriptInputTransforms>,
528 options: ResolvedVc<EcmascriptOptions>,
529 compile_time_info: ResolvedVc<CompileTimeInfo>,
530 inner_assets: ResolvedVc<InnerAssets>,
531 ) -> Result<Vc<Self>> {
532 if inner_assets.await?.is_empty() {
533 Ok(Self::new(
534 *source,
535 *asset_context,
536 ty,
537 *transforms,
538 *options,
539 *compile_time_info,
540 ))
541 } else {
542 Ok(Self::cell(EcmascriptModuleAsset {
543 source,
544 asset_context,
545 ty: ty.into_value(),
546 transforms,
547 options,
548 compile_time_info,
549 inner_assets: Some(inner_assets),
550 last_successful_parse: Default::default(),
551 }))
552 }
553 }
554
555 #[turbo_tasks::function]
556 pub fn source(&self) -> Vc<Box<dyn Source>> {
557 *self.source
558 }
559
560 #[turbo_tasks::function]
561 pub fn analyze(self: Vc<Self>) -> Vc<AnalyzeEcmascriptModuleResult> {
562 analyse_ecmascript_module(self, None)
563 }
564
565 #[turbo_tasks::function]
566 pub fn options(&self) -> Vc<EcmascriptOptions> {
567 *self.options
568 }
569
570 #[turbo_tasks::function]
571 pub fn parse(&self) -> Vc<ParseResult> {
572 parse(*self.source, Value::new(self.ty), *self.transforms)
573 }
574}
575
576impl EcmascriptModuleAsset {
577 #[tracing::instrument(level = "trace", skip_all)]
578 pub(crate) async fn determine_module_type(self: Vc<Self>) -> Result<ReadRef<ModuleTypeResult>> {
579 let this = self.await?;
580
581 match this.options.await?.specified_module_type {
582 SpecifiedModuleType::EcmaScript => {
583 return ModuleTypeResult::new(SpecifiedModuleType::EcmaScript).await;
584 }
585 SpecifiedModuleType::CommonJs => {
586 return ModuleTypeResult::new(SpecifiedModuleType::CommonJs).await;
587 }
588 SpecifiedModuleType::Automatic => {}
589 }
590
591 determine_module_type_for_directory(
592 self.origin_path()
593 .resolve()
594 .await?
595 .parent()
596 .resolve()
597 .await?,
598 )
599 .await
600 }
601}
602
603#[turbo_tasks::value_impl]
604impl Module for EcmascriptModuleAsset {
605 #[turbo_tasks::function]
606 async fn ident(&self) -> Result<Vc<AssetIdent>> {
607 if let Some(inner_assets) = self.inner_assets {
608 let mut ident = self.source.ident().owned().await?;
609 for (name, asset) in inner_assets.await?.iter() {
610 ident.add_asset(
611 ResolvedVc::cell(name.to_string().into()),
612 asset.ident().to_resolved().await?,
613 );
614 }
615 ident.add_modifier(modifier().to_resolved().await?);
616 ident.layer = Some(self.asset_context.layer().to_resolved().await?);
617 Ok(AssetIdent::new(Value::new(ident)))
618 } else {
619 Ok(self
620 .source
621 .ident()
622 .with_modifier(modifier())
623 .with_layer(self.asset_context.layer()))
624 }
625 }
626
627 #[turbo_tasks::function]
628 async fn references(self: Vc<Self>) -> Result<Vc<ModuleReferences>> {
629 Ok(self.analyze().references())
630 }
631
632 #[turbo_tasks::function]
633 async fn is_self_async(self: Vc<Self>) -> Result<Vc<bool>> {
634 if let Some(async_module) = *self.get_async_module().await? {
635 Ok(async_module.is_self_async(self.references()))
636 } else {
637 Ok(Vc::cell(false))
638 }
639 }
640}
641
642#[turbo_tasks::value_impl]
643impl Asset for EcmascriptModuleAsset {
644 #[turbo_tasks::function]
645 fn content(&self) -> Vc<AssetContent> {
646 self.source.content()
647 }
648}
649
650#[turbo_tasks::value_impl]
651impl ChunkableModule for EcmascriptModuleAsset {
652 #[turbo_tasks::function]
653 fn as_chunk_item(
654 self: ResolvedVc<Self>,
655 module_graph: ResolvedVc<ModuleGraph>,
656 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
657 ) -> Vc<Box<dyn ChunkItem>> {
658 Vc::upcast(ModuleChunkItem::cell(ModuleChunkItem {
659 module: self,
660 module_graph,
661 chunking_context,
662 }))
663 }
664}
665
666#[turbo_tasks::value_impl]
667impl EcmascriptChunkPlaceable for EcmascriptModuleAsset {
668 #[turbo_tasks::function]
669 async fn get_exports(self: Vc<Self>) -> Result<Vc<EcmascriptExports>> {
670 Ok(*self.analyze().await?.exports)
671 }
672
673 #[turbo_tasks::function]
674 async fn get_async_module(self: Vc<Self>) -> Result<Vc<OptionAsyncModule>> {
675 Ok(*self.analyze().await?.async_module)
676 }
677
678 #[turbo_tasks::function]
679 async fn is_marked_as_side_effect_free(
680 self: Vc<Self>,
681 side_effect_free_packages: Vc<Glob>,
682 ) -> Result<Vc<bool>> {
683 let pkg_side_effect_free =
685 is_marked_as_side_effect_free(self.ident().path(), side_effect_free_packages);
686 Ok(if *pkg_side_effect_free.await? {
687 pkg_side_effect_free
688 } else {
689 Vc::cell(self.analyze().await?.has_side_effect_free_directive)
690 })
691 }
692}
693
694#[turbo_tasks::value_impl]
695impl EvaluatableAsset for EcmascriptModuleAsset {}
696
697#[turbo_tasks::value_impl]
698impl ResolveOrigin for EcmascriptModuleAsset {
699 #[turbo_tasks::function]
700 fn origin_path(&self) -> Vc<FileSystemPath> {
701 self.source.ident().path()
702 }
703
704 #[turbo_tasks::function]
705 fn asset_context(&self) -> Vc<Box<dyn AssetContext>> {
706 *self.asset_context
707 }
708
709 #[turbo_tasks::function]
710 async fn get_inner_asset(&self, request: Vc<Request>) -> Result<Vc<OptionModule>> {
711 Ok(Vc::cell(if let Some(inner_assets) = &self.inner_assets {
712 if let Some(request) = request.await?.request() {
713 inner_assets.await?.get(&request).copied()
714 } else {
715 None
716 }
717 } else {
718 None
719 }))
720 }
721}
722
723#[turbo_tasks::value]
724struct ModuleChunkItem {
725 module: ResolvedVc<EcmascriptModuleAsset>,
726 module_graph: ResolvedVc<ModuleGraph>,
727 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
728}
729
730#[turbo_tasks::value_impl]
731impl ChunkItem for ModuleChunkItem {
732 #[turbo_tasks::function]
733 fn asset_ident(&self) -> Vc<AssetIdent> {
734 self.module.ident()
735 }
736
737 #[turbo_tasks::function]
738 fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
739 *ResolvedVc::upcast(self.chunking_context)
740 }
741
742 #[turbo_tasks::function]
743 async fn ty(&self) -> Result<Vc<Box<dyn ChunkType>>> {
744 Ok(Vc::upcast(
745 Vc::<EcmascriptChunkType>::default().resolve().await?,
746 ))
747 }
748
749 #[turbo_tasks::function]
750 fn module(&self) -> Vc<Box<dyn Module>> {
751 *ResolvedVc::upcast(self.module)
752 }
753}
754
755#[turbo_tasks::value_impl]
756impl EcmascriptChunkItem for ModuleChunkItem {
757 #[turbo_tasks::function]
758 fn content(self: Vc<Self>) -> Vc<EcmascriptChunkItemContent> {
759 panic!("content() should not be called");
760 }
761
762 #[turbo_tasks::function]
763 async fn content_with_async_module_info(
764 self: Vc<Self>,
765 async_module_info: Option<Vc<AsyncModuleInfo>>,
766 ) -> Result<Vc<EcmascriptChunkItemContent>> {
767 let span = tracing::info_span!(
768 "code generation",
769 module = self.asset_ident().to_string().await?.to_string()
770 );
771 async {
772 let this = self.await?;
773 let async_module_options = this
774 .module
775 .get_async_module()
776 .module_options(async_module_info);
777
778 let content = this.module.module_content(
780 *this.module_graph,
781 *this.chunking_context,
782 async_module_info,
783 );
784
785 EcmascriptChunkItemContent::new(
786 content,
787 *this.chunking_context,
788 this.module.options(),
789 async_module_options,
790 )
791 .resolve()
792 .await
793 }
794 .instrument(span)
795 .await
796 }
797}
798
799#[turbo_tasks::value(shared)]
801pub struct EcmascriptModuleContent {
802 pub inner_code: Rope,
803 pub source_map: Option<Rope>,
804 pub is_esm: bool,
805 }
807
808#[turbo_tasks::value(shared)]
809#[derive(Clone, Debug, Hash, TaskInput)]
810pub struct EcmascriptModuleContentOptions {
811 parsed: ResolvedVc<ParseResult>,
812 ident: ResolvedVc<AssetIdent>,
813 specified_module_type: SpecifiedModuleType,
814 module_graph: ResolvedVc<ModuleGraph>,
815 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
816 references: ResolvedVc<ModuleReferences>,
817 esm_references: ResolvedVc<EsmAssetReferences>,
818 part_references: Vec<ResolvedVc<EcmascriptModulePartReference>>,
819 code_generation: ResolvedVc<CodeGens>,
820 async_module: ResolvedVc<OptionAsyncModule>,
821 generate_source_map: bool,
822 original_source_map: Option<ResolvedVc<Box<dyn GenerateSourceMap>>>,
823 exports: ResolvedVc<EcmascriptExports>,
824 async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
825}
826
827impl EcmascriptModuleContentOptions {
828 async fn merged_code_gens(&self) -> Result<Vec<CodeGeneration>> {
829 let EcmascriptModuleContentOptions {
830 parsed,
831 module_graph,
832 chunking_context,
833 references,
834 esm_references,
835 part_references,
836 code_generation,
837 async_module,
838 exports,
839 async_module_info,
840 ..
841 } = self;
842
843 async {
844 let additional_code_gens = [
845 if let Some(async_module) = &*async_module.await? {
846 Some(
847 async_module
848 .code_generation(
849 async_module_info.map(|info| *info),
850 **references,
851 **chunking_context,
852 )
853 .await?,
854 )
855 } else {
856 None
857 },
858 if let EcmascriptExports::EsmExports(exports) = *exports.await? {
859 Some(
860 exports
861 .code_generation(**module_graph, **chunking_context, Some(**parsed))
862 .await?,
863 )
864 } else {
865 None
866 },
867 ];
868
869 let esm_code_gens = esm_references
870 .await?
871 .iter()
872 .map(|r| r.code_generation(**chunking_context))
873 .try_join()
874 .await?;
875
876 let part_code_gens = part_references
877 .iter()
878 .map(|r| r.code_generation(**chunking_context))
879 .try_join()
880 .await?;
881
882 let code_gens = code_generation
883 .await?
884 .iter()
885 .map(|c| c.code_generation(**module_graph, **chunking_context))
886 .try_join()
887 .await?;
888
889 anyhow::Ok(
890 esm_code_gens
891 .into_iter()
892 .chain(part_code_gens.into_iter())
893 .chain(additional_code_gens.into_iter().flatten())
894 .chain(code_gens.into_iter())
895 .collect(),
896 )
897 }
898 .instrument(tracing::info_span!("precompute code generation"))
899 .await
900 }
901}
902
903#[turbo_tasks::value_impl]
904impl EcmascriptModuleContent {
905 #[turbo_tasks::function]
907 pub async fn new(input: Vc<EcmascriptModuleContentOptions>) -> Result<Vc<Self>> {
908 let input = input.await?;
909 let EcmascriptModuleContentOptions {
910 parsed,
911 ident,
912 specified_module_type,
913 generate_source_map,
914 original_source_map,
915 ..
916 } = &*input;
917 let code_gens = input.merged_code_gens().await?;
918 async {
919 let content = process_parse_result(
920 *parsed,
921 **ident,
922 *specified_module_type,
923 code_gens,
924 *generate_source_map,
925 *original_source_map,
926 )
927 .await?;
928 emit_content(content).await
929 }
930 .instrument(tracing::info_span!("gen content with code gens"))
931 .await
932 }
933
934 #[turbo_tasks::function]
936 pub async fn new_without_analysis(
937 parsed: Vc<ParseResult>,
938 ident: Vc<AssetIdent>,
939 specified_module_type: SpecifiedModuleType,
940 generate_source_map: bool,
941 ) -> Result<Vc<Self>> {
942 let content = process_parse_result(
943 parsed.to_resolved().await?,
944 ident,
945 specified_module_type,
946 vec![],
947 generate_source_map,
948 None,
949 )
950 .await?;
951 emit_content(content).await
952 }
953}
954
955struct CodeGenResult {
956 program: Program,
957 source_map: Arc<SourceMap>,
958 comments: Either<ImmutableComments, Arc<ImmutableComments>>,
959 is_esm: bool,
960 generate_source_map: bool,
961 original_source_map: Option<ResolvedVc<Box<dyn GenerateSourceMap>>>,
962}
963
964async fn process_parse_result(
965 parsed: ResolvedVc<ParseResult>,
966 ident: Vc<AssetIdent>,
967 specified_module_type: SpecifiedModuleType,
968 code_gens: Vec<CodeGeneration>,
969 generate_source_map: bool,
970 original_source_map: Option<ResolvedVc<Box<dyn GenerateSourceMap>>>,
971) -> Result<CodeGenResult> {
972 let parsed = parsed.final_read_hint().await?;
973
974 Ok(match &*parsed {
975 ParseResult::Ok { .. } => {
976 let mut parsed = ReadRef::try_unwrap(parsed);
979 let (mut program, source_map, globals, eval_context, comments) = match &mut parsed {
980 Ok(ParseResult::Ok {
981 program,
982 source_map,
983 globals,
984 eval_context,
985 comments,
986 }) => (
987 program.take(),
988 &*source_map,
989 &*globals,
990 &*eval_context,
991 match Arc::try_unwrap(take(comments)) {
992 Ok(comments) => Either::Left(comments),
993 Err(comments) => Either::Right(comments),
994 },
995 ),
996 Err(parsed) => {
997 let ParseResult::Ok {
998 program,
999 source_map,
1000 globals,
1001 eval_context,
1002 comments,
1003 } = &**parsed
1004 else {
1005 unreachable!();
1006 };
1007 (
1008 program.clone(),
1009 source_map,
1010 globals,
1011 eval_context,
1012 Either::Right(comments.clone()),
1013 )
1014 }
1015 _ => unreachable!(),
1016 };
1017 let top_level_mark = eval_context.top_level_mark;
1018 let is_esm = eval_context.is_esm(specified_module_type);
1019
1020 process_content_with_code_gens(&mut program, globals, Some(top_level_mark), code_gens);
1021
1022 CodeGenResult {
1023 program,
1024 source_map: source_map.clone(),
1025 comments,
1026 is_esm,
1027 generate_source_map,
1028 original_source_map,
1029 }
1030 }
1031 ParseResult::Unparseable { messages } => {
1032 let path = ident.path().to_string().await?;
1033 let error_messages = messages
1034 .as_ref()
1035 .and_then(|m| m.first().map(|f| format!("\n{f}")))
1036 .unwrap_or("".into());
1037 let msg = format!("Could not parse module '{path}'\n{error_messages}");
1038 let body = vec![
1039 quote!(
1040 "const e = new Error($msg);" as Stmt,
1041 msg: Expr = Expr::Lit(msg.into()),
1042 ),
1043 quote!("e.code = 'MODULE_UNPARSEABLE';" as Stmt),
1044 quote!("throw e;" as Stmt),
1045 ];
1046
1047 CodeGenResult {
1048 program: Program::Script(Script {
1049 span: DUMMY_SP,
1050 body,
1051 shebang: None,
1052 }),
1053 source_map: Arc::new(SourceMap::default()),
1054 comments: Either::Left(Default::default()),
1055 is_esm: false,
1056 generate_source_map: false,
1057 original_source_map: None,
1058 }
1059 }
1060 ParseResult::NotFound => {
1061 let path = ident.path().to_string().await?;
1062 let msg = format!("Could not parse module '{path}'");
1063 let body = vec![
1064 quote!(
1065 "const e = new Error($msg);" as Stmt,
1066 msg: Expr = Expr::Lit(msg.into()),
1067 ),
1068 quote!("e.code = 'MODULE_UNPARSEABLE';" as Stmt),
1069 quote!("throw e;" as Stmt),
1070 ];
1071 CodeGenResult {
1072 program: Program::Script(Script {
1073 span: DUMMY_SP,
1074 body,
1075 shebang: None,
1076 }),
1077 source_map: Arc::new(SourceMap::default()),
1078 comments: Either::Left(Default::default()),
1079 is_esm: false,
1080 generate_source_map: false,
1081 original_source_map: None,
1082 }
1083 }
1084 })
1085}
1086
1087async fn emit_content(content: CodeGenResult) -> Result<Vc<EcmascriptModuleContent>> {
1088 let CodeGenResult {
1089 program,
1090 source_map,
1091 comments,
1092 is_esm,
1093 generate_source_map,
1094 original_source_map,
1095 } = content;
1096
1097 let mut bytes: Vec<u8> = vec![];
1098 let mut mappings = vec![];
1102
1103 {
1104 let comments = match comments {
1105 Either::Left(comments) => Either::Left(comments.into_consumable()),
1106 Either::Right(ref comments) => Either::Right(comments.consumable()),
1107 };
1108 let comments: &dyn Comments = match &comments {
1109 Either::Left(comments) => comments,
1110 Either::Right(comments) => comments,
1111 };
1112
1113 let mut emitter = Emitter {
1114 cfg: swc_core::ecma::codegen::Config::default(),
1115 cm: source_map.clone(),
1116 comments: Some(&comments),
1117 wr: JsWriter::new(
1118 source_map.clone(),
1119 "\n",
1120 &mut bytes,
1121 generate_source_map.then_some(&mut mappings),
1122 ),
1123 };
1124
1125 emitter.emit_program(&program)?;
1126 drop(program);
1128 }
1129
1130 let source_map = if generate_source_map {
1131 if let Some(original_source_map) = original_source_map {
1132 Some(generate_js_source_map(
1133 source_map.clone(),
1134 mappings,
1135 original_source_map.generate_source_map().await?.as_ref(),
1136 true,
1137 )?)
1138 } else {
1139 Some(generate_js_source_map(
1140 source_map.clone(),
1141 mappings,
1142 None,
1143 true,
1144 )?)
1145 }
1146 } else {
1147 None
1148 };
1149
1150 Ok(EcmascriptModuleContent {
1151 inner_code: bytes.into(),
1152 source_map,
1153 is_esm,
1154 }
1155 .cell())
1156}
1157
1158fn process_content_with_code_gens(
1159 program: &mut Program,
1160 globals: &Globals,
1161 top_level_mark: Option<Mark>,
1162 mut code_gens: Vec<CodeGeneration>,
1163) {
1164 let mut visitors = Vec::new();
1165 let mut root_visitors = Vec::new();
1166 let mut early_hoisted_stmts = FxIndexMap::default();
1167 let mut hoisted_stmts = FxIndexMap::default();
1168 for code_gen in &mut code_gens {
1169 for CodeGenerationHoistedStmt { key, stmt } in code_gen.hoisted_stmts.drain(..) {
1170 hoisted_stmts.entry(key).or_insert(stmt);
1171 }
1172 for CodeGenerationHoistedStmt { key, stmt } in code_gen.early_hoisted_stmts.drain(..) {
1173 early_hoisted_stmts.insert(key.clone(), stmt);
1174 }
1175
1176 for (path, visitor) in &code_gen.visitors {
1177 if path.is_empty() {
1178 root_visitors.push(&**visitor);
1179 } else {
1180 visitors.push((path, &**visitor));
1181 }
1182 }
1183 }
1184
1185 GLOBALS.set(globals, || {
1186 if !visitors.is_empty() {
1187 program.visit_mut_with_ast_path(
1188 &mut ApplyVisitors::new(visitors),
1189 &mut Default::default(),
1190 );
1191 }
1192 for pass in root_visitors {
1193 program.modify(pass);
1194 }
1195 program.visit_mut_with(
1196 &mut swc_core::ecma::transforms::base::hygiene::hygiene_with_config(
1197 swc_core::ecma::transforms::base::hygiene::Config {
1198 top_level_mark: top_level_mark.unwrap_or_default(),
1199 ..Default::default()
1200 },
1201 ),
1202 );
1203 program.visit_mut_with(&mut swc_core::ecma::transforms::base::fixer::fixer(None));
1204
1205 remove_shebang(program);
1208 });
1209
1210 match program {
1211 Program::Module(ast::Module { body, .. }) => {
1212 body.splice(
1213 0..0,
1214 early_hoisted_stmts
1215 .into_values()
1216 .chain(hoisted_stmts.into_values())
1217 .map(ModuleItem::Stmt),
1218 );
1219 }
1220 Program::Script(Script { body, .. }) => {
1221 body.splice(
1222 0..0,
1223 early_hoisted_stmts
1224 .into_values()
1225 .chain(hoisted_stmts.into_values()),
1226 );
1227 }
1228 };
1229}
1230
1231pub fn register() {
1232 turbo_tasks::register();
1233 turbo_tasks_fs::register();
1234 turbopack_core::register();
1235 turbo_esregex::register();
1236 include!(concat!(env!("OUT_DIR"), "/register.rs"));
1237}