@atom-js-org/cli 0.5.3-alpha.1 → 0.5.3-alpha.3

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.1",
3
+ "version": "0.5.3-alpha.3",
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
@@ -21,17 +21,33 @@ const { resolveElectronCompatibilityRoot } = require('./electron-compat.cjs');
21
21
  const cliPackageVersion = require('../package.json').version;
22
22
 
23
23
  async function buildCommand(targetInput, options = {}) {
24
- const target = validateTarget(targetInput);
24
+ const requestedTarget = validateTarget(targetInput);
25
25
  const project = loadProject(options.project);
26
26
  const host = hostTarget();
27
+ const target = requestedTarget === 'current' ? host : requestedTarget;
27
28
 
28
- if (options.local && (target === 'all' || target !== host)) {
29
- 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'));
37
+ }
38
+
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.`);
30
48
  }
31
49
 
32
- const needsRemote = options.remote || target === 'all' || target !== host;
33
- if (needsRemote) return remoteBuild(project, target);
34
- return localBuild(project, host, options);
50
+ return localBuild(project, target, options);
35
51
  }
36
52
 
37
53
  async function localBuild(project, target, options = {}) {
@@ -61,8 +77,8 @@ async function localBuild(project, target, options = {}) {
61
77
  await vendorFramework(stagedApp, project, target);
62
78
  await installProductionDependencies(stagedApp, options.skipInstall, target);
63
79
 
64
- console.log('Embedding application code and production dependencies into the executable...');
65
- await createApplicationPayload(stagedApp, payloadPath);
80
+ console.log('Embedding application code, assets and production dependencies into the executable...');
81
+ const payloadSummary = await createApplicationPayload(stagedApp, payloadPath);
66
82
 
67
83
  const executableName = target === 'windows' ? `${productName}.exe` : productName;
68
84
  const executablePath = path.join(unpacked, executableName);
@@ -108,6 +124,15 @@ async function localBuild(project, target, options = {}) {
108
124
  outputs = await packageLinux({ project, buildRoot, unpacked, executableName, productName });
109
125
  }
110
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
+
111
136
  const manifest = {
112
137
  atomjsVersion: cliPackageVersion,
113
138
  target,
@@ -116,6 +141,12 @@ async function localBuild(project, target, options = {}) {
116
141
  createdAt: new Date().toISOString(),
117
142
  run: path.relative(buildRoot, runPath),
118
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
+ },
119
150
  outputs: outputs.map((file) => path.relative(buildRoot, file))
120
151
  };
121
152
  await fs.promises.writeFile(path.join(buildRoot, 'manifest.json'), JSON.stringify(manifest, null, 2));
@@ -191,15 +222,18 @@ async function vendorFramework(appDir, project, target) {
191
222
  };
192
223
  if (target === 'windows') {
193
224
  pkg.dependencies['@webviewjs/webview'] = '0.4.0';
225
+ pkg.dependencies.koffi = '3.1.2';
194
226
  delete pkg.dependencies['webview-nodejs'];
195
227
  if (pkg.optionalDependencies) delete pkg.optionalDependencies['webview-nodejs'];
196
228
  } else if (target === 'linux' && process.env.ATOM_SKIP_WEBVIEW_CHECK !== '1') {
197
229
  pkg.dependencies['webview-nodejs'] = '0.5.0';
198
230
  delete pkg.dependencies['@webviewjs/webview'];
231
+ delete pkg.dependencies.koffi;
199
232
  if (pkg.optionalDependencies) delete pkg.optionalDependencies['webview-nodejs'];
200
233
  } else {
201
234
  delete pkg.dependencies['webview-nodejs'];
202
235
  delete pkg.dependencies['@webviewjs/webview'];
236
+ delete pkg.dependencies.koffi;
203
237
  if (pkg.optionalDependencies) delete pkg.optionalDependencies['webview-nodejs'];
204
238
  }
205
239
  pkg.overrides = { ...(pkg.overrides || {}), tar: '7.5.20' };
@@ -241,6 +275,15 @@ async function installProductionDependencies(appDir, skipInstall, target) {
241
275
  if (!fs.existsSync(bindingPath)) {
242
276
  throw new Error('@webviewjs/webview was not installed. Remove node_modules and package-lock.json, then retry the build.');
243
277
  }
278
+ const nativeDragPath = path.join(appDir, 'node_modules', 'koffi');
279
+ if (!fs.existsSync(nativeDragPath)) {
280
+ throw new Error('koffi was not installed. AtomJS requires its prebuilt Win32 FFI package for native window movement.');
281
+ }
282
+ try {
283
+ await run(process.execPath, ['-e', "require('koffi');"], { cwd: appDir });
284
+ } catch (error) {
285
+ throw new Error(`The prebuilt Koffi package could not load for this Windows architecture: ${error.message}`);
286
+ }
244
287
  } else if (target === 'linux') {
245
288
  const bindingPath = path.join(appDir, 'node_modules', 'webview-nodejs');
246
289
  if (!fs.existsSync(bindingPath) && process.env.ATOM_SKIP_WEBVIEW_CHECK !== '1') {
@@ -251,12 +294,15 @@ async function installProductionDependencies(appDir, skipInstall, target) {
251
294
 
252
295
  async function createApplicationPayload(appDir, outputPath) {
253
296
  const files = [];
297
+ let sourceBytes = 0;
254
298
 
255
299
  async function addFile(source, relative, stat) {
300
+ const data = await fs.promises.readFile(source);
301
+ sourceBytes += data.length;
256
302
  files.push({
257
303
  path: relative.split(path.sep).join('/'),
258
304
  mode: stat.mode & 0o777,
259
- data: (await fs.promises.readFile(source)).toString('base64')
305
+ data: data.toString('base64')
260
306
  });
261
307
  }
262
308
 
@@ -300,6 +346,12 @@ async function createApplicationPayload(appDir, outputPath) {
300
346
  const payload = Buffer.from(JSON.stringify({ format: 1, files }));
301
347
  const compressed = require('node:zlib').gzipSync(payload, { level: 9 });
302
348
  await fs.promises.writeFile(outputPath, compressed);
349
+ return {
350
+ fileCount: files.length,
351
+ sourceBytes,
352
+ compressedBytes: compressed.length,
353
+ paths: files.map((entry) => entry.path)
354
+ };
303
355
  }
304
356
 
305
357
  async function createSeaLauncher({ executablePath, appDir, target, productName, appId, project = null, payloadPath = null }) {
@@ -955,6 +1007,8 @@ async function packageMacOS({ project, buildRoot, unpacked, executableName, prod
955
1007
  <key>CFBundleName</key><string>${xml(bundleName)}</string>
956
1008
  <key>CFBundleDisplayName</key><string>${xml(productName)}</string>
957
1009
  <key>CFBundlePackageType</key><string>APPL</string>
1010
+ <key>LSUIElement</key><true/>
1011
+ <key>LSMultipleInstancesProhibited</key><true/>
958
1012
  <key>CFBundleShortVersionString</key><string>${xml(version)}</string>
959
1013
  <key>CFBundleVersion</key><string>${xml(bundleVersion)}</string>
960
1014
  <key>LSMinimumSystemVersion</key><string>${xml(config.minimumSystemVersion)}</string>
package/src/init.cjs CHANGED
@@ -17,7 +17,6 @@ async function initCommand(directory, options = {}) {
17
17
  const productName = humanize(packageName);
18
18
 
19
19
  await fse.ensureDir(path.join(root, 'src'));
20
- await fse.ensureDir(path.join(root, '.github', 'workflows'));
21
20
 
22
21
  await fs.promises.writeFile(path.join(root, 'package.json'), JSON.stringify({
23
22
  name: packageName,
@@ -26,8 +25,8 @@ async function initCommand(directory, options = {}) {
26
25
  main: 'src/main.js',
27
26
  scripts: {
28
27
  dev: 'atom run dev',
29
- build: 'atom build all',
30
- 'build:current': `atom build ${hostTarget()}`,
28
+ build: 'atom build current --local',
29
+ 'build:current': 'atom build current --local',
31
30
  start: 'atom run build'
32
31
  },
33
32
  dependencies: {
@@ -81,9 +80,6 @@ async function initCommand(directory, options = {}) {
81
80
  deb: true,
82
81
  rpm: true
83
82
  }
84
- },
85
- github: {
86
- workflow: 'atom-build.yml'
87
83
  }
88
84
  }, null, 2));
89
85
 
@@ -95,7 +91,6 @@ async function initCommand(directory, options = {}) {
95
91
  await fs.promises.writeFile(path.join(root, 'src', 'index.html'), htmlTemplate(productName));
96
92
  await fs.promises.writeFile(path.join(root, 'src', 'renderer.js'), rendererTemplate());
97
93
  await fs.promises.writeFile(path.join(root, '.gitignore'), 'node_modules/\nbuild/\n.atom/\n');
98
- await fs.promises.writeFile(path.join(root, '.github', 'workflows', 'atom-build.yml'), workflowTemplate());
99
94
 
100
95
  console.log(`Created AtomJS project in ${root}`);
101
96
  console.log('Next: npm install && npm run dev');
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
  }