@@ -5,6 +5,8 @@ import fsp from 'node:fs/promises';
55import path from 'node:path' ;
66import { spawnSync } from 'node:child_process' ;
77import { fileURLToPath } from 'node:url' ;
8+ import makeDistributablesModule from '@electron-forge/core/dist/api/make.js' ;
9+ import packageApplicationModule from '@electron-forge/core/dist/api/package.js' ;
810import { getMsixPaths } from './msix-config.js' ;
911import { loadStorePackageConfig } from './store-package-config.js' ;
1012import { buildStorePurchaseAddon } from './build-store-purchase-addon.js' ;
@@ -21,10 +23,8 @@ const packageDir = configuredPackageOutputDir
2123 : path . resolve ( projectRoot , configuredPackageOutputDir ) )
2224 : path . join ( projectRoot , 'pkg' ) ;
2325const packageJson = JSON . parse ( fs . readFileSync ( path . join ( projectRoot , 'package.json' ) , 'utf8' ) ) ;
24-
25- function resolveForgeCliScriptPath ( ) {
26- return path . join ( projectRoot , 'node_modules' , '@electron-forge' , 'cli' , 'dist' , 'electron-forge.js' ) ;
27- }
26+ const makeDistributables = makeDistributablesModule . default ?? makeDistributablesModule ;
27+ const packageApplication = packageApplicationModule . default ?? packageApplicationModule ;
2828
2929function run ( command , args , options = { } ) {
3030 const result = spawnSync ( command , args , {
@@ -140,84 +140,6 @@ function unique(values) {
140140 return [ ...new Set ( values ) ] ;
141141}
142142
143- function inferPackagedRootFromAsar ( asarPath , platform ) {
144- const asarDirectory = path . dirname ( asarPath ) ;
145-
146- if ( platform === 'darwin' ) {
147- const resourcesDirectory = path . basename ( asarDirectory ) ;
148- const contentsDirectory = path . basename ( path . dirname ( asarDirectory ) ) ;
149- const bundlePath = path . dirname ( path . dirname ( asarDirectory ) ) ;
150-
151- if ( resourcesDirectory === 'Resources' && contentsDirectory === 'Contents' && bundlePath . endsWith ( '.app' ) ) {
152- return bundlePath ;
153- }
154-
155- return null ;
156- }
157-
158- if ( path . basename ( asarDirectory ) !== 'resources' ) {
159- return null ;
160- }
161-
162- return path . dirname ( asarDirectory ) ;
163- }
164-
165- async function pathExists ( targetPath ) {
166- try {
167- await fsp . access ( targetPath ) ;
168- return true ;
169- } catch {
170- return false ;
171- }
172- }
173-
174- async function findPaths ( rootPath , predicate , maxDepth = 4 , depth = 0 ) {
175- const matches = [ ] ;
176- let entries = [ ] ;
177-
178- try {
179- entries = await fsp . readdir ( rootPath , { withFileTypes : true } ) ;
180- } catch {
181- return matches ;
182- }
183-
184- for ( const entry of entries ) {
185- const candidatePath = path . join ( rootPath , entry . name ) ;
186- if ( await predicate ( candidatePath , entry , depth ) ) {
187- matches . push ( candidatePath ) ;
188- }
189-
190- if ( entry . isDirectory ( ) && depth < maxDepth ) {
191- matches . push ( ...await findPaths ( candidatePath , predicate , maxDepth , depth + 1 ) ) ;
192- }
193- }
194-
195- return matches ;
196- }
197-
198- async function describeDirectoryTree ( rootPath , maxDepth = 3 , depth = 0 ) {
199- let entries = [ ] ;
200-
201- try {
202- entries = await fsp . readdir ( rootPath , { withFileTypes : true } ) ;
203- } catch {
204- return [ ] ;
205- }
206-
207- const lines = [ ] ;
208- for ( const entry of entries . sort ( ( left , right ) => left . name . localeCompare ( right . name ) ) ) {
209- const candidatePath = path . join ( rootPath , entry . name ) ;
210- const relativePath = path . relative ( projectRoot , candidatePath ) || path . basename ( candidatePath ) ;
211- lines . push ( `${ ' ' . repeat ( depth ) } - ${ relativePath } ${ entry . isDirectory ( ) ? '/' : '' } ` ) ;
212-
213- if ( entry . isDirectory ( ) && depth < maxDepth ) {
214- lines . push ( ...await describeDirectoryTree ( candidatePath , maxDepth , depth + 1 ) ) ;
215- }
216- }
217-
218- return lines ;
219- }
220-
221143async function resetOutputDirectories ( ) {
222144 await fsp . rm ( outDir , { recursive : true , force : true } ) ;
223145 await fsp . rm ( packageDir , { recursive : true , force : true } ) ;
@@ -236,94 +158,16 @@ function resolveUnpackedDestination(platform, arch) {
236158 return path . join ( packageDir , arch === 'arm64' ? 'mac-arm64' : 'mac' ) ;
237159}
238160
239- async function findFallbackPackagedPath ( platform , arch ) {
240- const executableName = platform === 'win32' ? `${ packageJson . productName || packageJson . name } .exe` : null ;
241- const fallbackExecutableName = platform === 'linux' ? `${ packageJson . productName || packageJson . name } ` : null ;
242- const asarPaths = await findPaths ( outDir , async ( candidatePath , entry ) => (
243- entry . isFile ( ) && entry . name === 'app.asar'
244- ) , 8 ) ;
245- const candidateRoots = unique (
246- asarPaths
247- . map ( asarPath => inferPackagedRootFromAsar ( asarPath , platform ) )
248- . filter ( Boolean ) ,
249- ) . sort ( ( left , right ) => left . localeCompare ( right ) ) ;
250-
251- for ( const candidateRoot of candidateRoots ) {
252- if ( platform === 'win32' ) {
253- if ( await pathExists ( path . join ( candidateRoot , executableName ) ) ) {
254- return candidateRoot ;
255- }
256- continue ;
257- }
258-
259- if ( platform === 'linux' && fallbackExecutableName ) {
260- if ( await pathExists ( path . join ( candidateRoot , fallbackExecutableName ) ) ) {
261- return candidateRoot ;
262- }
263- }
264-
265- return candidateRoot ;
266- }
267-
268- if ( platform === 'darwin' ) {
269- const bundles = await findPaths ( outDir , async ( candidatePath , entry ) => entry . isDirectory ( ) && entry . name . endsWith ( '.app' ) , 8 ) ;
270- return bundles [ 0 ] || null ;
271- }
272-
273- return null ;
274- }
275-
276- async function resolvePackagedApplicationPath ( platform , arch , packagedPath ) {
277- if ( packagedPath && await pathExists ( packagedPath ) ) {
278- return packagedPath ;
279- }
280-
281- const fallbackPath = await findFallbackPackagedPath ( platform , arch ) ;
282- if ( fallbackPath ) {
283- console . warn ( `[electron-forge] Using fallback packaged application path from ${ path . relative ( projectRoot , fallbackPath ) } ` ) ;
284- }
285-
286- return fallbackPath ;
287- }
288-
289- async function waitForPackagedApplicationPath ( platform , arch , packagedPath , timeoutMs = 30000 ) {
290- const startedAt = Date . now ( ) ;
291-
292- while ( ( Date . now ( ) - startedAt ) < timeoutMs ) {
293- const resolvedPackagedPath = await resolvePackagedApplicationPath ( platform , arch , packagedPath ) ;
294- if ( resolvedPackagedPath ) {
295- return resolvedPackagedPath ;
296- }
297-
298- await new Promise ( resolve => setTimeout ( resolve , 500 ) ) ;
299- }
300-
301- return null ;
302- }
303-
304161async function stagePackagedApplication ( platform , arch , packagedPath ) {
305- const resolvedPackagedPath = await waitForPackagedApplicationPath ( platform , arch , packagedPath ) ;
306- if ( ! resolvedPackagedPath ) {
307- const outEntries = await describeDirectoryTree ( outDir , 4 ) ;
308- const asarPaths = await findPaths ( outDir , async ( candidatePath , entry ) => (
309- entry . isFile ( ) && entry . name === 'app.asar'
310- ) , 8 ) ;
311- if ( outEntries . length > 0 ) {
312- console . warn ( `[electron-forge] out directory contents:\n${ outEntries . join ( '\n' ) } ` ) ;
313- } else {
314- console . warn ( '[electron-forge] out directory is empty or missing after packaging.' ) ;
315- }
316- if ( asarPaths . length > 0 ) {
317- console . warn ( `[electron-forge] discovered app.asar files:\n${ asarPaths . map ( candidatePath => `- ${ path . relative ( projectRoot , candidatePath ) } ` ) . join ( '\n' ) } ` ) ;
318- }
319- throw new Error ( `Unable to locate packaged application output for ${ platform } /${ arch } under ${ outDir } ` ) ;
162+ if ( ! packagedPath ) {
163+ throw new Error ( `Forge package() did not return a packagedPath for ${ platform } /${ arch } ` ) ;
320164 }
321165
322166 const destination = resolveUnpackedDestination ( platform , arch ) ;
323167 await fsp . rm ( destination , { recursive : true , force : true } ) ;
324168 await fsp . mkdir ( path . dirname ( destination ) , { recursive : true } ) ;
325169
326- await fsp . cp ( resolvedPackagedPath , destination , { recursive : true } ) ;
170+ await fsp . cp ( packagedPath , destination , { recursive : true } ) ;
327171 await materializeForgePackagingResources ( destination , platform ) ;
328172 console . log ( `[electron-forge] staged unpacked application ${ path . relative ( projectRoot , destination ) } ` ) ;
329173 return destination ;
@@ -480,13 +324,7 @@ async function recoverMacDmgDetachRace(error, options) {
480324}
481325
482326async function collectArtifacts ( makeResults ) {
483- let artifactPaths = unique ( makeResults . flatMap ( result => result . artifacts ) ) ;
484- if ( artifactPaths . length === 0 ) {
485- artifactPaths = await findFallbackMakeArtifacts ( ) ;
486- if ( artifactPaths . length > 0 ) {
487- console . warn ( `[electron-forge] Recovered Forge artifacts from ${ path . relative ( projectRoot , path . join ( outDir , 'make' ) ) } ` ) ;
488- }
489- }
327+ const artifactPaths = unique ( makeResults . flatMap ( result => result . artifacts ) ) ;
490328
491329 for ( const artifactPath of artifactPaths ) {
492330 const stats = await fsp . stat ( artifactPath ) ;
@@ -501,20 +339,6 @@ async function collectArtifacts(makeResults) {
501339 }
502340}
503341
504- async function findFallbackMakeArtifacts ( ) {
505- const makeRoot = path . join ( outDir , 'make' ) ;
506- const supportedExtensions = new Set ( [ '.appimage' , '.dmg' , '.exe' , '.msix' , '.zip' ] ) ;
507- const artifactPaths = await findPaths ( makeRoot , async ( candidatePath , entry ) => {
508- if ( ! entry . isFile ( ) ) {
509- return false ;
510- }
511-
512- return supportedExtensions . has ( path . extname ( candidatePath ) . toLowerCase ( ) ) ;
513- } , 5 ) ;
514-
515- return artifactPaths . sort ( ( left , right ) => left . localeCompare ( right ) ) ;
516- }
517-
518342async function createTarGzArtifact ( unpackedDir , platform , arch ) {
519343 const artifactName = `${ sanitizeArtifactNameSegment ( packageJson . productName || packageJson . name ) } -${ packageJson . version } -${ platform } -${ arch } .tar.gz` ;
520344 const artifactPath = path . join ( packageDir , artifactName ) ;
@@ -524,11 +348,6 @@ async function createTarGzArtifact(unpackedDir, platform, arch) {
524348 console . log ( `[electron-forge] collected ${ path . relative ( projectRoot , artifactPath ) } ` ) ;
525349}
526350
527- function runForgeCli ( subcommand , args ) {
528- const forgeCliScriptPath = resolveForgeCliScriptPath ( ) ;
529- run ( process . execPath , [ forgeCliScriptPath , subcommand , ...args , projectRoot ] ) ;
530- }
531-
532351async function main ( ) {
533352 ensureDarwinFileLimit ( ) ;
534353
@@ -539,14 +358,19 @@ async function main() {
539358 await buildStorePurchaseAddon ( { arch : options . arch } ) ;
540359 }
541360
542- runForgeCli ( 'package' , [
543- '--platform' ,
544- options . platform ,
545- '--arch' ,
546- options . arch ,
547- ] ) ;
361+ const packageResults = await packageApplication ( {
362+ dir : projectRoot ,
363+ platform : options . platform ,
364+ arch : options . arch ,
365+ outDir,
366+ interactive : false ,
367+ } ) ;
368+
369+ if ( packageResults . length !== 1 ) {
370+ throw new Error ( `Expected one packaged application, received ${ packageResults . length } ` ) ;
371+ }
548372
549- const unpackedDir = await stagePackagedApplication ( options . platform , options . arch , null ) ;
373+ const unpackedDir = await stagePackagedApplication ( options . platform , options . arch , packageResults [ 0 ] . packagedPath ) ;
550374
551375 if ( options . platform === 'win32' && options . targets . includes ( 'msix' ) ) {
552376 const { storeConfig } = await loadStorePackageConfig ( ) ;
@@ -564,24 +388,27 @@ async function main() {
564388
565389 const forgeTargets = mapForgeTargets ( options . platform , options . targets ) ;
566390 if ( forgeTargets . length > 0 ) {
391+ let makeResults ;
567392 try {
568- runForgeCli ( 'make' , [
569- '--platform' ,
570- options . platform ,
571- '-- arch' ,
572- options . arch ,
573- '--skip-package' ,
574- '--targets' ,
575- forgeTargets . join ( ',' ) ,
576- ] ) ;
393+ makeResults = await makeDistributables ( {
394+ dir : projectRoot ,
395+ platform : options . platform ,
396+ arch : options . arch ,
397+ outDir ,
398+ skipPackage : true ,
399+ overrideTargets : forgeTargets ,
400+ interactive : false ,
401+ } ) ;
577402 } catch ( error ) {
578- const recoveredArtifacts = await recoverMacDmgDetachRace ( error , options ) ;
579- if ( ! recoveredArtifacts ) {
403+ const recoveredResults = await recoverMacDmgDetachRace ( error , options ) ;
404+ if ( ! recoveredResults ) {
580405 throw error ;
581406 }
407+
408+ makeResults = recoveredResults ;
582409 }
583410
584- await collectArtifacts ( [ ] ) ;
411+ await collectArtifacts ( makeResults ) ;
585412 }
586413
587414 if ( options . targets . includes ( 'tar.gz' ) ) {
0 commit comments