@atom-js-org/cli 0.3.0-alpha.0 → 0.4.0-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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atom-js-org/cli",
3
- "version": "0.3.0-alpha.0",
3
+ "version": "0.4.0-alpha.0",
4
4
  "description": "CLI for running and building AtomJS desktop applications.",
5
5
  "bin": {
6
6
  "atom": "bin/atom.cjs"
package/src/build.cjs CHANGED
@@ -39,8 +39,8 @@ async function localBuild(project, target, options = {}) {
39
39
  }
40
40
 
41
41
  const buildRoot = path.join(project.root, 'build', target);
42
- const unpacked = path.join(buildRoot, 'unpacked');
43
- const appDir = path.join(unpacked, 'app');
42
+ const unpacked = path.join(buildRoot, 'portable');
43
+ const appDir = null;
44
44
  const productName = sanitizeFilename(project.config.productName);
45
45
 
46
46
  console.log(`\nAtomJS build (${target})`);
@@ -50,7 +50,8 @@ async function localBuild(project, target, options = {}) {
50
50
  await fse.remove(buildRoot);
51
51
  await fse.ensureDir(unpacked);
52
52
 
53
- const stageRoot = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'atomjs-stage-'));
53
+ const stageBase = await resolveShortStageBase();
54
+ const stageRoot = await fs.promises.mkdtemp(path.join(stageBase, 's-'));
54
55
  const stagedApp = path.join(stageRoot, 'app');
55
56
  const payloadPath = path.join(stageRoot, 'atom-app.payload.gz');
56
57
 
@@ -59,21 +60,8 @@ async function localBuild(project, target, options = {}) {
59
60
  await vendorFramework(stagedApp, project, target);
60
61
  await installProductionDependencies(stagedApp, options.skipInstall, target);
61
62
 
62
- if (target === 'macos') {
63
- console.log('Embedding application code and production dependencies into the native executable...');
64
- await createApplicationPayload(stagedApp, payloadPath);
65
- } else {
66
- await fse.move(stagedApp, appDir, { overwrite: true });
67
- }
68
-
69
- if (target !== 'macos') {
70
- const runtimeDir = path.join(unpacked, 'runtime');
71
- await fse.ensureDir(runtimeDir);
72
- const runtimeNodeName = target === 'windows' ? 'node.exe' : 'node';
73
- const runtimeNodePath = path.join(runtimeDir, runtimeNodeName);
74
- await fs.promises.copyFile(process.execPath, runtimeNodePath);
75
- if (target !== 'windows') await fs.promises.chmod(runtimeNodePath, 0o755);
76
- }
63
+ console.log('Embedding application code and production dependencies into the executable...');
64
+ await createApplicationPayload(stagedApp, payloadPath);
77
65
 
78
66
  const executableName = target === 'windows' ? `${productName}.exe` : productName;
79
67
  const executablePath = path.join(unpacked, executableName);
@@ -82,7 +70,7 @@ async function localBuild(project, target, options = {}) {
82
70
  appDir,
83
71
  target,
84
72
  productName,
85
- payloadPath: target === 'macos' ? payloadPath : null
73
+ payloadPath
86
74
  });
87
75
 
88
76
  const creditPath = path.join(unpacked, 'ATOMJS-CREDIT.txt');
@@ -138,6 +126,28 @@ async function localBuild(project, target, options = {}) {
138
126
  }
139
127
  }
140
128
 
129
+
130
+ async function resolveShortStageBase() {
131
+ const configured = process.env.ATOMJS_TEMP_DIR || process.env.ATOM_TEMP_DIR;
132
+ const candidates = configured
133
+ ? [path.resolve(configured)]
134
+ : process.platform === 'win32'
135
+ ? [path.join(path.parse(process.cwd()).root, '.atomjs-tmp'), path.join(os.tmpdir(), 'atomjs')]
136
+ : [path.join(os.tmpdir(), 'atomjs')];
137
+
138
+ let lastError;
139
+ for (const candidate of candidates) {
140
+ try {
141
+ await fse.ensureDir(candidate);
142
+ await fs.promises.access(candidate, fs.constants.W_OK);
143
+ return candidate;
144
+ } catch (error) {
145
+ lastError = error;
146
+ }
147
+ }
148
+ throw new Error(`AtomJS could not create a writable short staging directory: ${lastError ? lastError.message : 'unknown error'}`);
149
+ }
150
+
141
151
  async function copyApplication(projectRoot, appDir) {
142
152
  await fse.copy(projectRoot, appDir, {
143
153
  dereference: false,
@@ -284,9 +294,8 @@ async function createSeaLauncher({ executablePath, appDir, target, productName,
284
294
  const blobPath = path.join(work, target === 'windows' ? 'sea-prep.blob.exe' : 'sea-prep.blob');
285
295
  const configPath = path.join(work, 'sea-config.json');
286
296
 
287
- const launcher = payloadPath
288
- ? createEmbeddedLauncherSource(productName)
289
- : createLooseLauncherSource();
297
+ if (!payloadPath) throw new Error('AtomJS SEA payload is required.');
298
+ const launcher = createEmbeddedLauncherSource(productName);
290
299
  await fs.promises.writeFile(launcherPath, launcher, 'utf8');
291
300
 
292
301
  const seaConfig = {
@@ -295,7 +304,7 @@ async function createSeaLauncher({ executablePath, appDir, target, productName,
295
304
  disableExperimentalSEAWarning: true,
296
305
  useSnapshot: false,
297
306
  useCodeCache: false,
298
- ...(payloadPath ? { assets: { 'atom-app': payloadPath } } : {})
307
+ assets: { 'atom-app': payloadPath }
299
308
  };
300
309
  await fs.promises.writeFile(configPath, JSON.stringify(seaConfig, null, 2));
301
310
 
@@ -318,33 +327,6 @@ async function createSeaLauncher({ executablePath, appDir, target, productName,
318
327
  await fse.remove(work);
319
328
  }
320
329
 
321
- function createLooseLauncherSource() {
322
- return `
323
- 'use strict';
324
- const path = require('node:path');
325
- const fs = require('node:fs');
326
- const { createRequire } = require('node:module');
327
-
328
- const executableDir = path.dirname(process.execPath);
329
- const appDir = path.join(executableDir, 'app');
330
- const packagePath = path.join(appDir, 'package.json');
331
- if (!fs.existsSync(packagePath)) {
332
- console.error('AtomJS application files are missing:', appDir);
333
- process.exit(1);
334
- }
335
-
336
- const runtimeNode = path.join(executableDir, 'runtime', process.platform === 'win32' ? 'node.exe' : 'node');
337
- process.chdir(appDir);
338
- process.env.ATOM_PROJECT_ROOT = appDir;
339
- process.env.ATOM_BUILD = '1';
340
- process.env.ATOM_NODE_EXECUTABLE = runtimeNode;
341
- const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
342
- const mainPath = path.resolve(appDir, pkg.main || 'main.js');
343
- const load = createRequire(packagePath);
344
- load(mainPath);
345
- `;
346
- }
347
-
348
330
  function createEmbeddedLauncherSource(productName) {
349
331
  return `
350
332
  'use strict';
@@ -354,6 +336,7 @@ const os = require('node:os');
354
336
  const crypto = require('node:crypto');
355
337
  const zlib = require('node:zlib');
356
338
  const { createRequire } = require('node:module');
339
+ const { pathToFileURL } = require('node:url');
357
340
  const { getAsset } = require('node:sea');
358
341
 
359
342
  const productName = ${JSON.stringify(productName)};
@@ -397,7 +380,20 @@ const executableDir = path.dirname(process.execPath);
397
380
  process.chdir(appDir);
398
381
  process.env.ATOM_PROJECT_ROOT = appDir;
399
382
  process.env.ATOM_BUILD = '1';
383
+ process.env.ATOM_EMBEDDED_RUNTIME = '1';
384
+ process.env.ATOM_WINDOW_HOST_ENTRY = path.join(appDir, 'vendor', 'atomjs', 'src', 'runtime', 'window-host.mjs');
400
385
  process.env.ATOM_MACOS_HOST_EXECUTABLE = path.join(executableDir, 'AtomJSWindowHost');
386
+ const hostModeIndex = process.argv.indexOf('--atomjs-window-host');
387
+ if (hostModeIndex !== -1) {
388
+ const hostEntry = process.env.ATOM_WINDOW_HOST_ENTRY;
389
+ const configPath = process.argv[hostModeIndex + 1];
390
+ process.argv = [process.argv[0], hostEntry, configPath];
391
+ import(pathToFileURL(hostEntry).href).catch((error) => {
392
+ console.error(error && error.stack ? error.stack : error);
393
+ process.exit(1);
394
+ });
395
+ return;
396
+ }
401
397
  const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
402
398
  const mainPath = path.resolve(appDir, pkg.main || 'main.js');
403
399
  const load = createRequire(packagePath);
@@ -419,11 +415,14 @@ async function packageWindows({ project, buildRoot, unpacked, executableName, pr
419
415
  : '';
420
416
  const script = `
421
417
  Unicode true
418
+ SetCompressor /SOLID lzma
422
419
  !include "MUI2.nsh"
423
420
  Name "${escapeNsis(productName)}"
424
421
  OutFile "${installerPath.replace(/\\/g, '\\\\')}"
425
422
  InstallDir "$LOCALAPPDATA\\Programs\\${escapeNsis(productName)}"
426
423
  RequestExecutionLevel user
424
+ ShowInstDetails show
425
+ ShowUninstDetails show
427
426
  ${credit}
428
427
  !insertmacro MUI_PAGE_WELCOME
429
428
  !insertmacro MUI_PAGE_DIRECTORY
@@ -441,6 +440,7 @@ SectionEnd
441
440
  Section "Uninstall"
442
441
  Delete "$DESKTOP\\${escapeNsis(productName)}.lnk"
443
442
  RMDir /r "$SMPROGRAMS\\${escapeNsis(productName)}"
443
+ DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${escapeNsis(project.config.appId)}"
444
444
  RMDir /r "$INSTDIR"
445
445
  SectionEnd
446
446
  `;
@@ -555,8 +555,6 @@ async function packageLinux({ project, buildRoot, unpacked, executableName, prod
555
555
  await fse.ensureDir(usrBin);
556
556
  await fse.ensureDir(usrLibApp);
557
557
  await fse.copy(path.join(unpacked, executableName), path.join(usrBin, productName));
558
- await fse.copy(path.join(unpacked, 'app'), path.join(usrBin, 'app'));
559
- await fse.copy(path.join(unpacked, 'runtime'), path.join(usrBin, 'runtime'));
560
558
  await fse.copy(path.join(unpacked, 'ATOMJS-CREDIT.txt'), path.join(usrLibApp, 'ATOMJS-CREDIT.txt'));
561
559
 
562
560
  const appRun = `#!/bin/sh\nHERE="$(dirname "$(readlink -f "$0")")"\nexec "$HERE/usr/bin/${productName}" "$@"\n`;
package/src/utils.cjs CHANGED
@@ -54,9 +54,17 @@ function loadProject(projectInput) {
54
54
  };
55
55
  }
56
56
 
57
+ function normalizeSpawn(command, args) {
58
+ if (process.platform === 'win32' && /\.(?:cmd|bat)$/i.test(command)) {
59
+ return { command: process.env.ComSpec || 'cmd.exe', args: ['/d', '/s', '/c', command, ...args] };
60
+ }
61
+ return { command, args };
62
+ }
63
+
57
64
  function run(command, args, options = {}) {
58
65
  return new Promise((resolve, reject) => {
59
- const child = spawn(command, args, {
66
+ const normalized = normalizeSpawn(command, args);
67
+ const child = spawn(normalized.command, normalized.args, {
60
68
  cwd: options.cwd || process.cwd(),
61
69
  env: options.env || process.env,
62
70
  stdio: options.stdio || 'inherit',
@@ -71,7 +79,8 @@ function run(command, args, options = {}) {
71
79
  }
72
80
 
73
81
  function capture(command, args, options = {}) {
74
- const result = spawnSync(command, args, {
82
+ const normalized = normalizeSpawn(command, args);
83
+ const result = spawnSync(normalized.command, normalized.args, {
75
84
  cwd: options.cwd || process.cwd(),
76
85
  env: options.env || process.env,
77
86
  encoding: 'utf8',
@@ -86,7 +95,8 @@ function capture(command, args, options = {}) {
86
95
  }
87
96
 
88
97
  function commandExists(command, args = ['--version']) {
89
- const result = spawnSync(command, args, { stdio: 'ignore', shell: false });
98
+ const normalized = normalizeSpawn(command, args);
99
+ const result = spawnSync(normalized.command, normalized.args, { stdio: 'ignore', shell: false });
90
100
  return !result.error && result.status === 0;
91
101
  }
92
102