@@ -2,7 +2,12 @@ use std::{fmt::Write, sync::Arc};
22
33use indexmap:: { IndexMap , IndexSet } ;
44use parcel_core:: * ;
5- use parcel_js_swc_core:: { Ast , async_module:: wrap_async_module, tree_shake:: tree_shake} ;
5+ use parcel_js_swc_core:: {
6+ Ast ,
7+ async_module:: wrap_async_module,
8+ script:: { link_script, minify_script} ,
9+ tree_shake:: tree_shake,
10+ } ;
611use swc_core:: {
712 common:: { DUMMY_SP , SyntaxContext } ,
813 ecma:: ast:: {
@@ -67,51 +72,27 @@ impl JsContent {
6772 const RUNTIME : & str = include_str ! ( concat!( env!( "OUT_DIR" ) , "/runtime.min.js" ) ) ;
6873 const DEV_RUNTIME : & str = include_str ! ( "../dev-runtime.js" ) ;
6974
70- if bundle. target . source_type == SourceType :: Script {
71- assert_eq ! ( bundle. assets. len( ) , 1 ) ;
72- let asset = & bundle_graph
73- . asset_graph
74- . asset ( bundle. main_entry_asset . unwrap ( ) ) ;
75- if let Some ( content) = asset. content . downcast_ref :: < JsContent > ( ) {
76- let should_optimize = bundle
77- . target
78- . flags
79- . contains ( EnvironmentFlags :: SHOULD_OPTIMIZE ) ;
80- let mut ast = content. ast . clone ( ) ;
81- insert_node_replacements ( & mut ast, content, asset, bundle, should_optimize) ;
82- insert_node_replacement_helpers (
83- & mut ast,
84- content,
85- bundle. target . output_format ,
86- should_optimize,
87- ) ;
88- let ( code, map) = content. to_code (
89- & ast,
90- bundle. target . source_map . is_some ( ) ,
91- bundle_source_root ( bundle, & options. project_root ) . as_deref ( ) ,
92- false ,
93- ) ?;
94- if let Some ( map) = map {
95- return Ok ( Arc :: new ( ContentWithSourceMap :: new ( code, map. into_bytes ( ) ) ) ) ;
96- }
97-
98- return Ok ( Arc :: new ( BufferContent :: new ( code) ) ) ;
99- }
100-
101- return Ok ( asset. content . clone ( ) ) ;
102- }
103-
10475 let should_build_source_map = bundle. target . source_map . is_some ( ) ;
10576 let should_optimize = bundle
10677 . target
10778 . flags
10879 . contains ( EnvironmentFlags :: SHOULD_OPTIMIZE ) ;
10980
81+ // A classic script's only asset runs unwrapped, so its top-level declarations are globals (see
82+ // `package_script`). What its dependencies resolve to goes in the module table before it.
83+ let script = if bundle. target . source_type == SourceType :: Script {
84+ assert_eq ! ( bundle. assets. len( ) , 1 ) ;
85+ Some ( bundle. assets [ 0 ] )
86+ } else {
87+ None
88+ } ;
89+
11090 // Resolve dependencies before writing the bundle preamble. Some dependency
11191 // resolutions, such as synchronous bundles in ESM output, must be emitted
11292 // as top-level imports before the module table.
11393 let mut synthetic_assets = IndexSet :: new ( ) ;
11494 let mut asset_plans = Vec :: with_capacity ( bundle. assets . len ( ) ) ;
95+ let mut script_dependencies = IndexMap :: new ( ) ;
11596 for asset_index in & bundle. assets {
11697 let asset = & bundle_graph. asset_graph . asset ( * asset_index) ;
11798 let dependencies = asset_dependencies (
@@ -123,9 +104,32 @@ impl JsContent {
123104 get_inline_bundle_content,
124105 & options. project_root ,
125106 ) ?;
126- asset_plans. push ( ( * asset_index, dependencies) ) ;
107+ if script == Some ( * asset_index) {
108+ script_dependencies = dependencies;
109+ } else {
110+ asset_plans. push ( ( * asset_index, dependencies) ) ;
111+ }
127112 }
128113
114+ let script = match script {
115+ Some ( asset_index) => {
116+ let asset = bundle_graph. asset_graph . asset ( asset_index) ;
117+ let Some ( content) = asset. content . downcast_ref :: < JsContent > ( ) else {
118+ return Ok ( asset. content . clone ( ) ) ;
119+ } ;
120+ let script = package_script ( content, asset, bundle, script_dependencies, options) ?;
121+ // Without a module table, the script is the whole bundle.
122+ if synthetic_assets. is_empty ( ) {
123+ return Ok ( match script. 1 {
124+ Some ( map) => Arc :: new ( ContentWithSourceMap :: new ( script. 0 , map. into_bytes ( ) ) ) ,
125+ None => Arc :: new ( BufferContent :: new ( script. 0 ) ) ,
126+ } ) ;
127+ }
128+ Some ( script)
129+ }
130+ None => None ,
131+ } ;
132+
129133 // Async modules are entered through the runtime's module evaluator, and async entries evaluated
130134 // by it (see `tla-runtime.js`).
131135 if bundle
@@ -160,7 +164,10 @@ impl JsContent {
160164 }
161165 }
162166
163- write_node_replacement_helpers ( & mut printer, bundle_graph, bundle, options) ?;
167+ // A script declares its own (see `package_script`).
168+ if script. is_none ( ) {
169+ write_node_replacement_helpers ( & mut printer, bundle_graph, bundle, options) ?;
170+ }
164171 let externals = write_external_imports ( & mut printer, bundle_graph, bundle) ?;
165172 let sync_bundles =
166173 write_sync_bundle_imports ( & mut printer, bundle_graph, bundle, & synthetic_assets) ?;
@@ -263,10 +270,50 @@ impl JsContent {
263270 if is_global {
264271 printer. write_str ( "\n })();" ) ?;
265272 }
273+ if let Some ( ( code, map) ) = script {
274+ printer. newline ( ) ?;
275+ printer. add_source_map ( map) ?;
276+ printer. write_str ( std:: str:: from_utf8 ( & code) . unwrap ( ) ) ?;
277+ }
266278 printer. into_content ( )
267279 }
268280}
269281
282+ /// Generates a classic script, linking its dependencies through the global require of the module
283+ /// table before it, and minified without renaming its top-level declarations, which are globals.
284+ fn package_script (
285+ content : & JsContent ,
286+ asset : & Asset ,
287+ bundle : & Bundle ,
288+ dependencies : IndexMap < String , Resolution < ' _ > > ,
289+ options : & ParcelOptions ,
290+ ) -> Result < ( Vec < u8 > , Option < String > ) , DiagnosticList > {
291+ let should_optimize = bundle
292+ . target
293+ . flags
294+ . contains ( EnvironmentFlags :: SHOULD_OPTIMIZE ) ;
295+ let mut ast = content. ast . clone ( ) ;
296+ insert_node_replacements ( & mut ast, content, asset, bundle, should_optimize) ;
297+ insert_node_replacement_helpers (
298+ & mut ast,
299+ content,
300+ bundle. target . output_format ,
301+ should_optimize,
302+ ) ;
303+ if !dependencies. is_empty ( ) {
304+ link_script ( & mut ast, dependencies, "parcelRequire" . into ( ) ) ;
305+ }
306+ if should_optimize {
307+ minify_script ( & mut ast) ;
308+ }
309+ content. to_code (
310+ & ast,
311+ bundle. target . source_map . is_some ( ) ,
312+ bundle_source_root ( bundle, & options. project_root ) . as_deref ( ) ,
313+ should_optimize,
314+ )
315+ }
316+
270317fn sync_bundle_binding ( bundle : & Bundle ) -> String {
271318 format ! ( "__parcelSyncBundle{:x}" , bundle. id)
272319}
@@ -837,7 +884,9 @@ fn write_runtime_globals(
837884 if let Some ( entry) = rsc_server_entry {
838885 write ! ( printer, "'{}'," , entry) ?;
839886 }
840- for entry in & bundle. entry_assets {
887+ // A classic script's entry runs after the module table rather than from it.
888+ let is_script = bundle. target . source_type == SourceType :: Script ;
889+ for entry in bundle. entry_assets . iter ( ) . filter ( |_| !is_script) {
841890 write ! (
842891 printer,
843892 "'{}'," ,
@@ -867,7 +916,7 @@ fn write_runtime_globals(
867916 if let Some ( main) = bundle
868917 . main_entry_asset
869918 . as_ref ( )
870- . filter ( |_| !bundle. entry_assets . is_empty ( ) )
919+ . filter ( |_| !bundle. entry_assets . is_empty ( ) && !is_script )
871920 {
872921 printer. write_var (
873922 runtime_main_entry,
0 commit comments