turbopack_ecmascript/
lib.rs

1// Needed for swc visit_ macros
2#![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};
91// TODO remove this
92pub 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    /// variant of tree shaking to use
147    pub tree_shaking_mode: Option<TreeShakingMode>,
148    /// module is forced to a specific type (happens e. g. for .cjs and .mjs)
149    pub specified_module_type: SpecifiedModuleType,
150    /// Determines how to treat `new URL(...)` rewrites.
151    /// This allows to construct url depends on the different building context,
152    /// e.g. SSR, CSR, or Node.js.
153    pub url_rewrite_behavior: Option<UrlRewriteBehavior>,
154    /// External imports should used `__turbopack_import__` instead of
155    /// `__turbopack_require__` and become async module references.
156    pub import_externals: bool,
157    /// Ignore very dynamic requests which doesn't have any static known part.
158    /// If false, they will reference the whole directory. If true, they won't
159    /// reference anything and lead to an runtime error instead.
160    pub ignore_dynamic_requests: bool,
161    /// If true, it reads a sourceMappingURL comment from the end of the file,
162    /// reads and generates a source map.
163    pub extract_source_map: bool,
164    /// If true, it stores the last successful parse result in state and keeps using it when
165    /// parsing fails. This is useful to keep the module graph structure intact when syntax errors
166    /// are temporarily introduced.
167    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    /// Module with EcmaScript code
174    Ecmascript,
175    /// Module with TypeScript code without types
176    Typescript {
177        // parse JSX syntax.
178        tsx: bool,
179        // follow references to imported types.
180        analyze_types: bool,
181    },
182    /// Module with TypeScript declaration code
183    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    /// Generates module contents without an analysis pass. This is useful for
296    /// transforming code that is not a module, e.g. runtime code.
297    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    /// Generates module contents without an analysis pass. This is useful for
412    /// transforming code that is not a module, e.g. runtime code.
413    #[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    // analysis.add_reference(PackageJsonReference::new(package_json));
478    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        // Check package.json first, so that we can skip parsing the module if it's marked that way.
684        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            // TODO check if we need to pass async_module_info at all
779            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/// The transformed contents of an Ecmascript module.
800#[turbo_tasks::value(shared)]
801pub struct EcmascriptModuleContent {
802    pub inner_code: Rope,
803    pub source_map: Option<Rope>,
804    pub is_esm: bool,
805    // pub refresh: bool,
806}
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    /// Creates a new [`Vc<EcmascriptModuleContent>`].
906    #[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    /// Creates a new [`Vc<EcmascriptModuleContent>`] without an analysis pass.
935    #[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            // We need a mutable version of the AST. We try to avoid cloning it by unwrapping the
977            // ReadRef.
978            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    // TODO: Insert this as a sourceless segment so that sourcemaps aren't affected.
1099    // = format!("/* {} */\n", self.module.path().to_string().await?).into_bytes();
1100
1101    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 the AST eagerly so we don't keep it in memory while generating source maps
1127        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        // we need to remove any shebang before bundling as it's only valid as the first
1206        // line in a js file (not in a chunk item wrapped in the runtime)
1207        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}