@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 +2 -2
- package/bin/atom.cjs +3 -3
- package/package.json +2 -2
- package/src/build.cjs +70 -14
- package/src/doctor.cjs +11 -3
- package/src/init.cjs +12 -16
- package/src/run.cjs +10 -19
- package/src/utils.cjs +2 -2
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
|
|
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', '
|
|
31
|
-
.option('--remote', '
|
|
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.
|
|
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": ">=
|
|
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
|
|
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.
|
|
28
|
-
throw new Error(
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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:
|
|
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
|
|
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
|
|
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:
|
|
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 >=
|
|
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
|
|
61
|
-
return major
|
|
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
|
|
29
|
-
'build:current':
|
|
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':
|
|
34
|
-
electron:
|
|
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':
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
}
|