@atom-js-org/cli 0.5.3-alpha.0 → 0.5.3-alpha.2

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/README.md CHANGED
@@ -8,9 +8,9 @@ atom run build
8
8
  atom build windows
9
9
  atom build macos
10
10
  atom build linux
11
- atom build all
11
+ atom build current --local
12
12
  ```
13
13
 
14
- `atom.config.json` controls artifact names, Windows EXE/NSIS branding, macOS app/DMG metadata and Linux AppImage/DEB/RPM output. New projects include default PNG and ICO assets that can be replaced directly.
14
+ `atom.config.json` controls artifact names, Windows EXE/NSIS branding, macOS app/DMG metadata and Linux AppImage/DEB/RPM output. New projects include default PNG and ICO assets that can be replaced directly. Builds are local-first and do not require GitHub Actions; run the target build on the matching operating system.
15
15
 
16
16
  Project: https://github.com/Atom-js-org/atom
package/bin/atom.cjs CHANGED
@@ -25,10 +25,10 @@ program
25
25
  program
26
26
  .command('build')
27
27
  .description('Build an AtomJS project')
28
- .argument('<target>', 'windows, macos, linux, or all')
28
+ .argument('<target>', 'current, windows, macos, linux, or all')
29
29
  .option('-p, --project <path>', 'project directory', process.cwd())
30
- .option('--local', 'never use the remote build workflow')
31
- .option('--remote', 'always use the remote build workflow')
30
+ .option('--local', 'build on this machine (default; kept for compatibility)')
31
+ .option('--remote', 'deprecated: remote GitHub Actions builds are disabled')
32
32
  .option('--skip-install', 'reuse staged node_modules when possible')
33
33
  .action(async (target, options) => buildCommand(target, options));
34
34
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atom-js-org/cli",
3
- "version": "0.5.3-alpha.0",
3
+ "version": "0.5.3-alpha.2",
4
4
  "description": "CLI for running and building AtomJS desktop applications.",
5
5
  "bin": {
6
6
  "atom": "bin/atom.cjs"
@@ -14,7 +14,7 @@
14
14
  "LICENSE"
15
15
  ],
16
16
  "engines": {
17
- "node": ">=20.12"
17
+ "node": ">=24"
18
18
  },
19
19
  "dependencies": {
20
20
  "archiver": "7.0.1",
package/src/build.cjs CHANGED
@@ -18,19 +18,36 @@ const {
18
18
  sanitizeFilename
19
19
  } = require('./utils.cjs');
20
20
  const { resolveElectronCompatibilityRoot } = require('./electron-compat.cjs');
21
+ const cliPackageVersion = require('../package.json').version;
21
22
 
22
23
  async function buildCommand(targetInput, options = {}) {
23
- const target = validateTarget(targetInput);
24
+ const requestedTarget = validateTarget(targetInput);
24
25
  const project = loadProject(options.project);
25
26
  const host = hostTarget();
27
+ const target = requestedTarget === 'current' ? host : requestedTarget;
26
28
 
27
- if (options.local && (target === 'all' || target !== host)) {
28
- throw new Error(`A local ${host} machine cannot produce a complete '${target}' release. Remove --local to use GitHub Actions.`);
29
+ if (options.remote) {
30
+ throw new Error([
31
+ 'Remote GitHub Actions builds are disabled.',
32
+ 'Build locally on the matching operating system instead:',
33
+ ' Windows: atom build windows --local',
34
+ ' macOS: atom build macos --local',
35
+ ' Linux: atom build linux --local'
36
+ ].join('\n'));
29
37
  }
30
38
 
31
- const needsRemote = options.remote || target === 'all' || target !== host;
32
- if (needsRemote) return remoteBuild(project, target);
33
- return localBuild(project, host, options);
39
+ if (target === 'all') {
40
+ throw new Error([
41
+ "'atom build all' cannot safely cross-build native WebView applications.",
42
+ 'Run the matching local build command once on each operating system.'
43
+ ].join('\n'));
44
+ }
45
+
46
+ if (target !== host) {
47
+ throw new Error(`Local target mismatch: this machine is ${host}, but '${target}' was requested. Build that target on a ${target} machine.`);
48
+ }
49
+
50
+ return localBuild(project, target, options);
34
51
  }
35
52
 
36
53
  async function localBuild(project, target, options = {}) {
@@ -60,8 +77,8 @@ async function localBuild(project, target, options = {}) {
60
77
  await vendorFramework(stagedApp, project, target);
61
78
  await installProductionDependencies(stagedApp, options.skipInstall, target);
62
79
 
63
- console.log('Embedding application code and production dependencies into the executable...');
64
- await createApplicationPayload(stagedApp, payloadPath);
80
+ console.log('Embedding application code, assets and production dependencies into the executable...');
81
+ const payloadSummary = await createApplicationPayload(stagedApp, payloadPath);
65
82
 
66
83
  const executableName = target === 'windows' ? `${productName}.exe` : productName;
67
84
  const executablePath = path.join(unpacked, executableName);
@@ -107,14 +124,29 @@ async function localBuild(project, target, options = {}) {
107
124
  outputs = await packageLinux({ project, buildRoot, unpacked, executableName, productName });
108
125
  }
109
126
 
127
+ const inventoryPath = path.join(buildRoot, 'packed-files.json');
128
+ await fs.promises.writeFile(inventoryPath, JSON.stringify({
129
+ format: 1,
130
+ fileCount: payloadSummary.fileCount,
131
+ sourceBytes: payloadSummary.sourceBytes,
132
+ compressedBytes: payloadSummary.compressedBytes,
133
+ files: payloadSummary.paths
134
+ }, null, 2));
135
+
110
136
  const manifest = {
111
- atomjsVersion: '0.5.2-alpha.0',
137
+ atomjsVersion: cliPackageVersion,
112
138
  target,
113
139
  productName,
114
140
  appId: project.config.appId,
115
141
  createdAt: new Date().toISOString(),
116
142
  run: path.relative(buildRoot, runPath),
117
143
  unpacked: path.relative(buildRoot, unpackedPath),
144
+ payload: {
145
+ fileCount: payloadSummary.fileCount,
146
+ sourceBytes: payloadSummary.sourceBytes,
147
+ compressedBytes: payloadSummary.compressedBytes,
148
+ inventory: path.basename(inventoryPath)
149
+ },
118
150
  outputs: outputs.map((file) => path.relative(buildRoot, file))
119
151
  };
120
152
  await fs.promises.writeFile(path.join(buildRoot, 'manifest.json'), JSON.stringify(manifest, null, 2));
@@ -188,11 +220,18 @@ async function vendorFramework(appDir, project, target) {
188
220
  '@atom-js-org/runtime': 'file:vendor/atomjs',
189
221
  electron: 'file:vendor/electron-compat'
190
222
  };
191
- if (target !== 'macos' && process.env.ATOM_SKIP_WEBVIEW_CHECK !== '1') {
223
+ if (target === 'windows') {
224
+ pkg.dependencies['@webviewjs/webview'] = '0.4.0';
225
+ delete pkg.dependencies['webview-nodejs'];
226
+ if (pkg.optionalDependencies) delete pkg.optionalDependencies['webview-nodejs'];
227
+ } else if (target === 'linux' && process.env.ATOM_SKIP_WEBVIEW_CHECK !== '1') {
192
228
  pkg.dependencies['webview-nodejs'] = '0.5.0';
229
+ delete pkg.dependencies['@webviewjs/webview'];
230
+ if (pkg.optionalDependencies) delete pkg.optionalDependencies['webview-nodejs'];
231
+ } else {
232
+ delete pkg.dependencies['webview-nodejs'];
233
+ delete pkg.dependencies['@webviewjs/webview'];
193
234
  if (pkg.optionalDependencies) delete pkg.optionalDependencies['webview-nodejs'];
194
- } else if (pkg.optionalDependencies) {
195
- delete pkg.optionalDependencies['webview-nodejs'];
196
235
  }
197
236
  pkg.overrides = { ...(pkg.overrides || {}), tar: '7.5.20' };
198
237
  delete pkg.devDependencies;
@@ -228,7 +267,12 @@ async function installProductionDependencies(appDir, skipInstall, target) {
228
267
  throw new Error('The AtomJS Electron compatibility facade was not installed in the staged application.');
229
268
  }
230
269
 
231
- if (target !== 'macos') {
270
+ if (target === 'windows') {
271
+ const bindingPath = path.join(appDir, 'node_modules', '@webviewjs', 'webview');
272
+ if (!fs.existsSync(bindingPath)) {
273
+ throw new Error('@webviewjs/webview was not installed. Remove node_modules and package-lock.json, then retry the build.');
274
+ }
275
+ } else if (target === 'linux') {
232
276
  const bindingPath = path.join(appDir, 'node_modules', 'webview-nodejs');
233
277
  if (!fs.existsSync(bindingPath) && process.env.ATOM_SKIP_WEBVIEW_CHECK !== '1') {
234
278
  throw new Error('webview-nodejs was not installed. Run `atom doctor`, install the platform prerequisites, and retry the build.');
@@ -238,12 +282,15 @@ async function installProductionDependencies(appDir, skipInstall, target) {
238
282
 
239
283
  async function createApplicationPayload(appDir, outputPath) {
240
284
  const files = [];
285
+ let sourceBytes = 0;
241
286
 
242
287
  async function addFile(source, relative, stat) {
288
+ const data = await fs.promises.readFile(source);
289
+ sourceBytes += data.length;
243
290
  files.push({
244
291
  path: relative.split(path.sep).join('/'),
245
292
  mode: stat.mode & 0o777,
246
- data: (await fs.promises.readFile(source)).toString('base64')
293
+ data: data.toString('base64')
247
294
  });
248
295
  }
249
296
 
@@ -287,6 +334,12 @@ async function createApplicationPayload(appDir, outputPath) {
287
334
  const payload = Buffer.from(JSON.stringify({ format: 1, files }));
288
335
  const compressed = require('node:zlib').gzipSync(payload, { level: 9 });
289
336
  await fs.promises.writeFile(outputPath, compressed);
337
+ return {
338
+ fileCount: files.length,
339
+ sourceBytes,
340
+ compressedBytes: compressed.length,
341
+ paths: files.map((entry) => entry.path)
342
+ };
290
343
  }
291
344
 
292
345
  async function createSeaLauncher({ executablePath, appDir, target, productName, appId, project = null, payloadPath = null }) {
@@ -455,6 +508,7 @@ process.chdir(appDir);
455
508
  process.env.ATOM_PROJECT_ROOT = appDir;
456
509
  process.env.ATOM_APP_NAME = productName;
457
510
  process.env.ATOM_APP_ID = appId;
511
+ process.title = productName;
458
512
  process.env.ATOM_BUILD = '1';
459
513
  process.env.ATOM_EMBEDDED_RUNTIME = '1';
460
514
  process.env.ATOM_WINDOW_HOST_ENTRY = path.join(appDir, 'vendor', 'atomjs', 'src', 'runtime', 'window-host.mjs');
@@ -941,6 +995,8 @@ async function packageMacOS({ project, buildRoot, unpacked, executableName, prod
941
995
  <key>CFBundleName</key><string>${xml(bundleName)}</string>
942
996
  <key>CFBundleDisplayName</key><string>${xml(productName)}</string>
943
997
  <key>CFBundlePackageType</key><string>APPL</string>
998
+ <key>LSUIElement</key><true/>
999
+ <key>LSMultipleInstancesProhibited</key><true/>
944
1000
  <key>CFBundleShortVersionString</key><string>${xml(version)}</string>
945
1001
  <key>CFBundleVersion</key><string>${xml(bundleVersion)}</string>
946
1002
  <key>LSMinimumSystemVersion</key><string>${xml(config.minimumSystemVersion)}</string>
package/src/doctor.cjs CHANGED
@@ -7,7 +7,7 @@ const { loadProject, commandExists } = require('./utils.cjs');
7
7
 
8
8
  async function doctorCommand(options = {}) {
9
9
  const rows = [];
10
- rows.push(check('Node.js >= 20.12', isNodeSupported(), process.version));
10
+ rows.push(check('Node.js >= 24', isNodeSupported(), process.version));
11
11
  rows.push(check('npm', commandExists(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['--version'])));
12
12
 
13
13
  if (process.platform === 'win32') {
@@ -31,6 +31,14 @@ async function doctorCommand(options = {}) {
31
31
 
32
32
  if (process.platform === 'darwin') {
33
33
  rows.push(check('macOS WKWebView backend', true, 'shared native Cocoa host; no osascript process'));
34
+ } else if (process.platform === 'win32') {
35
+ try {
36
+ const project = loadProject(options.project);
37
+ const binding = require.resolve('@webviewjs/webview/package.json', { paths: [project.root, process.cwd()] });
38
+ rows.push(check('Windows prebuilt WebView binding', true, path.dirname(binding)));
39
+ } catch {
40
+ rows.push(check('Windows prebuilt WebView binding', false, 'run npm install; CMake and Visual Studio are not required'));
41
+ }
34
42
  } else {
35
43
  try {
36
44
  const project = loadProject(options.project);
@@ -57,8 +65,8 @@ async function doctorCommand(options = {}) {
57
65
  }
58
66
 
59
67
  function isNodeSupported() {
60
- const [major, minor] = process.versions.node.split('.').map(Number);
61
- return major > 20 || (major === 20 && minor >= 12);
68
+ const [major] = process.versions.node.split('.').map(Number);
69
+ return major >= 24;
62
70
  }
63
71
 
64
72
  function check(name, ok, detail = '') {
package/src/init.cjs CHANGED
@@ -3,6 +3,7 @@
3
3
  const fs = require('node:fs');
4
4
  const path = require('node:path');
5
5
  const fse = require('fs-extra');
6
+ const cliPackageVersion = require('../package.json').version;
6
7
 
7
8
  async function initCommand(directory, options = {}) {
8
9
  const root = path.resolve(directory);
@@ -16,7 +17,6 @@ async function initCommand(directory, options = {}) {
16
17
  const productName = humanize(packageName);
17
18
 
18
19
  await fse.ensureDir(path.join(root, 'src'));
19
- await fse.ensureDir(path.join(root, '.github', 'workflows'));
20
20
 
21
21
  await fs.promises.writeFile(path.join(root, 'package.json'), JSON.stringify({
22
22
  name: packageName,
@@ -25,19 +25,19 @@ async function initCommand(directory, options = {}) {
25
25
  main: 'src/main.js',
26
26
  scripts: {
27
27
  dev: 'atom run dev',
28
- build: 'atom build all',
29
- 'build:current': `atom build ${hostTarget()}`,
28
+ build: 'atom build current --local',
29
+ 'build:current': 'atom build current --local',
30
30
  start: 'atom run build'
31
31
  },
32
32
  dependencies: {
33
- '@atom-js-org/runtime': '0.5.2-alpha.0',
34
- electron: 'npm:@atom-js-org/electron@0.5.2-alpha.0'
35
- },
36
- optionalDependencies: {
37
- 'webview-nodejs': '0.5.0'
33
+ '@atom-js-org/runtime': cliPackageVersion,
34
+ electron: `npm:@atom-js-org/electron@${cliPackageVersion}`
38
35
  },
39
36
  devDependencies: {
40
- '@atom-js-org/cli': '0.5.2-alpha.0'
37
+ '@atom-js-org/cli': cliPackageVersion
38
+ },
39
+ engines: {
40
+ node: '>=24'
41
41
  },
42
42
  overrides: {
43
43
  tar: '7.5.20'
@@ -80,9 +80,6 @@ async function initCommand(directory, options = {}) {
80
80
  deb: true,
81
81
  rpm: true
82
82
  }
83
- },
84
- github: {
85
- workflow: 'atom-build.yml'
86
83
  }
87
84
  }, null, 2));
88
85
 
@@ -94,7 +91,6 @@ async function initCommand(directory, options = {}) {
94
91
  await fs.promises.writeFile(path.join(root, 'src', 'index.html'), htmlTemplate(productName));
95
92
  await fs.promises.writeFile(path.join(root, 'src', 'renderer.js'), rendererTemplate());
96
93
  await fs.promises.writeFile(path.join(root, '.gitignore'), 'node_modules/\nbuild/\n.atom/\n');
97
- await fs.promises.writeFile(path.join(root, '.github', 'workflows', 'atom-build.yml'), workflowTemplate());
98
94
 
99
95
  console.log(`Created AtomJS project in ${root}`);
100
96
  console.log('Next: npm install && npm run dev');
@@ -209,7 +205,7 @@ jobs:
209
205
  - uses: actions/checkout@v4
210
206
  - uses: actions/setup-node@v4
211
207
  with:
212
- node-version: 22
208
+ node-version: 24
213
209
  cache: npm
214
210
  - run: choco install nsis -y
215
211
  - run: npm ci
@@ -229,7 +225,7 @@ jobs:
229
225
  - uses: actions/checkout@v4
230
226
  - uses: actions/setup-node@v4
231
227
  with:
232
- node-version: 22
228
+ node-version: 24
233
229
  cache: npm
234
230
  - run: npm ci
235
231
  working-directory: \${{ inputs.project }}
@@ -249,7 +245,7 @@ jobs:
249
245
  - run: sudo apt-get update && sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev zenity rpm
250
246
  - uses: actions/setup-node@v4
251
247
  with:
252
- node-version: 22
248
+ node-version: 24
253
249
  cache: npm
254
250
  - run: |
255
251
  sudo curl -L -o /usr/local/bin/appimagetool https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage
package/src/run.cjs CHANGED
@@ -3,6 +3,7 @@
3
3
  const path = require('node:path');
4
4
  const fs = require('node:fs');
5
5
  const { spawn } = require('node:child_process');
6
+ const { pathToFileURL } = require('node:url');
6
7
  const { loadProject, hostTarget } = require('./utils.cjs');
7
8
  const { ensureElectronCompatibility } = require('./electron-compat.cjs');
8
9
 
@@ -26,26 +27,16 @@ async function runDev(options) {
26
27
  const iconPath = project.config.icon
27
28
  ? path.resolve(project.root, project.config.icon)
28
29
  : null;
29
- const child = spawn(process.execPath, [mainPath], {
30
- cwd: project.root,
31
- env: {
32
- ...process.env,
33
- ATOM_PROJECT_ROOT: project.root,
34
- ATOM_APP_NAME: project.config.productName,
35
- ATOM_APP_ID: project.config.appId,
36
- ...(iconPath && fs.existsSync(iconPath) ? { ATOM_APP_ICON: iconPath } : {}),
37
- ATOM_DEV: '1'
38
- },
39
- stdio: 'inherit'
40
- });
41
-
42
- await new Promise((resolve, reject) => {
43
- child.once('error', reject);
44
- child.once('exit', (code, signal) => {
45
- if (code === 0 || signal === 'SIGTERM' || signal === 'SIGINT') resolve();
46
- else reject(new Error(`Application exited with code ${code}`));
47
- });
30
+ Object.assign(process.env, {
31
+ ATOM_PROJECT_ROOT: project.root,
32
+ ATOM_APP_NAME: project.config.productName,
33
+ ATOM_APP_ID: project.config.appId,
34
+ ...(iconPath && fs.existsSync(iconPath) ? { ATOM_APP_ICON: iconPath } : {}),
35
+ ATOM_DEV: '1'
48
36
  });
37
+ process.title = project.config.productName;
38
+ process.chdir(project.root);
39
+ await import(pathToFileURL(mainPath).href);
49
40
  }
50
41
 
51
42
  async function runBuild(options) {
package/src/utils.cjs CHANGED
@@ -4,7 +4,7 @@ const fs = require('node:fs');
4
4
  const path = require('node:path');
5
5
  const { spawn, spawnSync } = require('node:child_process');
6
6
 
7
- const TARGETS = new Set(['windows', 'macos', 'linux', 'all']);
7
+ const TARGETS = new Set(['current', 'windows', 'macos', 'linux', 'all']);
8
8
 
9
9
  function hostTarget() {
10
10
  if (process.platform === 'win32') return 'windows';
@@ -169,7 +169,7 @@ function commandExists(command, args = ['--version']) {
169
169
  function validateTarget(target) {
170
170
  const normalized = String(target).toLowerCase();
171
171
  if (!TARGETS.has(normalized)) {
172
- throw new Error(`Unknown build target '${target}'. Use windows, macos, linux, or all.`);
172
+ throw new Error(`Unknown build target '${target}'. Use current, windows, macos, linux, or all.`);
173
173
  }
174
174
  return normalized;
175
175
  }