@atom-js-org/cli 0.4.0-alpha.0 → 0.4.2-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/build.cjs +109 -6
- package/src/doctor.cjs +35 -9
- package/src/init.cjs +3 -3
package/package.json
CHANGED
package/src/build.cjs
CHANGED
|
@@ -106,7 +106,7 @@ async function localBuild(project, target, options = {}) {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
const manifest = {
|
|
109
|
-
atomjsVersion: '0.
|
|
109
|
+
atomjsVersion: '0.4.2-alpha.0',
|
|
110
110
|
target,
|
|
111
111
|
productName,
|
|
112
112
|
appId: project.config.appId,
|
|
@@ -311,6 +311,9 @@ async function createSeaLauncher({ executablePath, appDir, target, productName,
|
|
|
311
311
|
await run(process.execPath, ['--experimental-sea-config', configPath], { cwd: work });
|
|
312
312
|
await fs.promises.copyFile(process.execPath, executablePath);
|
|
313
313
|
|
|
314
|
+
if (target === 'windows') {
|
|
315
|
+
await prepareWindowsExecutableForInjection(executablePath);
|
|
316
|
+
}
|
|
314
317
|
if (target === 'macos' && commandExists('codesign', ['--version'])) {
|
|
315
318
|
spawnSync('codesign', ['--remove-signature', executablePath], { stdio: 'ignore' });
|
|
316
319
|
}
|
|
@@ -320,13 +323,79 @@ async function createSeaLauncher({ executablePath, appDir, target, productName,
|
|
|
320
323
|
machoSegmentName: 'NODE_SEA'
|
|
321
324
|
});
|
|
322
325
|
|
|
323
|
-
if (target
|
|
326
|
+
if (target === 'windows') {
|
|
327
|
+
await setWindowsGuiSubsystem(executablePath);
|
|
328
|
+
} else {
|
|
329
|
+
await fs.promises.chmod(executablePath, 0o755);
|
|
330
|
+
}
|
|
324
331
|
if (target === 'macos' && commandExists('codesign', ['--version'])) {
|
|
325
332
|
await run('codesign', ['--force', '--sign', '-', executablePath]);
|
|
326
333
|
}
|
|
327
334
|
await fse.remove(work);
|
|
328
335
|
}
|
|
329
336
|
|
|
337
|
+
|
|
338
|
+
async function prepareWindowsExecutableForInjection(executablePath) {
|
|
339
|
+
const image = await fs.promises.readFile(executablePath);
|
|
340
|
+
const pe = readPortableExecutableLayout(image);
|
|
341
|
+
const certificateDirectory = pe.dataDirectoryOffset + (8 * 4);
|
|
342
|
+
const certificateOffset = image.readUInt32LE(certificateDirectory);
|
|
343
|
+
const certificateSize = image.readUInt32LE(certificateDirectory + 4);
|
|
344
|
+
|
|
345
|
+
image.writeUInt32LE(0, certificateDirectory);
|
|
346
|
+
image.writeUInt32LE(0, certificateDirectory + 4);
|
|
347
|
+
image.writeUInt32LE(0, pe.optionalHeaderOffset + 64);
|
|
348
|
+
|
|
349
|
+
const certificateEnd = certificateOffset + certificateSize;
|
|
350
|
+
const output = certificateOffset > 0 && certificateSize > 0 && certificateEnd === image.length
|
|
351
|
+
? image.subarray(0, certificateOffset)
|
|
352
|
+
: image;
|
|
353
|
+
await fs.promises.writeFile(executablePath, output);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
async function setWindowsGuiSubsystem(executablePath) {
|
|
357
|
+
const image = await fs.promises.readFile(executablePath);
|
|
358
|
+
const pe = readPortableExecutableLayout(image);
|
|
359
|
+
image.writeUInt16LE(2, pe.optionalHeaderOffset + 68);
|
|
360
|
+
image.writeUInt32LE(0, pe.optionalHeaderOffset + 64);
|
|
361
|
+
await fs.promises.writeFile(executablePath, image);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function readPortableExecutableLayout(image) {
|
|
365
|
+
if (image.length < 0x100 || image.toString('ascii', 0, 2) !== 'MZ') {
|
|
366
|
+
throw new Error('AtomJS expected a Windows PE executable but the DOS header is missing.');
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const peOffset = image.readUInt32LE(0x3c);
|
|
370
|
+
const coffHeaderSize = 24;
|
|
371
|
+
if (peOffset < 0x40 || peOffset + coffHeaderSize > image.length || image.readUInt32LE(peOffset) !== 0x00004550) {
|
|
372
|
+
throw new Error('AtomJS expected a Windows PE executable but the PE header is invalid.');
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const optionalHeaderSize = image.readUInt16LE(peOffset + 20);
|
|
376
|
+
const optionalHeaderOffset = peOffset + coffHeaderSize;
|
|
377
|
+
const optionalHeaderEnd = optionalHeaderOffset + optionalHeaderSize;
|
|
378
|
+
if (optionalHeaderSize < 2 || optionalHeaderEnd > image.length) {
|
|
379
|
+
throw new Error('AtomJS expected a complete Windows PE optional header.');
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const magic = image.readUInt16LE(optionalHeaderOffset);
|
|
383
|
+
if (magic !== 0x10b && magic !== 0x20b) {
|
|
384
|
+
throw new Error(`AtomJS does not support Windows PE optional-header magic 0x${magic.toString(16)}.`);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const dataDirectoryOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96);
|
|
388
|
+
const certificateDirectoryEnd = dataDirectoryOffset + (8 * 5);
|
|
389
|
+
if (certificateDirectoryEnd > optionalHeaderEnd) {
|
|
390
|
+
throw new Error('AtomJS expected the Windows PE certificate data directory.');
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return {
|
|
394
|
+
optionalHeaderOffset,
|
|
395
|
+
dataDirectoryOffset
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
330
399
|
function createEmbeddedLauncherSource(productName) {
|
|
331
400
|
return `
|
|
332
401
|
'use strict';
|
|
@@ -410,6 +479,14 @@ async function packageWindows({ project, buildRoot, unpacked, executableName, pr
|
|
|
410
479
|
const nsisPath = path.join(buildRoot, 'installer.nsi');
|
|
411
480
|
const installerPath = path.join(buildRoot, `${productName} Installer.exe`);
|
|
412
481
|
const escapedSource = unpacked.replace(/\\/g, '\\\\');
|
|
482
|
+
const version = String(project.packageJson.version || '0.0.0');
|
|
483
|
+
const author = project.packageJson.author;
|
|
484
|
+
const publisher = typeof author === 'string'
|
|
485
|
+
? author
|
|
486
|
+
: author && typeof author === 'object' && author.name
|
|
487
|
+
? author.name
|
|
488
|
+
: 'AtomJS application';
|
|
489
|
+
const uninstallKey = `Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${escapeNsis(project.config.appId)}`;
|
|
413
490
|
const credit = project.config.installerCredit
|
|
414
491
|
? `!define MUI_WELCOMEPAGE_TEXT "This installer will install ${escapeNsis(productName)}.$\\r$\\n$\\r$\\nPowered by AtomJS — https://github.com/Atom-js-org/atom"`
|
|
415
492
|
: '';
|
|
@@ -427,27 +504,40 @@ ${credit}
|
|
|
427
504
|
!insertmacro MUI_PAGE_WELCOME
|
|
428
505
|
!insertmacro MUI_PAGE_DIRECTORY
|
|
429
506
|
!insertmacro MUI_PAGE_INSTFILES
|
|
507
|
+
!define MUI_FINISHPAGE_RUN "$INSTDIR\\${escapeNsis(executableName)}"
|
|
430
508
|
!insertmacro MUI_PAGE_FINISH
|
|
509
|
+
!insertmacro MUI_UNPAGE_CONFIRM
|
|
510
|
+
!insertmacro MUI_UNPAGE_INSTFILES
|
|
431
511
|
!insertmacro MUI_LANGUAGE "English"
|
|
432
512
|
Section "Install"
|
|
513
|
+
SetShellVarContext current
|
|
433
514
|
SetOutPath "$INSTDIR"
|
|
434
515
|
File /r "${escapedSource}\\*"
|
|
435
516
|
CreateDirectory "$SMPROGRAMS\\${escapeNsis(productName)}"
|
|
436
517
|
CreateShortcut "$SMPROGRAMS\\${escapeNsis(productName)}\\${escapeNsis(productName)}.lnk" "$INSTDIR\\${escapeNsis(executableName)}"
|
|
437
518
|
CreateShortcut "$DESKTOP\\${escapeNsis(productName)}.lnk" "$INSTDIR\\${escapeNsis(executableName)}"
|
|
438
519
|
WriteUninstaller "$INSTDIR\\Uninstall.exe"
|
|
520
|
+
WriteRegStr HKCU "${uninstallKey}" "DisplayName" "${escapeNsis(productName)}"
|
|
521
|
+
WriteRegStr HKCU "${uninstallKey}" "DisplayVersion" "${escapeNsis(version)}"
|
|
522
|
+
WriteRegStr HKCU "${uninstallKey}" "Publisher" "${escapeNsis(publisher)}"
|
|
523
|
+
WriteRegStr HKCU "${uninstallKey}" "DisplayIcon" "$INSTDIR\\${escapeNsis(executableName)}"
|
|
524
|
+
WriteRegStr HKCU "${uninstallKey}" "UninstallString" "$\\\"$INSTDIR\\Uninstall.exe$\\\""
|
|
525
|
+
WriteRegDWORD HKCU "${uninstallKey}" "NoModify" 1
|
|
526
|
+
WriteRegDWORD HKCU "${uninstallKey}" "NoRepair" 1
|
|
439
527
|
SectionEnd
|
|
440
528
|
Section "Uninstall"
|
|
529
|
+
SetShellVarContext current
|
|
441
530
|
Delete "$DESKTOP\\${escapeNsis(productName)}.lnk"
|
|
442
531
|
RMDir /r "$SMPROGRAMS\\${escapeNsis(productName)}"
|
|
443
|
-
DeleteRegKey HKCU "
|
|
532
|
+
DeleteRegKey HKCU "${uninstallKey}"
|
|
444
533
|
RMDir /r "$INSTDIR"
|
|
445
534
|
SectionEnd
|
|
446
535
|
`;
|
|
447
536
|
await fs.promises.writeFile(nsisPath, script.trimStart(), 'utf8');
|
|
448
537
|
|
|
449
|
-
|
|
450
|
-
|
|
538
|
+
const makensis = resolveNsisExecutable();
|
|
539
|
+
if (makensis) {
|
|
540
|
+
await run(makensis, [nsisPath]);
|
|
451
541
|
if (fs.existsSync(installerPath)) outputs.push(installerPath);
|
|
452
542
|
} else {
|
|
453
543
|
console.warn('NSIS was not found; installer.nsi was generated but the .exe installer was skipped.');
|
|
@@ -455,6 +545,19 @@ SectionEnd
|
|
|
455
545
|
return outputs;
|
|
456
546
|
}
|
|
457
547
|
|
|
548
|
+
function resolveNsisExecutable() {
|
|
549
|
+
const candidates = [
|
|
550
|
+
process.env.MAKENSIS_PATH,
|
|
551
|
+
process.env.NSIS_HOME && path.join(process.env.NSIS_HOME, 'makensis.exe'),
|
|
552
|
+
process.env['ProgramFiles(x86)'] && path.join(process.env['ProgramFiles(x86)'], 'NSIS', 'makensis.exe'),
|
|
553
|
+
process.env.ProgramFiles && path.join(process.env.ProgramFiles, 'NSIS', 'makensis.exe'),
|
|
554
|
+
process.env.LOCALAPPDATA && path.join(process.env.LOCALAPPDATA, 'Programs', 'NSIS', 'makensis.exe')
|
|
555
|
+
].filter(Boolean);
|
|
556
|
+
|
|
557
|
+
if (commandExists('makensis', ['/VERSION'])) return 'makensis';
|
|
558
|
+
return candidates.find((candidate) => fs.existsSync(candidate)) || null;
|
|
559
|
+
}
|
|
560
|
+
|
|
458
561
|
async function packageMacOS({ project, buildRoot, unpacked, executableName, productName, hostSource }) {
|
|
459
562
|
const outputs = [];
|
|
460
563
|
const appBundle = path.join(buildRoot, `${productName}.app`);
|
|
@@ -693,4 +796,4 @@ function xml(value) {
|
|
|
693
796
|
return String(value).replace(/[<>&'\"]/g, (char) => ({ '<': '<', '>': '>', '&': '&', "'": ''', '"': '"' })[char]);
|
|
694
797
|
}
|
|
695
798
|
|
|
696
|
-
module.exports = { buildCommand, localBuild, createApplicationPayload };
|
|
799
|
+
module.exports = { buildCommand, localBuild, createApplicationPayload, readPortableExecutableLayout };
|
package/src/doctor.cjs
CHANGED
|
@@ -12,8 +12,9 @@ async function doctorCommand(options = {}) {
|
|
|
12
12
|
|
|
13
13
|
if (process.platform === 'win32') {
|
|
14
14
|
rows.push(check('PowerShell', commandExists('powershell.exe', ['-NoProfile', '-Command', '$PSVersionTable.PSVersion.ToString()'])));
|
|
15
|
-
|
|
16
|
-
rows.push(check('
|
|
15
|
+
const webView2Version = getWindowsWebView2Version();
|
|
16
|
+
rows.push(check('WebView2 Runtime', Boolean(webView2Version), webView2Version || 'required by the native WebView'));
|
|
17
|
+
rows.push(check('NSIS (optional installer)', Boolean(resolveNsisExecutable())));
|
|
17
18
|
} else if (process.platform === 'darwin') {
|
|
18
19
|
rows.push(check('Xcode Command Line Tools', commandExists('/usr/bin/xcrun', ['--version']), 'required to compile the native Cocoa host'));
|
|
19
20
|
rows.push(check('Clang', commandExists('/usr/bin/xcrun', ['clang', '--version'])));
|
|
@@ -69,18 +70,43 @@ function pkgConfigExists(name) {
|
|
|
69
70
|
return !result.error && result.status === 0;
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
function
|
|
73
|
+
function getWindowsWebView2Version() {
|
|
73
74
|
const script = `
|
|
74
75
|
$paths = @(
|
|
75
|
-
'HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\EdgeUpdate\\Clients\\{
|
|
76
|
-
'HKLM:\\SOFTWARE\\Microsoft\\EdgeUpdate\\Clients\\{
|
|
77
|
-
'HKCU:\\Software\\Microsoft\\EdgeUpdate\\Clients\\{
|
|
76
|
+
'HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\EdgeUpdate\\Clients\\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}',
|
|
77
|
+
'HKLM:\\SOFTWARE\\Microsoft\\EdgeUpdate\\Clients\\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}',
|
|
78
|
+
'HKCU:\\Software\\Microsoft\\EdgeUpdate\\Clients\\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}'
|
|
78
79
|
)
|
|
79
|
-
foreach ($p in $paths) {
|
|
80
|
+
foreach ($p in $paths) {
|
|
81
|
+
try {
|
|
82
|
+
$version = Get-ItemPropertyValue -Path $p -Name 'pv' -ErrorAction Stop
|
|
83
|
+
if ($version -and $version -ne '0.0.0.0') {
|
|
84
|
+
Write-Output $version
|
|
85
|
+
exit 0
|
|
86
|
+
}
|
|
87
|
+
} catch {}
|
|
88
|
+
}
|
|
80
89
|
exit 1
|
|
81
90
|
`;
|
|
82
|
-
const result = spawnSync('powershell.exe', ['-NoProfile', '-Command', script], {
|
|
83
|
-
|
|
91
|
+
const result = spawnSync('powershell.exe', ['-NoProfile', '-Command', script], {
|
|
92
|
+
encoding: 'utf8',
|
|
93
|
+
windowsHide: true
|
|
94
|
+
});
|
|
95
|
+
if (result.error || result.status !== 0) return null;
|
|
96
|
+
return String(result.stdout || '').trim() || null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function resolveNsisExecutable() {
|
|
100
|
+
const candidates = [
|
|
101
|
+
process.env.MAKENSIS_PATH,
|
|
102
|
+
process.env.NSIS_HOME && path.join(process.env.NSIS_HOME, 'makensis.exe'),
|
|
103
|
+
process.env['ProgramFiles(x86)'] && path.join(process.env['ProgramFiles(x86)'], 'NSIS', 'makensis.exe'),
|
|
104
|
+
process.env.ProgramFiles && path.join(process.env.ProgramFiles, 'NSIS', 'makensis.exe'),
|
|
105
|
+
process.env.LOCALAPPDATA && path.join(process.env.LOCALAPPDATA, 'Programs', 'NSIS', 'makensis.exe')
|
|
106
|
+
].filter(Boolean);
|
|
107
|
+
|
|
108
|
+
if (commandExists('makensis', ['/VERSION'])) return 'makensis';
|
|
109
|
+
return candidates.find((candidate) => fs.existsSync(candidate)) || null;
|
|
84
110
|
}
|
|
85
111
|
|
|
86
112
|
module.exports = { doctorCommand };
|
package/src/init.cjs
CHANGED
|
@@ -30,14 +30,14 @@ async function initCommand(directory, options = {}) {
|
|
|
30
30
|
start: 'atom run build'
|
|
31
31
|
},
|
|
32
32
|
dependencies: {
|
|
33
|
-
'@atom-js-org/runtime': '0.
|
|
34
|
-
electron: 'npm:@atom-js-org/electron@0.
|
|
33
|
+
'@atom-js-org/runtime': '0.4.2-alpha.0',
|
|
34
|
+
electron: 'npm:@atom-js-org/electron@0.4.2-alpha.0'
|
|
35
35
|
},
|
|
36
36
|
optionalDependencies: {
|
|
37
37
|
'webview-nodejs': '0.5.0'
|
|
38
38
|
},
|
|
39
39
|
devDependencies: {
|
|
40
|
-
'@atom-js-org/cli': '0.
|
|
40
|
+
'@atom-js-org/cli': '0.4.2-alpha.0'
|
|
41
41
|
},
|
|
42
42
|
overrides: {
|
|
43
43
|
tar: '7.5.20'
|