2025-01-01 04:12:39 +09:00
// Copyright 2018-2025 the Deno authors. MIT license.
2023-04-19 17:50:56 -04:00
2024-03-31 10:58:19 -04:00
use std ::borrow ::Cow ;
2025-01-19 14:23:07 -05:00
use std ::collections ::HashMap ;
2024-07-03 20:54:33 -04:00
use std ::collections ::VecDeque ;
2024-11-26 06:23:09 -08:00
use std ::env ;
2024-03-22 14:03:56 -07:00
use std ::ffi ::OsString ;
2024-02-13 21:52:30 +05:30
use std ::fs ;
2024-07-31 21:15:13 -07:00
use std ::fs ::File ;
2024-11-26 06:23:09 -08:00
use std ::path ::Component ;
2023-04-19 17:50:56 -04:00
use std ::path ::Path ;
2023-05-10 20:06:59 -04:00
use std ::path ::PathBuf ;
2023-04-19 17:50:56 -04:00
2025-01-19 14:23:07 -05:00
use capacity_builder ::BytesAppendable ;
2024-10-24 15:48:48 -04:00
use deno_ast ::MediaType ;
2024-11-01 12:27:00 -04:00
use deno_ast ::ModuleKind ;
2023-04-19 17:50:56 -04:00
use deno_ast ::ModuleSpecifier ;
2024-07-04 20:41:01 -04:00
use deno_config ::workspace ::WorkspaceResolver ;
2023-07-28 17:46:26 +02:00
use deno_core ::anyhow ::bail ;
2023-04-19 17:50:56 -04:00
use deno_core ::anyhow ::Context ;
use deno_core ::error ::AnyError ;
use deno_core ::serde_json ;
use deno_core ::url ::Url ;
2024-10-24 15:48:48 -04:00
use deno_graph ::ModuleGraph ;
2025-01-17 15:39:29 -05:00
use deno_lib ::args ::CaData ;
use deno_lib ::args ::UnstableConfig ;
use deno_lib ::shared ::ReleaseChannel ;
2025-01-19 14:23:07 -05:00
use deno_lib ::standalone ::binary ::CjsExportAnalysisEntry ;
2025-01-17 15:39:29 -05:00
use deno_lib ::standalone ::binary ::Metadata ;
use deno_lib ::standalone ::binary ::NodeModules ;
2025-01-19 14:23:07 -05:00
use deno_lib ::standalone ::binary ::RemoteModuleEntry ;
2025-01-17 15:39:29 -05:00
use deno_lib ::standalone ::binary ::SerializedResolverWorkspaceJsrPackage ;
use deno_lib ::standalone ::binary ::SerializedWorkspaceResolver ;
use deno_lib ::standalone ::binary ::SerializedWorkspaceResolverImportMap ;
2025-01-19 14:23:07 -05:00
use deno_lib ::standalone ::binary ::SpecifierDataStore ;
use deno_lib ::standalone ::binary ::SpecifierId ;
use deno_lib ::standalone ::binary ::MAGIC_BYTES ;
2025-01-17 15:39:29 -05:00
use deno_lib ::standalone ::virtual_fs ::BuiltVfs ;
use deno_lib ::standalone ::virtual_fs ::VfsBuilder ;
2025-01-15 09:35:46 -05:00
use deno_lib ::standalone ::virtual_fs ::VfsEntry ;
use deno_lib ::standalone ::virtual_fs ::VirtualDirectory ;
use deno_lib ::standalone ::virtual_fs ::VirtualDirectoryEntries ;
use deno_lib ::standalone ::virtual_fs ::WindowsSystemRootablePath ;
2025-01-17 15:39:29 -05:00
use deno_lib ::standalone ::virtual_fs ::DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME ;
use deno_lib ::util ::hash ::FastInsecureHasher ;
use deno_lib ::version ::DENO_VERSION_INFO ;
2024-10-24 15:48:48 -04:00
use deno_npm ::resolution ::SerializedNpmResolutionSnapshot ;
2023-05-17 17:38:50 -04:00
use deno_npm ::NpmSystemInfo ;
2024-12-12 13:07:35 -05:00
use deno_path_util ::url_from_directory_path ;
use deno_path_util ::url_to_file_path ;
2024-07-03 20:54:33 -04:00
use indexmap ::IndexMap ;
2025-01-19 14:23:07 -05:00
use node_resolver ::analyze ::CjsAnalysis ;
use node_resolver ::analyze ::CjsCodeAnalyzer ;
2023-04-19 17:50:56 -04:00
2024-12-31 12:13:39 -05:00
use super ::virtual_fs ::output_vfs ;
2023-04-19 17:50:56 -04:00
use crate ::args ::CliOptions ;
use crate ::args ::CompileFlags ;
2025-01-17 15:39:29 -05:00
use crate ::cache ::DenoDir ;
2024-10-24 15:48:48 -04:00
use crate ::emit ::Emitter ;
2024-06-03 17:17:08 -04:00
use crate ::http_util ::HttpClientProvider ;
2025-01-19 14:23:07 -05:00
use crate ::node ::CliCjsCodeAnalyzer ;
2023-05-10 20:06:59 -04:00
use crate ::npm ::CliNpmResolver ;
2025-01-14 10:01:05 -05:00
use crate ::resolver ::CliCjsTracker ;
2024-08-08 09:19:05 +02:00
use crate ::util ::archive ;
2023-04-19 17:50:56 -04:00
use crate ::util ::progress_bar ::ProgressBar ;
use crate ::util ::progress_bar ::ProgressBarStyle ;
2024-10-24 15:48:48 -04:00
/// A URL that can be designated as the base for relative URLs.
///
/// After creation, this URL may be used to get the key for a
/// module in the binary.
#[ derive(Debug, Clone, Copy, PartialEq, Eq, Hash) ]
2024-12-12 13:07:35 -05:00
pub enum StandaloneRelativeFileBaseUrl < ' a > {
WindowsSystemRoot ,
Path ( & ' a Url ) ,
2024-10-24 15:48:48 -04:00
}
impl < ' a > StandaloneRelativeFileBaseUrl < ' a > {
/// Gets the module map key of the provided specifier.
///
/// * Descendant file specifiers will be made relative to the base.
/// * Non-descendant file specifiers will stay as-is (absolute).
/// * Non-file specifiers will stay as-is.
pub fn specifier_key < ' b > ( & self , target : & ' b Url ) -> Cow < ' b , str > {
if target . scheme ( ) ! = " file " {
return Cow ::Borrowed ( target . as_str ( ) ) ;
}
2024-12-12 13:07:35 -05:00
let base = match self {
Self ::Path ( base ) = > base ,
Self ::WindowsSystemRoot = > return Cow ::Borrowed ( target . path ( ) ) ,
} ;
2024-10-24 15:48:48 -04:00
2024-12-12 13:07:35 -05:00
match base . make_relative ( target ) {
2024-10-24 15:48:48 -04:00
Some ( relative ) = > {
2024-12-12 13:07:35 -05:00
// This is not a great scenario to have because it means that the
// specifier is outside the vfs and could cause the binary to act
// strangely. If you encounter this, the fix is to add more paths
// to the vfs builder by calling `add_possible_min_root_dir`.
debug_assert! (
! relative . starts_with ( " ../ " ) ,
" {} -> {} ({}) " ,
base . as_str ( ) ,
target . as_str ( ) ,
relative ,
) ;
Cow ::Owned ( relative )
2024-10-24 15:48:48 -04:00
}
None = > Cow ::Borrowed ( target . as_str ( ) ) ,
}
}
}
2023-04-19 17:50:56 -04:00
2025-01-19 14:23:07 -05:00
struct SpecifierStore < ' a > {
data : IndexMap < & ' a Url , SpecifierId > ,
}
2024-07-31 21:15:13 -07:00
2025-01-19 14:23:07 -05:00
impl < ' a > SpecifierStore < ' a > {
pub fn with_capacity ( capacity : usize ) -> Self {
Self {
data : IndexMap ::with_capacity ( capacity ) ,
2024-08-14 21:42:23 -07:00
}
2025-01-19 14:23:07 -05:00
}
2024-08-14 21:42:23 -07:00
2025-01-19 14:23:07 -05:00
pub fn get_or_add ( & mut self , specifier : & ' a Url ) -> SpecifierId {
let len = self . data . len ( ) ;
let entry = self . data . entry ( specifier ) ;
match entry {
indexmap ::map ::Entry ::Occupied ( occupied_entry ) = > * occupied_entry . get ( ) ,
indexmap ::map ::Entry ::Vacant ( vacant_entry ) = > {
let new_id = SpecifierId ::new ( len as u32 ) ;
vacant_entry . insert ( new_id ) ;
new_id
}
}
}
pub fn for_serialization (
self ,
base_url : & StandaloneRelativeFileBaseUrl < ' a > ,
) -> SpecifierStoreForSerialization < ' a > {
SpecifierStoreForSerialization {
data : self
. data
. into_iter ( )
. map ( | ( specifier , id ) | ( base_url . specifier_key ( specifier ) , id ) )
. collect ( ) ,
}
}
}
struct SpecifierStoreForSerialization < ' a > {
data : Vec < ( Cow < ' a , str > , SpecifierId ) > ,
}
impl < ' a > BytesAppendable < ' a > for & ' a SpecifierStoreForSerialization < ' a > {
fn append_to_builder < TBytes : capacity_builder ::BytesType > (
self ,
builder : & mut capacity_builder ::BytesBuilder < ' a , TBytes > ,
) {
builder . append_le ( self . data . len ( ) as u32 ) ;
for ( specifier_str , id ) in & self . data {
builder . append_le ( specifier_str . len ( ) as u32 ) ;
builder . append ( specifier_str . as_ref ( ) ) ;
builder . append ( * id ) ;
}
2024-07-31 21:15:13 -07:00
}
2023-04-19 17:50:56 -04:00
}
pub fn is_standalone_binary ( exe_path : & Path ) -> bool {
2024-07-31 21:15:13 -07:00
let Ok ( data ) = std ::fs ::read ( exe_path ) else {
2023-04-19 17:50:56 -04:00
return false ;
} ;
2024-07-31 21:15:13 -07:00
libsui ::utils ::is_elf ( & data )
2024-08-01 02:11:24 -07:00
| | libsui ::utils ::is_pe ( & data )
| | libsui ::utils ::is_macho ( & data )
2023-04-19 17:50:56 -04:00
}
2024-12-11 09:40:50 -05:00
pub struct WriteBinOptions < ' a > {
pub writer : File ,
pub display_output_filename : & ' a str ,
pub graph : & ' a ModuleGraph ,
pub entrypoint : & ' a ModuleSpecifier ,
pub include_files : & ' a [ ModuleSpecifier ] ,
pub compile_flags : & ' a CompileFlags ,
}
2023-05-01 14:35:23 -04:00
pub struct DenoCompileBinaryWriter < ' a > {
2025-01-19 14:23:07 -05:00
cjs_code_analyzer : CliCjsCodeAnalyzer ,
2025-01-14 10:01:05 -05:00
cjs_tracker : & ' a CliCjsTracker ,
2024-11-19 16:19:35 -05:00
cli_options : & ' a CliOptions ,
2025-01-17 15:39:29 -05:00
deno_dir : & ' a DenoDir ,
2024-10-24 15:48:48 -04:00
emitter : & ' a Emitter ,
2024-06-03 17:17:08 -04:00
http_client_provider : & ' a HttpClientProvider ,
2025-01-14 10:01:05 -05:00
npm_resolver : & ' a CliNpmResolver ,
2024-07-04 20:41:01 -04:00
workspace_resolver : & ' a WorkspaceResolver ,
2023-05-17 17:38:50 -04:00
npm_system_info : NpmSystemInfo ,
2023-04-19 17:50:56 -04:00
}
2023-05-01 14:35:23 -04:00
impl < ' a > DenoCompileBinaryWriter < ' a > {
2023-05-10 20:06:59 -04:00
#[ allow(clippy::too_many_arguments) ]
2023-04-19 17:50:56 -04:00
pub fn new (
2025-01-19 14:23:07 -05:00
cjs_code_analyzer : CliCjsCodeAnalyzer ,
2025-01-14 10:01:05 -05:00
cjs_tracker : & ' a CliCjsTracker ,
2024-11-19 16:19:35 -05:00
cli_options : & ' a CliOptions ,
2025-01-17 15:39:29 -05:00
deno_dir : & ' a DenoDir ,
2024-10-24 15:48:48 -04:00
emitter : & ' a Emitter ,
2024-06-03 17:17:08 -04:00
http_client_provider : & ' a HttpClientProvider ,
2025-01-14 10:01:05 -05:00
npm_resolver : & ' a CliNpmResolver ,
2024-07-04 20:41:01 -04:00
workspace_resolver : & ' a WorkspaceResolver ,
2023-05-17 17:38:50 -04:00
npm_system_info : NpmSystemInfo ,
2023-04-19 17:50:56 -04:00
) -> Self {
Self {
2025-01-19 14:23:07 -05:00
cjs_code_analyzer ,
2024-11-01 12:27:00 -04:00
cjs_tracker ,
2024-11-19 16:19:35 -05:00
cli_options ,
2023-04-19 17:50:56 -04:00
deno_dir ,
2024-10-24 15:48:48 -04:00
emitter ,
2024-06-03 17:17:08 -04:00
http_client_provider ,
2023-05-10 20:06:59 -04:00
npm_resolver ,
2024-07-04 20:41:01 -04:00
workspace_resolver ,
2023-05-17 17:38:50 -04:00
npm_system_info ,
2023-04-19 17:50:56 -04:00
}
}
pub async fn write_bin (
& self ,
2024-12-11 09:40:50 -05:00
options : WriteBinOptions < '_ > ,
2023-04-19 17:50:56 -04:00
) -> Result < ( ) , AnyError > {
// Select base binary based on target
2024-12-11 09:40:50 -05:00
let mut original_binary =
self . get_base_binary ( options . compile_flags ) . await ? ;
2023-07-28 17:46:26 +02:00
2024-12-11 09:40:50 -05:00
if options . compile_flags . no_terminal {
let target = options . compile_flags . resolve_target ( ) ;
2023-07-29 08:06:47 -04:00
if ! target . contains ( " windows " ) {
2023-07-28 17:46:26 +02:00
bail! (
2023-07-29 08:06:47 -04:00
" The `--no-terminal` flag is only available when targeting Windows (current: {}) " ,
target ,
2023-07-28 17:46:26 +02:00
)
}
2024-11-19 16:19:35 -05:00
set_windows_binary_to_gui ( & mut original_binary )
. context ( " Setting windows binary to GUI. " ) ? ;
2023-07-28 17:46:26 +02:00
}
2024-12-11 09:40:50 -05:00
if options . compile_flags . icon . is_some ( ) {
let target = options . compile_flags . resolve_target ( ) ;
2024-08-14 21:42:23 -07:00
if ! target . contains ( " windows " ) {
bail! (
" The `--icon` flag is only available when targeting Windows (current: {}) " ,
target ,
)
}
}
2025-01-19 14:23:07 -05:00
self . write_standalone_binary ( options , original_binary ) . await
2023-04-19 17:50:56 -04:00
}
async fn get_base_binary (
& self ,
2023-07-29 08:06:47 -04:00
compile_flags : & CompileFlags ,
2023-04-19 17:50:56 -04:00
) -> Result < Vec < u8 > , AnyError > {
2024-02-13 21:52:30 +05:30
// Used for testing.
//
// Phase 2 of the 'min sized' deno compile RFC talks
// about adding this as a flag.
2024-11-26 06:23:09 -08:00
if let Some ( path ) = get_dev_binary_path ( ) {
2024-04-27 17:11:57 -04:00
return std ::fs ::read ( & path ) . with_context ( | | {
format! ( " Could not find denort at ' {} ' " , path . to_string_lossy ( ) )
} ) ;
2023-04-19 17:50:56 -04:00
}
2023-07-29 08:06:47 -04:00
let target = compile_flags . resolve_target ( ) ;
2024-11-10 09:00:44 +05:30
let binary_name = format! ( " denort- {target} .zip " ) ;
2025-01-17 15:39:29 -05:00
let binary_path_suffix = match DENO_VERSION_INFO . release_channel {
ReleaseChannel ::Canary = > {
format! ( " canary/ {} / {} " , DENO_VERSION_INFO . git_hash , binary_name )
}
_ = > {
2025-01-19 05:19:47 -05:00
format! ( " release/v {} / {} " , DENO_VERSION_INFO . deno , binary_name )
2025-01-17 15:39:29 -05:00
}
} ;
2023-04-19 17:50:56 -04:00
2024-11-10 09:00:44 +05:30
let download_directory = self . deno_dir . dl_folder_path ( ) ;
let binary_path = download_directory . join ( & binary_path_suffix ) ;
2023-04-19 17:50:56 -04:00
2024-11-10 09:00:44 +05:30
if ! binary_path . exists ( ) {
self
. download_base_binary ( & download_directory , & binary_path_suffix )
2024-11-19 16:19:35 -05:00
. await
. context ( " Setting up base binary. " ) ? ;
2024-11-10 09:00:44 +05:30
}
2023-04-19 17:50:56 -04:00
2024-11-19 16:19:35 -05:00
let read_file = | path : & Path | -> Result < Vec < u8 > , AnyError > {
std ::fs ::read ( path ) . with_context ( | | format! ( " Reading {} " , path . display ( ) ) )
} ;
let archive_data = read_file ( & binary_path ) ? ;
2023-04-19 17:50:56 -04:00
let temp_dir = tempfile ::TempDir ::new ( ) ? ;
2024-08-08 09:19:05 +02:00
let base_binary_path = archive ::unpack_into_dir ( archive ::UnpackArgs {
2024-11-10 09:00:44 +05:30
exe_name : " denort " ,
archive_name : & binary_name ,
2024-08-08 09:19:05 +02:00
archive_data : & archive_data ,
is_windows : target . contains ( " windows " ) ,
dest_path : temp_dir . path ( ) ,
} ) ? ;
2024-11-19 16:19:35 -05:00
let base_binary = read_file ( & base_binary_path ) ? ;
2023-04-19 17:50:56 -04:00
drop ( temp_dir ) ; // delete the temp dir
Ok ( base_binary )
}
2024-11-10 09:00:44 +05:30
async fn download_base_binary (
& self ,
output_directory : & Path ,
binary_path_suffix : & str ,
) -> Result < ( ) , AnyError > {
let download_url = format! ( " https://dl.deno.land/ {binary_path_suffix} " ) ;
let maybe_bytes = {
let progress_bars = ProgressBar ::new ( ProgressBarStyle ::DownloadBars ) ;
let progress = progress_bars . update ( & download_url ) ;
self
. http_client_provider
. get_or_create ( ) ?
. download_with_progress_and_retries (
download_url . parse ( ) ? ,
None ,
& progress ,
)
. await ?
} ;
let bytes = match maybe_bytes {
Some ( bytes ) = > bytes ,
None = > {
2024-11-19 16:19:35 -05:00
bail! ( " Download could not be found, aborting " ) ;
2024-11-10 09:00:44 +05:30
}
} ;
2024-11-19 16:19:35 -05:00
let create_dir_all = | dir : & Path | {
std ::fs ::create_dir_all ( dir )
. with_context ( | | format! ( " Creating {} " , dir . display ( ) ) )
} ;
create_dir_all ( output_directory ) ? ;
2024-11-10 09:00:44 +05:30
let output_path = output_directory . join ( binary_path_suffix ) ;
2024-11-19 16:19:35 -05:00
create_dir_all ( output_path . parent ( ) . unwrap ( ) ) ? ;
std ::fs ::write ( & output_path , bytes )
. with_context ( | | format! ( " Writing {} " , output_path . display ( ) ) ) ? ;
2024-11-10 09:00:44 +05:30
Ok ( ( ) )
}
2023-04-19 17:50:56 -04:00
/// This functions creates a standalone deno binary by appending a bundle
/// and magic trailer to the currently executing binary.
2024-07-03 20:54:33 -04:00
#[ allow(clippy::too_many_arguments) ]
2025-01-19 14:23:07 -05:00
async fn write_standalone_binary (
2023-04-19 17:50:56 -04:00
& self ,
2024-12-11 09:40:50 -05:00
options : WriteBinOptions < '_ > ,
2023-04-19 17:50:56 -04:00
original_bin : Vec < u8 > ,
) -> Result < ( ) , AnyError > {
2024-12-11 09:40:50 -05:00
let WriteBinOptions {
writer ,
display_output_filename ,
graph ,
entrypoint ,
include_files ,
compile_flags ,
} = options ;
2024-11-19 16:19:35 -05:00
let ca_data = match self . cli_options . ca_data ( ) {
2023-04-19 17:50:56 -04:00
Some ( CaData ::File ( ca_file ) ) = > Some (
2024-11-19 16:19:35 -05:00
std ::fs ::read ( ca_file ) . with_context ( | | format! ( " Reading {ca_file} " ) ) ? ,
2023-04-19 17:50:56 -04:00
) ,
Some ( CaData ::Bytes ( bytes ) ) = > Some ( bytes . clone ( ) ) ,
None = > None ,
} ;
2024-12-12 13:07:35 -05:00
let mut vfs = VfsBuilder ::new ( ) ;
2025-01-14 10:01:05 -05:00
let npm_snapshot = match & self . npm_resolver {
CliNpmResolver ::Managed ( managed ) = > {
let snapshot = managed
. resolution ( )
. serialized_valid_snapshot_for_system ( & self . npm_system_info ) ;
2024-12-12 13:07:35 -05:00
if ! snapshot . as_serialized ( ) . packages . is_empty ( ) {
self . fill_npm_vfs ( & mut vfs ) . context ( " Building npm vfs. " ) ? ;
Some ( snapshot )
} else {
None
2023-10-02 17:53:55 -04:00
}
2024-12-12 13:07:35 -05:00
}
2025-01-14 10:01:05 -05:00
CliNpmResolver ::Byonm ( _ ) = > {
2024-12-12 13:07:35 -05:00
self . fill_npm_vfs ( & mut vfs ) ? ;
None
}
2024-10-24 15:48:48 -04:00
} ;
2024-11-19 16:19:35 -05:00
for include_file in include_files {
let path = deno_path_util ::url_to_file_path ( include_file ) ? ;
2024-12-12 13:07:35 -05:00
vfs
. add_file_at_path ( & path )
. with_context ( | | format! ( " Including {} " , path . display ( ) ) ) ? ;
2024-11-19 16:19:35 -05:00
}
2025-01-19 14:23:07 -05:00
let specifiers_count = graph . specifiers_count ( ) ;
let mut specifier_store = SpecifierStore ::with_capacity ( specifiers_count ) ;
let mut remote_modules_store =
SpecifierDataStore ::with_capacity ( specifiers_count ) ;
// todo(dsherret): transpile and analyze CJS in parallel
2024-10-24 15:48:48 -04:00
for module in graph . modules ( ) {
if module . specifier ( ) . scheme ( ) = = " data " {
continue ; // don't store data urls as an entry as they're in the code
}
2025-01-19 14:23:07 -05:00
let mut maybe_source_map = None ;
let mut maybe_transpiled = None ;
let mut maybe_cjs_analysis = None ;
let ( maybe_original_source , media_type ) = match module {
2024-10-24 15:48:48 -04:00
deno_graph ::Module ::Js ( m ) = > {
2025-01-19 14:23:07 -05:00
let specifier = & m . specifier ;
let original_bytes = m . source . as_bytes ( ) ;
if self . cjs_tracker . is_maybe_cjs ( specifier , m . media_type ) ? {
if self . cjs_tracker . is_cjs_with_known_is_script (
specifier ,
2024-11-01 12:27:00 -04:00
m . media_type ,
m . is_script ,
2025-01-19 14:23:07 -05:00
) ? {
let cjs_analysis = self
. cjs_code_analyzer
. analyze_cjs (
module . specifier ( ) ,
Some ( Cow ::Borrowed ( m . source . as_ref ( ) ) ) ,
)
. await ? ;
maybe_cjs_analysis = Some ( match cjs_analysis {
CjsAnalysis ::Esm ( _ ) = > CjsExportAnalysisEntry ::Esm ,
CjsAnalysis ::Cjs ( exports ) = > {
CjsExportAnalysisEntry ::Cjs ( exports )
}
} ) ;
} else {
maybe_cjs_analysis = Some ( CjsExportAnalysisEntry ::Esm ) ;
}
}
if m . media_type . is_emittable ( ) {
let module_kind = match maybe_cjs_analysis . as_ref ( ) {
Some ( CjsExportAnalysisEntry ::Cjs ( _ ) ) = > ModuleKind ::Cjs ,
_ = > ModuleKind ::Esm ,
} ;
2024-12-19 12:53:52 -05:00
let ( source , source_map ) =
self . emitter . emit_parsed_source_for_deno_compile (
2024-11-01 12:27:00 -04:00
& m . specifier ,
m . media_type ,
module_kind ,
& m . source ,
2024-12-19 12:53:52 -05:00
) ? ;
if source ! = m . source . as_ref ( ) {
2025-01-19 14:23:07 -05:00
maybe_source_map = Some ( source_map . into_bytes ( ) ) ;
maybe_transpiled = Some ( source . into_bytes ( ) ) ;
2024-12-19 12:53:52 -05:00
}
2025-01-19 14:23:07 -05:00
}
( Some ( original_bytes ) , m . media_type )
2024-10-24 15:48:48 -04:00
}
deno_graph ::Module ::Json ( m ) = > {
2025-01-19 14:23:07 -05:00
( Some ( m . source . as_bytes ( ) ) , m . media_type )
2024-10-24 15:48:48 -04:00
}
2024-11-19 18:59:23 -05:00
deno_graph ::Module ::Wasm ( m ) = > {
2025-01-19 14:23:07 -05:00
( Some ( m . source . as_ref ( ) ) , MediaType ::Wasm )
2024-11-19 18:59:23 -05:00
}
2024-10-24 15:48:48 -04:00
deno_graph ::Module ::Npm ( _ )
| deno_graph ::Module ::Node ( _ )
2025-01-19 14:23:07 -05:00
| deno_graph ::Module ::External ( _ ) = > ( None , MediaType ::Unknown ) ,
2024-10-24 15:48:48 -04:00
} ;
2024-12-19 12:53:52 -05:00
if let Some ( original_source ) = maybe_original_source {
2025-01-19 14:23:07 -05:00
let maybe_cjs_export_analysis = maybe_cjs_analysis
. as_ref ( )
. map ( bincode ::serialize )
. transpose ( ) ? ;
2024-12-19 12:53:52 -05:00
if module . specifier ( ) . scheme ( ) = = " file " {
let file_path = deno_path_util ::url_to_file_path ( module . specifier ( ) ) ? ;
vfs
. add_file_with_data (
& file_path ,
2025-01-19 14:23:07 -05:00
deno_lib ::standalone ::virtual_fs ::AddFileDataOptions {
data : original_source . to_vec ( ) ,
maybe_transpiled ,
maybe_source_map ,
maybe_cjs_export_analysis ,
} ,
2024-12-19 12:53:52 -05:00
)
. with_context ( | | {
format! ( " Failed adding ' {} ' " , file_path . display ( ) )
} ) ? ;
} else {
2025-01-19 14:23:07 -05:00
let specifier_id = specifier_store . get_or_add ( module . specifier ( ) ) ;
2024-12-19 12:53:52 -05:00
remote_modules_store . add (
2025-01-19 14:23:07 -05:00
specifier_id ,
RemoteModuleEntry {
media_type ,
data : Cow ::Borrowed ( original_source ) ,
maybe_transpiled : maybe_transpiled . map ( Cow ::Owned ) ,
maybe_source_map : maybe_source_map . map ( Cow ::Owned ) ,
maybe_cjs_export_analysis : maybe_cjs_export_analysis
. map ( Cow ::Owned ) ,
} ,
2024-12-19 12:53:52 -05:00
) ;
}
2024-10-24 15:48:48 -04:00
}
}
2025-01-19 14:23:07 -05:00
let mut redirects_store =
SpecifierDataStore ::with_capacity ( graph . redirects . len ( ) ) ;
for ( from , to ) in & graph . redirects {
redirects_store . add (
specifier_store . get_or_add ( from ) ,
specifier_store . get_or_add ( to ) ,
) ;
}
2023-05-10 20:06:59 -04:00
2024-12-12 13:07:35 -05:00
if let Some ( import_map ) = self . workspace_resolver . maybe_import_map ( ) {
if let Ok ( file_path ) = url_to_file_path ( import_map . base_url ( ) ) {
if let Some ( import_map_parent_dir ) = file_path . parent ( ) {
// tell the vfs about the import map's parent directory in case it
// falls outside what the root of where the VFS will be based
vfs . add_possible_min_root_dir ( import_map_parent_dir ) ;
}
}
}
if let Some ( node_modules_dir ) = self . npm_resolver . root_node_modules_path ( ) {
// ensure the vfs doesn't go below the node_modules directory's parent
if let Some ( parent ) = node_modules_dir . parent ( ) {
vfs . add_possible_min_root_dir ( parent ) ;
}
}
2025-01-19 14:23:07 -05:00
// do CJS export analysis on all the files in the VFS
// todo(dsherret): analyze cjs in parallel
let mut to_add = Vec ::new ( ) ;
for ( file_path , file ) in vfs . iter_files ( ) {
if file . cjs_export_analysis_offset . is_some ( ) {
continue ; // already analyzed
}
let specifier = deno_path_util ::url_from_file_path ( & file_path ) ? ;
let media_type = MediaType ::from_specifier ( & specifier ) ;
if self . cjs_tracker . is_maybe_cjs ( & specifier , media_type ) ? {
let maybe_source = vfs
. file_bytes ( file . offset )
. map ( | text | String ::from_utf8_lossy ( text ) ) ;
let cjs_analysis_result = self
. cjs_code_analyzer
. analyze_cjs ( & specifier , maybe_source )
. await ;
let maybe_analysis = match cjs_analysis_result {
Ok ( CjsAnalysis ::Esm ( _ ) ) = > Some ( CjsExportAnalysisEntry ::Esm ) ,
Ok ( CjsAnalysis ::Cjs ( exports ) ) = > {
Some ( CjsExportAnalysisEntry ::Cjs ( exports ) )
}
Err ( err ) = > {
log ::debug! (
" Ignoring cjs export analysis for '{}': {} " ,
specifier ,
err
) ;
None
}
} ;
if let Some ( analysis ) = & maybe_analysis {
to_add . push ( ( file_path , bincode ::serialize ( analysis ) ? ) ) ;
}
}
}
for ( file_path , analysis ) in to_add {
vfs . add_cjs_export_analysis ( & file_path , analysis ) ;
}
2024-12-12 13:07:35 -05:00
let vfs = self . build_vfs_consolidating_global_npm_cache ( vfs ) ;
2025-01-19 14:23:07 -05:00
2024-12-12 13:07:35 -05:00
let root_dir_url = match & vfs . root_path {
WindowsSystemRootablePath ::Path ( dir ) = > {
Some ( url_from_directory_path ( dir ) ? )
}
WindowsSystemRootablePath ::WindowSystemRoot = > None ,
} ;
let root_dir_url = match & root_dir_url {
Some ( url ) = > StandaloneRelativeFileBaseUrl ::Path ( url ) ,
None = > StandaloneRelativeFileBaseUrl ::WindowsSystemRoot ,
} ;
2024-12-19 12:53:52 -05:00
let code_cache_key = if self . cli_options . code_cache_enabled ( ) {
let mut hasher = FastInsecureHasher ::new_deno_versioned ( ) ;
for module in graph . modules ( ) {
if let Some ( source ) = module . source ( ) {
hasher
. write ( root_dir_url . specifier_key ( module . specifier ( ) ) . as_bytes ( ) ) ;
hasher . write ( source . as_bytes ( ) ) ;
}
}
Some ( hasher . finish ( ) )
} else {
None
} ;
2025-01-14 10:01:05 -05:00
let node_modules = match & self . npm_resolver {
CliNpmResolver ::Managed ( _ ) = > {
2024-12-12 13:07:35 -05:00
npm_snapshot . as_ref ( ) . map ( | _ | NodeModules ::Managed {
node_modules_dir : self . npm_resolver . root_node_modules_path ( ) . map (
| path | {
root_dir_url
. specifier_key (
& ModuleSpecifier ::from_directory_path ( path ) . unwrap ( ) ,
)
. into_owned ( )
} ,
) ,
} )
}
2025-01-14 10:01:05 -05:00
CliNpmResolver ::Byonm ( resolver ) = > Some ( NodeModules ::Byonm {
2024-12-12 13:07:35 -05:00
root_node_modules_dir : resolver . root_node_modules_path ( ) . map (
| node_modules_dir | {
root_dir_url
. specifier_key (
& ModuleSpecifier ::from_directory_path ( node_modules_dir )
. unwrap ( ) ,
)
. into_owned ( )
} ,
) ,
} ) ,
} ;
2024-11-19 16:19:35 -05:00
let env_vars_from_env_file = match self . cli_options . env_file_name ( ) {
2024-11-17 22:49:35 +00:00
Some ( env_filenames ) = > {
let mut aggregated_env_vars = IndexMap ::new ( ) ;
for env_filename in env_filenames . iter ( ) . rev ( ) {
log ::info! ( " {} Environment variables from the file \" {} \" were embedded in the generated executable file " , crate ::colors ::yellow ( " Warning " ) , env_filename ) ;
let env_vars = get_file_env_vars ( env_filename . to_string ( ) ) ? ;
aggregated_env_vars . extend ( env_vars ) ;
}
aggregated_env_vars
2024-07-10 00:33:41 +03:00
}
None = > Default ::default ( ) ,
} ;
2024-12-12 13:07:35 -05:00
output_vfs ( & vfs , display_output_filename ) ;
2023-04-19 17:50:56 -04:00
let metadata = Metadata {
argv : compile_flags . args . clone ( ) ,
2024-11-19 16:19:35 -05:00
seed : self . cli_options . seed ( ) ,
2024-12-19 12:53:52 -05:00
code_cache_key ,
2024-11-19 16:19:35 -05:00
location : self . cli_options . location_flag ( ) . clone ( ) ,
2025-01-02 16:55:03 -05:00
permissions : self . cli_options . permissions_options ( ) ,
2024-11-19 16:19:35 -05:00
v8_flags : self . cli_options . v8_flags ( ) . clone ( ) ,
unsafely_ignore_certificate_errors : self
. cli_options
2023-04-19 17:50:56 -04:00
. unsafely_ignore_certificate_errors ( )
. clone ( ) ,
2024-11-19 16:19:35 -05:00
log_level : self . cli_options . log_level ( ) ,
ca_stores : self . cli_options . ca_stores ( ) . clone ( ) ,
2023-04-19 17:50:56 -04:00
ca_data ,
2024-07-10 00:33:41 +03:00
env_vars_from_env_file ,
2024-07-03 20:54:33 -04:00
entrypoint_key : root_dir_url . specifier_key ( entrypoint ) . into_owned ( ) ,
workspace_resolver : SerializedWorkspaceResolver {
2024-07-04 20:41:01 -04:00
import_map : self . workspace_resolver . maybe_import_map ( ) . map ( | i | {
2024-07-03 20:54:33 -04:00
SerializedWorkspaceResolverImportMap {
specifier : if i . base_url ( ) . scheme ( ) = = " file " {
root_dir_url . specifier_key ( i . base_url ( ) ) . into_owned ( )
} else {
// just make a remote url local
" deno.json " . to_string ( )
} ,
json : i . to_json ( ) ,
}
} ) ,
2024-08-07 09:43:05 +02:00
jsr_pkgs : self
. workspace_resolver
. jsr_packages ( )
. map ( | pkg | SerializedResolverWorkspaceJsrPackage {
relative_base : root_dir_url . specifier_key ( & pkg . base ) . into_owned ( ) ,
name : pkg . name . clone ( ) ,
version : pkg . version . clone ( ) ,
exports : pkg . exports . clone ( ) ,
} )
. collect ( ) ,
2024-07-04 20:41:01 -04:00
package_jsons : self
. workspace_resolver
2024-07-03 20:54:33 -04:00
. package_jsons ( )
. map ( | pkg_json | {
(
root_dir_url
. specifier_key ( & pkg_json . specifier ( ) )
. into_owned ( ) ,
serde_json ::to_value ( pkg_json ) . unwrap ( ) ,
)
} )
. collect ( ) ,
2024-07-04 20:41:01 -04:00
pkg_json_resolution : self . workspace_resolver . pkg_json_dep_resolution ( ) ,
2024-07-03 20:54:33 -04:00
} ,
2023-11-29 09:32:23 -05:00
node_modules ,
2024-01-22 18:37:28 +01:00
unstable_config : UnstableConfig {
2024-09-09 22:44:29 +01:00
legacy_flag_enabled : false ,
2024-11-19 16:19:35 -05:00
bare_node_builtins : self . cli_options . unstable_bare_node_builtins ( ) ,
2024-11-27 09:50:38 -05:00
detect_cjs : self . cli_options . unstable_detect_cjs ( ) ,
2024-11-19 16:19:35 -05:00
sloppy_imports : self . cli_options . unstable_sloppy_imports ( ) ,
features : self . cli_options . unstable_features ( ) ,
2024-12-10 18:24:23 -08:00
npm_lazy_caching : self . cli_options . unstable_npm_lazy_caching ( ) ,
2024-01-22 18:37:28 +01:00
} ,
2024-11-19 16:19:35 -05:00
otel_config : self . cli_options . otel_config ( ) ,
2025-01-13 12:02:37 -05:00
vfs_case_sensitivity : vfs . case_sensitivity ,
2023-04-19 17:50:56 -04:00
} ;
2025-01-19 14:23:07 -05:00
let data_section_bytes = serialize_binary_data_section (
2023-05-10 20:06:59 -04:00
& metadata ,
2024-10-24 15:48:48 -04:00
npm_snapshot . map ( | s | s . into_serialized ( ) ) ,
2025-01-19 14:23:07 -05:00
& specifier_store . for_serialization ( & root_dir_url ) ,
& redirects_store ,
2024-10-24 15:48:48 -04:00
& remote_modules_store ,
2024-12-12 13:07:35 -05:00
& vfs ,
2023-05-10 20:06:59 -04:00
)
2025-01-19 14:23:07 -05:00
. context ( " Serializing binary data section. " ) ? ;
write_binary_bytes ( writer , original_bin , data_section_bytes , compile_flags )
. context ( " Writing binary bytes " )
2023-05-10 20:06:59 -04:00
}
2024-12-12 13:07:35 -05:00
fn fill_npm_vfs ( & self , builder : & mut VfsBuilder ) -> Result < ( ) , AnyError > {
2023-12-06 16:25:24 -05:00
fn maybe_warn_different_system ( system_info : & NpmSystemInfo ) {
if system_info ! = & NpmSystemInfo ::default ( ) {
log ::warn! ( " {} The node_modules directory may be incompatible with the target system. " , crate ::colors ::yellow ( " Warning " ) ) ;
}
}
2025-01-14 10:01:05 -05:00
match & self . npm_resolver {
CliNpmResolver ::Managed ( npm_resolver ) = > {
2023-10-03 19:05:06 -04:00
if let Some ( node_modules_path ) = npm_resolver . root_node_modules_path ( ) {
2023-12-06 16:25:24 -05:00
maybe_warn_different_system ( & self . npm_system_info ) ;
2023-11-29 09:32:23 -05:00
builder . add_dir_recursive ( node_modules_path ) ? ;
2024-12-12 13:07:35 -05:00
Ok ( ( ) )
2023-09-29 09:26:25 -04:00
} else {
2024-12-12 13:07:35 -05:00
// we'll flatten to remove any custom registries later
2025-01-14 10:01:05 -05:00
let mut packages = npm_resolver
. resolution ( )
. all_system_packages ( & self . npm_system_info ) ;
2024-08-19 12:41:11 -04:00
packages . sort_by ( | a , b | a . id . cmp ( & b . id ) ) ; // determinism
for package in packages {
2023-09-29 09:26:25 -04:00
let folder =
npm_resolver . resolve_pkg_folder_from_pkg_id ( & package . id ) ? ;
builder . add_dir_recursive ( & folder ) ? ;
}
2024-12-12 13:07:35 -05:00
Ok ( ( ) )
2023-09-29 09:26:25 -04:00
}
}
2025-01-14 10:01:05 -05:00
CliNpmResolver ::Byonm ( _ ) = > {
2023-12-06 16:25:24 -05:00
maybe_warn_different_system ( & self . npm_system_info ) ;
2024-11-19 16:19:35 -05:00
for pkg_json in self . cli_options . workspace ( ) . package_jsons ( ) {
2024-07-03 20:54:33 -04:00
builder . add_file_at_path ( & pkg_json . path ) ? ;
2023-11-29 09:32:23 -05:00
}
2024-07-03 20:54:33 -04:00
// traverse and add all the node_modules directories in the workspace
let mut pending_dirs = VecDeque ::new ( ) ;
pending_dirs . push_back (
2024-11-19 16:19:35 -05:00
self
. cli_options
. workspace ( )
. root_dir ( )
. to_file_path ( )
. unwrap ( ) ,
2024-07-03 20:54:33 -04:00
) ;
while let Some ( pending_dir ) = pending_dirs . pop_front ( ) {
2024-08-19 12:41:11 -04:00
let mut entries = fs ::read_dir ( & pending_dir )
. with_context ( | | {
format! ( " Failed reading: {} " , pending_dir . display ( ) )
} ) ?
. collect ::< Result < Vec < _ > , _ > > ( ) ? ;
entries . sort_by_cached_key ( | entry | entry . file_name ( ) ) ; // determinism
2024-07-03 20:54:33 -04:00
for entry in entries {
let path = entry . path ( ) ;
if ! path . is_dir ( ) {
continue ;
}
if path . ends_with ( " node_modules " ) {
builder . add_dir_recursive ( & path ) ? ;
} else {
pending_dirs . push_back ( path ) ;
}
}
2023-11-29 09:32:23 -05:00
}
2024-12-12 13:07:35 -05:00
Ok ( ( ) )
}
}
}
fn build_vfs_consolidating_global_npm_cache (
& self ,
mut vfs : VfsBuilder ,
) -> BuiltVfs {
2025-01-14 10:01:05 -05:00
match & self . npm_resolver {
CliNpmResolver ::Managed ( npm_resolver ) = > {
2024-12-12 13:07:35 -05:00
if npm_resolver . root_node_modules_path ( ) . is_some ( ) {
return vfs . build ( ) ;
}
let global_cache_root_path = npm_resolver . global_cache_root_path ( ) ;
// Flatten all the registries folders into a single ".deno_compile_node_modules/localhost" folder
// that will be used by denort when loading the npm cache. This avoids us exposing
// the user's private registry information and means we don't have to bother
// serializing all the different registry config into the binary.
let Some ( root_dir ) = vfs . get_dir_mut ( global_cache_root_path ) else {
return vfs . build ( ) ;
} ;
root_dir . name = DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME . to_string ( ) ;
let mut new_entries = Vec ::with_capacity ( root_dir . entries . len ( ) ) ;
let mut localhost_entries = IndexMap ::new ( ) ;
2024-12-19 12:53:52 -05:00
for entry in root_dir . entries . take_inner ( ) {
2024-12-12 13:07:35 -05:00
match entry {
2024-12-19 12:53:52 -05:00
VfsEntry ::Dir ( mut dir ) = > {
for entry in dir . entries . take_inner ( ) {
2024-12-12 13:07:35 -05:00
log ::debug! ( " Flattening {} into node_modules " , entry . name ( ) ) ;
if let Some ( existing ) =
localhost_entries . insert ( entry . name ( ) . to_string ( ) , entry )
{
panic! (
" Unhandled scenario where a duplicate entry was found: {:?} " ,
existing
) ;
}
}
}
VfsEntry ::File ( _ ) | VfsEntry ::Symlink ( _ ) = > {
new_entries . push ( entry ) ;
}
}
}
new_entries . push ( VfsEntry ::Dir ( VirtualDirectory {
name : " localhost " . to_string ( ) ,
2024-12-19 12:53:52 -05:00
entries : VirtualDirectoryEntries ::new (
localhost_entries . into_iter ( ) . map ( | ( _ , v ) | v ) . collect ( ) ,
) ,
2024-12-12 13:07:35 -05:00
} ) ) ;
2024-12-19 12:53:52 -05:00
root_dir . entries = VirtualDirectoryEntries ::new ( new_entries ) ;
2024-12-12 13:07:35 -05:00
// it's better to not expose the user's cache directory, so take it out
// of there
2025-01-13 12:02:37 -05:00
let case_sensitivity = vfs . case_sensitivity ( ) ;
2024-12-12 13:07:35 -05:00
let parent = global_cache_root_path . parent ( ) . unwrap ( ) ;
let parent_dir = vfs . get_dir_mut ( parent ) . unwrap ( ) ;
let index = parent_dir
. entries
2025-01-13 12:02:37 -05:00
. binary_search (
DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME ,
case_sensitivity ,
)
2024-12-12 13:07:35 -05:00
. unwrap ( ) ;
let npm_global_cache_dir_entry = parent_dir . entries . remove ( index ) ;
// go up from the ancestors removing empty directories...
// this is not as optimized as it could be
let mut last_name =
Cow ::Borrowed ( DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME ) ;
2025-01-13 22:29:21 -05:00
for ancestor in
parent . ancestors ( ) . map ( Some ) . chain ( std ::iter ::once ( None ) )
{
let dir = if let Some ( ancestor ) = ancestor {
vfs . get_dir_mut ( ancestor ) . unwrap ( )
} else if cfg! ( windows ) {
vfs . get_system_root_dir_mut ( )
} else {
break ;
} ;
2025-01-13 12:02:37 -05:00
if let Ok ( index ) =
dir . entries . binary_search ( & last_name , case_sensitivity )
{
2024-12-12 13:07:35 -05:00
dir . entries . remove ( index ) ;
}
last_name = Cow ::Owned ( dir . name . clone ( ) ) ;
if ! dir . entries . is_empty ( ) {
break ;
}
}
// now build the vfs and add the global cache dir entry there
let mut built_vfs = vfs . build ( ) ;
2025-01-13 12:02:37 -05:00
built_vfs
. entries
. insert ( npm_global_cache_dir_entry , case_sensitivity ) ;
2024-12-12 13:07:35 -05:00
built_vfs
2023-05-10 20:06:59 -04:00
}
2025-01-14 10:01:05 -05:00
CliNpmResolver ::Byonm ( _ ) = > vfs . build ( ) ,
2023-05-10 20:06:59 -04:00
}
2023-04-19 17:50:56 -04:00
}
}
2023-07-28 17:46:26 +02:00
2025-01-19 14:23:07 -05:00
#[ allow(clippy::too_many_arguments) ]
fn write_binary_bytes (
mut file_writer : File ,
original_bin : Vec < u8 > ,
data_section_bytes : Vec < u8 > ,
compile_flags : & CompileFlags ,
) -> Result < ( ) , AnyError > {
let target = compile_flags . resolve_target ( ) ;
if target . contains ( " linux " ) {
libsui ::Elf ::new ( & original_bin ) . append (
" d3n0l4nd " ,
& data_section_bytes ,
& mut file_writer ,
) ? ;
} else if target . contains ( " windows " ) {
let mut pe = libsui ::PortableExecutable ::from ( & original_bin ) ? ;
if let Some ( icon ) = compile_flags . icon . as_ref ( ) {
let icon = std ::fs ::read ( icon ) ? ;
pe = pe . set_icon ( & icon ) ? ;
}
pe . write_resource ( " d3n0l4nd " , data_section_bytes ) ?
. build ( & mut file_writer ) ? ;
} else if target . contains ( " darwin " ) {
libsui ::Macho ::from ( original_bin ) ?
. write_section ( " d3n0l4nd " , data_section_bytes ) ?
. build_and_sign ( & mut file_writer ) ? ;
}
Ok ( ( ) )
}
/// Binary format:
/// * d3n0l4nd
/// * <metadata_len><metadata>
/// * <npm_snapshot_len><npm_snapshot>
/// * <specifiers>
/// * <redirects>
/// * <remote_modules>
/// * <vfs_headers_len><vfs_headers>
/// * <vfs_file_data_len><vfs_file_data>
/// * d3n0l4nd
#[ allow(clippy::too_many_arguments) ]
fn serialize_binary_data_section (
metadata : & Metadata ,
npm_snapshot : Option < SerializedNpmResolutionSnapshot > ,
specifiers : & SpecifierStoreForSerialization ,
redirects : & SpecifierDataStore < SpecifierId > ,
remote_modules : & SpecifierDataStore < RemoteModuleEntry < '_ > > ,
vfs : & BuiltVfs ,
) -> Result < Vec < u8 > , AnyError > {
let metadata = serde_json ::to_string ( metadata ) ? ;
let npm_snapshot =
npm_snapshot . map ( serialize_npm_snapshot ) . unwrap_or_default ( ) ;
let serialized_vfs = serde_json ::to_string ( & vfs . entries ) ? ;
let bytes = capacity_builder ::BytesBuilder ::build ( | builder | {
builder . append ( MAGIC_BYTES ) ;
// 1. Metadata
{
builder . append_le ( metadata . len ( ) as u64 ) ;
builder . append ( & metadata ) ;
}
// 2. Npm snapshot
{
builder . append_le ( npm_snapshot . len ( ) as u64 ) ;
builder . append ( & npm_snapshot ) ;
}
// 3. Specifiers
builder . append ( specifiers ) ;
// 4. Redirects
redirects . serialize ( builder ) ;
// 5. Remote modules
remote_modules . serialize ( builder ) ;
// 6. VFS
{
builder . append_le ( serialized_vfs . len ( ) as u64 ) ;
builder . append ( & serialized_vfs ) ;
let vfs_bytes_len = vfs . files . iter ( ) . map ( | f | f . len ( ) as u64 ) . sum ::< u64 > ( ) ;
builder . append_le ( vfs_bytes_len ) ;
for file in & vfs . files {
builder . append ( file ) ;
}
}
// write the magic bytes at the end so we can use it
// to make sure we've deserialized correctly
builder . append ( MAGIC_BYTES ) ;
} ) ? ;
Ok ( bytes )
}
fn serialize_npm_snapshot (
mut snapshot : SerializedNpmResolutionSnapshot ,
) -> Vec < u8 > {
fn append_string ( bytes : & mut Vec < u8 > , string : & str ) {
let len = string . len ( ) as u32 ;
bytes . extend_from_slice ( & len . to_le_bytes ( ) ) ;
bytes . extend_from_slice ( string . as_bytes ( ) ) ;
}
snapshot . packages . sort_by ( | a , b | a . id . cmp ( & b . id ) ) ; // determinism
let ids_to_stored_ids = snapshot
. packages
. iter ( )
. enumerate ( )
. map ( | ( i , pkg ) | ( & pkg . id , i as u32 ) )
. collect ::< HashMap < _ , _ > > ( ) ;
let mut root_packages : Vec < _ > = snapshot . root_packages . iter ( ) . collect ( ) ;
root_packages . sort ( ) ;
let mut bytes = Vec ::new ( ) ;
bytes . extend_from_slice ( & ( snapshot . packages . len ( ) as u32 ) . to_le_bytes ( ) ) ;
for pkg in & snapshot . packages {
append_string ( & mut bytes , & pkg . id . as_serialized ( ) ) ;
}
bytes . extend_from_slice ( & ( root_packages . len ( ) as u32 ) . to_le_bytes ( ) ) ;
for ( req , id ) in root_packages {
append_string ( & mut bytes , & req . to_string ( ) ) ;
let id = ids_to_stored_ids . get ( & id ) . unwrap ( ) ;
bytes . extend_from_slice ( & id . to_le_bytes ( ) ) ;
}
for pkg in & snapshot . packages {
let deps_len = pkg . dependencies . len ( ) as u32 ;
bytes . extend_from_slice ( & deps_len . to_le_bytes ( ) ) ;
let mut deps : Vec < _ > = pkg . dependencies . iter ( ) . collect ( ) ;
deps . sort ( ) ;
for ( req , id ) in deps {
append_string ( & mut bytes , req ) ;
let id = ids_to_stored_ids . get ( & id ) . unwrap ( ) ;
bytes . extend_from_slice ( & id . to_le_bytes ( ) ) ;
}
}
bytes
}
2024-11-26 06:23:09 -08:00
fn get_denort_path ( deno_exe : PathBuf ) -> Option < OsString > {
let mut denort = deno_exe ;
denort . set_file_name ( if cfg! ( windows ) {
" denort.exe "
} else {
" denort "
} ) ;
denort . exists ( ) . then ( | | denort . into_os_string ( ) )
}
fn get_dev_binary_path ( ) -> Option < OsString > {
env ::var_os ( " DENORT_BIN " ) . or_else ( | | {
env ::current_exe ( ) . ok ( ) . and_then ( | exec_path | {
if exec_path
. components ( )
. any ( | component | component = = Component ::Normal ( " target " . as_ref ( ) ) )
{
get_denort_path ( exec_path )
} else {
None
}
} )
} )
}
2024-07-10 00:33:41 +03:00
/// This function returns the environment variables specified
/// in the passed environment file.
fn get_file_env_vars (
filename : String ,
2024-08-19 12:41:11 -04:00
) -> Result < IndexMap < String , String > , dotenvy ::Error > {
let mut file_env_vars = IndexMap ::new ( ) ;
2024-07-10 00:33:41 +03:00
for item in dotenvy ::from_filename_iter ( filename ) ? {
let Ok ( ( key , val ) ) = item else {
continue ; // this failure will be warned about on load
} ;
file_env_vars . insert ( key , val ) ;
}
Ok ( file_env_vars )
}
2023-07-28 17:46:26 +02:00
/// This function sets the subsystem field in the PE header to 2 (GUI subsystem)
/// For more information about the PE header: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format
fn set_windows_binary_to_gui ( bin : & mut [ u8 ] ) -> Result < ( ) , AnyError > {
// Get the PE header offset located in an i32 found at offset 60
// See: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#ms-dos-stub-image-only
let start_pe = u32 ::from_le_bytes ( ( bin [ 60 .. 64 ] ) . try_into ( ) ? ) ;
// Get image type (PE32 or PE32+) indicates whether the binary is 32 or 64 bit
// The used offset and size values can be found here:
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-image-only
let start_32 = start_pe as usize + 28 ;
let magic_32 =
u16 ::from_le_bytes ( bin [ ( start_32 ) .. ( start_32 + 2 ) ] . try_into ( ) ? ) ;
let start_64 = start_pe as usize + 24 ;
let magic_64 =
u16 ::from_le_bytes ( bin [ ( start_64 ) .. ( start_64 + 2 ) ] . try_into ( ) ? ) ;
// Take the standard fields size for the current architecture (32 or 64 bit)
// This is the ofset for the Windows-Specific fields
let standard_fields_size = if magic_32 = = 0x10b {
28
} else if magic_64 = = 0x20b {
24
} else {
bail! ( " Could not find a matching magic field in the PE header " )
} ;
// Set the subsystem field (offset 68) to 2 (GUI subsystem)
// For all possible options, see: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-windows-specific-fields-image-only
let subsystem_offset = 68 ;
let subsystem_start =
start_pe as usize + standard_fields_size + subsystem_offset ;
let subsystem : u16 = 2 ;
bin [ ( subsystem_start ) .. ( subsystem_start + 2 ) ]
. copy_from_slice ( & subsystem . to_le_bytes ( ) ) ;
Ok ( ( ) )
}