@gjsify/cli 0.3.21 → 0.4.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.
Files changed (67) hide show
  1. package/dist/cli.gjs.mjs +798 -0
  2. package/lib/actions/build.js +4 -17
  3. package/lib/bundler-pick.d.ts +79 -0
  4. package/lib/bundler-pick.js +428 -0
  5. package/lib/commands/foreach.d.ts +16 -0
  6. package/lib/commands/foreach.js +268 -0
  7. package/lib/commands/index.d.ts +2 -0
  8. package/lib/commands/index.js +2 -0
  9. package/lib/commands/install.d.ts +1 -0
  10. package/lib/commands/install.js +222 -26
  11. package/lib/commands/run.d.ts +1 -1
  12. package/lib/commands/run.js +133 -20
  13. package/lib/commands/workspace.d.ts +8 -0
  14. package/lib/commands/workspace.js +69 -0
  15. package/lib/config.js +12 -1
  16. package/lib/index.js +11 -3
  17. package/lib/types/config-data.d.ts +10 -1
  18. package/lib/utils/install-backend-native.d.ts +5 -1
  19. package/lib/utils/install-backend-native.js +88 -11
  20. package/lib/utils/install-backend.d.ts +11 -1
  21. package/lib/utils/install-backend.js +4 -2
  22. package/lib/utils/pkg-json-edit.d.ts +47 -0
  23. package/lib/utils/pkg-json-edit.js +108 -0
  24. package/package.json +36 -12
  25. package/src/actions/build.ts +0 -431
  26. package/src/actions/index.ts +0 -1
  27. package/src/commands/build.ts +0 -146
  28. package/src/commands/check.ts +0 -87
  29. package/src/commands/create.ts +0 -63
  30. package/src/commands/dlx.ts +0 -195
  31. package/src/commands/flatpak/build.ts +0 -225
  32. package/src/commands/flatpak/ci.ts +0 -173
  33. package/src/commands/flatpak/deps.ts +0 -120
  34. package/src/commands/flatpak/index.ts +0 -53
  35. package/src/commands/flatpak/init.ts +0 -191
  36. package/src/commands/flatpak/utils.ts +0 -76
  37. package/src/commands/gettext.ts +0 -258
  38. package/src/commands/gresource.ts +0 -97
  39. package/src/commands/gsettings.ts +0 -87
  40. package/src/commands/index.ts +0 -12
  41. package/src/commands/info.ts +0 -70
  42. package/src/commands/install.ts +0 -195
  43. package/src/commands/run.ts +0 -33
  44. package/src/commands/showcase.ts +0 -149
  45. package/src/config.ts +0 -304
  46. package/src/constants.ts +0 -1
  47. package/src/index.ts +0 -37
  48. package/src/types/cli-build-options.ts +0 -100
  49. package/src/types/command.ts +0 -10
  50. package/src/types/config-data-library.ts +0 -5
  51. package/src/types/config-data-typescript.ts +0 -6
  52. package/src/types/config-data.ts +0 -225
  53. package/src/types/cosmiconfig-result.ts +0 -5
  54. package/src/types/index.ts +0 -6
  55. package/src/utils/check-system-deps.ts +0 -480
  56. package/src/utils/detect-native-packages.ts +0 -153
  57. package/src/utils/discover-showcases.ts +0 -75
  58. package/src/utils/dlx-cache.ts +0 -135
  59. package/src/utils/install-backend-native.ts +0 -363
  60. package/src/utils/install-backend.ts +0 -88
  61. package/src/utils/install-global.ts +0 -182
  62. package/src/utils/normalize-bundler-options.ts +0 -129
  63. package/src/utils/parse-spec.ts +0 -48
  64. package/src/utils/resolve-gjs-entry.ts +0 -96
  65. package/src/utils/resolve-plugin-by-name.ts +0 -106
  66. package/src/utils/run-gjs.ts +0 -90
  67. package/tsconfig.json +0 -16
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/cli",
3
- "version": "0.3.21",
3
+ "version": "0.4.0",
4
4
  "description": "CLI for Gjsify",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -9,12 +9,26 @@
9
9
  "bin": {
10
10
  "gjsify": "./lib/index.js"
11
11
  },
12
+ "gjsify": {
13
+ "bin": {
14
+ "gjsify": "./dist/cli.gjs.mjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "lib",
19
+ "dist/cli.gjs.mjs",
20
+ "showcases.json"
21
+ ],
12
22
  "scripts": {
13
- "clear": "rm -rf lib tsconfig.tsbuildinfo || exit 0",
23
+ "clear": "rm -rf lib dist tsconfig.tsbuildinfo || exit 0",
14
24
  "check": "tsc --noEmit",
15
25
  "start": "node lib/index.js",
16
26
  "build": "tsc && yarn chmod",
17
- "chmod": "chmod +x ./lib/index.js"
27
+ "build:gjs-bundle": "node lib/index.js build src/index.ts --app gjs --outfile dist/cli.gjs.mjs",
28
+ "chmod": "chmod +x ./lib/index.js",
29
+ "build:test:node": "node lib/index.js build src/test.mts --app node --outfile dist/test.node.mjs",
30
+ "test:node": "node dist/test.node.mjs",
31
+ "test": "yarn build:test:node && yarn test:node"
18
32
  },
19
33
  "keywords": [
20
34
  "gjs",
@@ -23,15 +37,16 @@
23
37
  "cli"
24
38
  ],
25
39
  "dependencies": {
26
- "@gjsify/create-app": "^0.3.21",
27
- "@gjsify/node-polyfills": "^0.3.21",
28
- "@gjsify/npm-registry": "^0.3.21",
29
- "@gjsify/resolve-npm": "^0.3.21",
30
- "@gjsify/rolldown-plugin-gjsify": "^0.3.21",
31
- "@gjsify/rolldown-plugin-pnp": "^0.3.21",
32
- "@gjsify/semver": "^0.3.21",
33
- "@gjsify/tar": "^0.3.21",
34
- "@gjsify/web-polyfills": "^0.3.21",
40
+ "@gjsify/create-app": "^0.4.0",
41
+ "@gjsify/node-polyfills": "^0.4.0",
42
+ "@gjsify/npm-registry": "^0.4.0",
43
+ "@gjsify/resolve-npm": "^0.4.0",
44
+ "@gjsify/rolldown-plugin-gjsify": "^0.4.0",
45
+ "@gjsify/rolldown-plugin-pnp": "^0.4.0",
46
+ "@gjsify/semver": "^0.4.0",
47
+ "@gjsify/tar": "^0.4.0",
48
+ "@gjsify/web-polyfills": "^0.4.0",
49
+ "@gjsify/workspace": "^0.4.0",
35
50
  "cosmiconfig": "^9.0.1",
36
51
  "get-tsconfig": "^4.14.0",
37
52
  "pkg-types": "^2.3.1",
@@ -39,7 +54,16 @@
39
54
  "yargs": "^18.0.0"
40
55
  },
41
56
  "devDependencies": {
57
+ "@gjsify/unit": "^0.4.0",
42
58
  "@types/yargs": "^17.0.35",
43
59
  "typescript": "^6.0.3"
60
+ },
61
+ "peerDependencies": {
62
+ "@gjsify/rolldown-native": "^0.4.0"
63
+ },
64
+ "peerDependenciesMeta": {
65
+ "@gjsify/rolldown-native": {
66
+ "optional": true
67
+ }
44
68
  }
45
69
  }
@@ -1,431 +0,0 @@
1
- import type { ConfigData, BundlerOptions } from "../types/index.js";
2
- import type { App, PluginOptions } from "@gjsify/rolldown-plugin-gjsify";
3
- import type { RolldownOutput, RolldownPluginOption } from "rolldown";
4
- import { rolldown } from "rolldown";
5
- import { gjsifyPlugin, textLoaderPlugin, resolveShebangLine } from "@gjsify/rolldown-plugin-gjsify";
6
- import { resolveUserPlugins } from "../utils/resolve-plugin-by-name.js";
7
- import {
8
- resolveGlobalsList,
9
- writeRegisterInjectFile,
10
- detectAutoGlobals,
11
- } from "@gjsify/rolldown-plugin-gjsify/globals";
12
- import { pnpPlugin } from "@gjsify/rolldown-plugin-pnp";
13
- import { dirname, extname } from "node:path";
14
- import { chmod, readFile, writeFile } from "node:fs/promises";
15
- import { normalizeBundlerOptions, mergeBundlerOptions } from "../utils/normalize-bundler-options.js";
16
-
17
- const DEFAULT_GJS_SHEBANG = "#!/usr/bin/env -S gjs -m";
18
-
19
- /**
20
- * `true` when `path` points at a location that's unsafe to use as a build
21
- * outfile (would overwrite source). Currently catches:
22
- * - any TypeScript extension (`.ts`, `.tsx`, `.mts`, `.cts`, `.mtsx`, `.ctsx`)
23
- * - paths that live under a `src/` segment (relative or absolute)
24
- */
25
- function isUnsafeDefaultOutput(path: string): boolean {
26
- if (/\.[cm]?tsx?$/i.test(path)) return true;
27
- const norm = path.replace(/\\/g, "/");
28
- if (/(?:^|\/)src\//.test(norm)) return true;
29
- return false;
30
- }
31
-
32
- /**
33
- * Resolve the gjsify-flavoured PnP plugin. Anchors the relay on this file's
34
- * URL so transitive `@gjsify/*` polyfills (reached via @gjsify/cli's deps on
35
- * @gjsify/{node,web}-polyfills) are resolvable for external consumers without
36
- * each one having to be a direct devDep.
37
- *
38
- * The path rewriter (`__filename`/`__dirname` + `import.meta.url` injection
39
- * for node_modules code) is registered separately by the orchestrator —
40
- * Rolldown's transform hooks all run sequentially, no shared `onLoad` race.
41
- */
42
- async function buildPnpPlugin(): Promise<RolldownPluginOption | null> {
43
- return pnpPlugin({ issuerUrl: import.meta.url });
44
- }
45
-
46
- export class BuildAction {
47
- constructor(readonly configData: ConfigData = {}) {}
48
-
49
- /** Library mode */
50
- async buildLibrary(): Promise<RolldownOutput[]> {
51
- const { verbose, library, typescript, exclude, aliases } = this.configData;
52
- const lib = library ?? {};
53
- const userBundler = normalizeBundlerOptions(this.configData);
54
-
55
- const moduleOutdir = lib.module ? dirname(lib.module) : undefined;
56
- const mainOutdir = lib.main ? dirname(lib.main) : undefined;
57
-
58
- const moduleOutExt = lib.module ? extname(lib.module) : ".js";
59
- const mainOutExt = lib.main ? extname(lib.main) : ".js";
60
-
61
- const multipleBuilds =
62
- moduleOutdir && mainOutdir && moduleOutdir !== mainOutdir;
63
-
64
- const pnp = await buildPnpPlugin();
65
- const pnpPlugins: RolldownPluginOption[] = pnp ? [pnp] : [];
66
-
67
- const results: RolldownOutput[] = [];
68
-
69
- if (multipleBuilds) {
70
- const moduleFormat: "esm" | "cjs" =
71
- moduleOutdir.includes("/cjs") || moduleOutExt === ".cjs"
72
- ? "cjs"
73
- : "esm";
74
- results.push(
75
- await runOneLibraryBuild({
76
- pluginOpts: {
77
- debug: verbose,
78
- library: moduleFormat,
79
- exclude,
80
- reflection: typescript?.reflection,
81
- jsExtension: moduleOutExt,
82
- },
83
- userBundler,
84
- output: { dir: moduleOutdir },
85
- userAliases: aliases,
86
- pnpPlugins,
87
- }),
88
- );
89
-
90
- const mainFormat: "esm" | "cjs" =
91
- mainOutdir.includes("/cjs") || mainOutExt === ".cjs" ? "cjs" : "esm";
92
- results.push(
93
- await runOneLibraryBuild({
94
- pluginOpts: {
95
- debug: verbose,
96
- library: mainFormat,
97
- exclude,
98
- reflection: typescript?.reflection,
99
- jsExtension: mainOutExt,
100
- },
101
- userBundler,
102
- output: { dir: mainOutdir },
103
- userAliases: aliases,
104
- pnpPlugins,
105
- }),
106
- );
107
- } else {
108
- const outfilePath =
109
- userBundler.output?.file ?? lib.module ?? lib.main;
110
- const outExt = outfilePath ? extname(outfilePath) : ".js";
111
- const outdir =
112
- userBundler.output?.dir ?? (outfilePath ? dirname(outfilePath) : undefined);
113
- const format: "esm" | "cjs" =
114
- (userBundler.output?.format as "esm" | "cjs" | undefined) ??
115
- (outdir?.includes("/cjs") || outExt === ".cjs" ? "cjs" : "esm");
116
- results.push(
117
- await runOneLibraryBuild({
118
- pluginOpts: {
119
- debug: verbose,
120
- library: format,
121
- exclude,
122
- reflection: typescript?.reflection,
123
- jsExtension: outExt,
124
- },
125
- userBundler,
126
- output: { dir: outdir },
127
- userAliases: aliases,
128
- pnpPlugins,
129
- }),
130
- );
131
- }
132
- return results;
133
- }
134
-
135
- /**
136
- * Parse the `--globals` value into { autoMode, extras }.
137
- * - `auto` → { autoMode: true, extras: '' }
138
- * - `auto,dom` → { autoMode: true, extras: 'dom' }
139
- * - `auto,dom,fetch` → { autoMode: true, extras: 'dom,fetch' }
140
- * - `dom,fetch` → { autoMode: false, extras: 'dom,fetch' }
141
- * - `none` / `` → { autoMode: false, extras: '' }
142
- * - `undefined` → { autoMode: true, extras: '' } (default)
143
- */
144
- private parseGlobalsValue(value: string | undefined): {
145
- autoMode: boolean;
146
- extras: string;
147
- } {
148
- if (value === undefined) return { autoMode: true, extras: "" };
149
- if (value === "none" || value === "")
150
- return { autoMode: false, extras: "" };
151
-
152
- const tokens = value
153
- .split(",")
154
- .map((t) => t.trim())
155
- .filter(Boolean);
156
- const hasAuto = tokens.includes("auto");
157
- const extras = tokens.filter((t) => t !== "auto").join(",");
158
-
159
- return { autoMode: hasAuto, extras };
160
- }
161
-
162
- /**
163
- * Resolve the `--globals` CLI list into a pre-computed inject stub path
164
- * that the orchestrator appends to its input list. Only runs for
165
- * `--app gjs` — Node and browser builds rely on native globals.
166
- *
167
- * Used only for the explicit-only path (no `auto` token in the value).
168
- * The auto path is handled in `buildApp` via the iterative multi-pass build.
169
- */
170
- private async resolveGlobalsInject(
171
- app: App,
172
- globals: string,
173
- verbose: boolean | undefined,
174
- ): Promise<string | undefined> {
175
- if (app !== "gjs") return undefined;
176
- if (!globals) return undefined;
177
-
178
- const registerPaths = resolveGlobalsList(globals);
179
- if (registerPaths.size === 0) return undefined;
180
-
181
- const injectPath = await writeRegisterInjectFile(
182
- registerPaths,
183
- process.cwd(),
184
- );
185
- if (verbose && injectPath) {
186
- console.debug(
187
- `[gjsify] globals: injected ${registerPaths.size} register module(s) from --globals ${globals}`,
188
- );
189
- }
190
- return injectPath ?? undefined;
191
- }
192
-
193
- /**
194
- * Post-processing: prepend the resolved shebang line and mark the
195
- * output executable. Only runs for GJS app builds with a single outfile.
196
- * The shebang plugin in `@gjsify/rolldown-plugin-gjsify` already injects
197
- * during bundling — this hook is the safety net for anything that
198
- * bypassed the plugin (e.g. user-supplied banners that out-ordered it),
199
- * plus the chmod.
200
- */
201
- private async applyShebang(
202
- outfile: string | undefined,
203
- verbose: boolean | undefined,
204
- ): Promise<void> {
205
- if (!outfile) {
206
- if (verbose)
207
- console.warn(
208
- "[gjsify] --shebang skipped: no single outfile (use --outfile for GJS executables)",
209
- );
210
- return;
211
- }
212
-
213
- const line = resolveShebangLine(this.configData.shebang) ?? DEFAULT_GJS_SHEBANG;
214
-
215
- const content = await readFile(outfile, "utf-8");
216
- if (content.startsWith("#!")) {
217
- if (verbose)
218
- console.debug(
219
- `[gjsify] --shebang skipped: ${outfile} already starts with a shebang`,
220
- );
221
- } else {
222
- await writeFile(outfile, line + "\n" + content);
223
- }
224
- await chmod(outfile, 0o755);
225
- if (verbose)
226
- console.debug(
227
- `[gjsify] --shebang: wrote ${line} + chmod 0o755 to ${outfile}`,
228
- );
229
- }
230
-
231
- /** Application mode */
232
- async buildApp(app: App = "gjs"): Promise<RolldownOutput[]> {
233
- const {
234
- verbose,
235
- typescript,
236
- exclude,
237
- library: pkg,
238
- aliases,
239
- excludeGlobals,
240
- } = this.configData;
241
-
242
- const userBundler = normalizeBundlerOptions(this.configData);
243
-
244
- const formatRaw =
245
- (userBundler.output?.format as "esm" | "cjs" | "iife" | undefined) ??
246
- (userBundler.output?.file?.endsWith(".cjs") ? "cjs" : "esm");
247
- // The orchestrator only handles esm/cjs (iife is not a GJS / Node /
248
- // browser-bundle target we support). Coerce.
249
- const format: "esm" | "cjs" = formatRaw === "iife" ? "esm" : formatRaw;
250
-
251
- // Set default outfile if no outdir is set
252
- let outfile = userBundler.output?.file;
253
- let outdir = userBundler.output?.dir;
254
- if (!outfile && !outdir && (pkg?.main || pkg?.module)) {
255
- const candidate =
256
- format === "cjs"
257
- ? pkg.main ?? pkg.module
258
- : pkg.module ?? pkg.main;
259
- if (candidate && isUnsafeDefaultOutput(candidate)) {
260
- throw new Error(
261
- `gjsify build: refusing to default --outfile to ${candidate} ` +
262
- `(would overwrite a TypeScript source file). Pass --outfile/--outdir ` +
263
- `explicitly, or set "gjsify.bundler.output.file" in package.json.`,
264
- );
265
- }
266
- outfile = candidate;
267
- }
268
-
269
- const { consoleShim, globals } = this.configData;
270
-
271
- const userExternal = Array.isArray(userBundler.external)
272
- ? (userBundler.external as string[])
273
- : undefined;
274
- const userBanner = typeof userBundler.output?.banner === "string"
275
- ? (userBundler.output.banner as string)
276
- : undefined;
277
-
278
- const pluginOpts: PluginOptions = {
279
- debug: verbose,
280
- app,
281
- format,
282
- exclude,
283
- reflection: typescript?.reflection,
284
- consoleShim,
285
- ...(aliases ? { aliases } : {}),
286
- };
287
-
288
- const { autoMode, extras } = this.parseGlobalsValue(globals);
289
-
290
- const pnp = await buildPnpPlugin();
291
- const pnpPlugins: RolldownPluginOption[] = pnp ? [pnp] : [];
292
-
293
- // User-supplied text loaders need to be available during BOTH the
294
- // auto-globals pre-build (`detectAutoGlobals`) and the final build —
295
- // otherwise Rolldown's parser hits unknown extensions like `.ui` /
296
- // `.asm` during the pre-build, fails to parse them as JS/JSX, and
297
- // the auto-globals iteration aborts before the final plugin chain is
298
- // ever assembled. Build the user-plugin chain once, up front, and
299
- // pass it into both passes.
300
- const userTextLoader = textLoaderPlugin({ loaders: this.configData.loaders });
301
- const userPlugins: RolldownPluginOption[] = userTextLoader ? [userTextLoader] : [];
302
-
303
- // User-supplied bundler.plugins (mix of plugin objects + by-name
304
- // entries) — resolved from the project's node_modules. Same
305
- // ordering rationale as the text loader: must be present during
306
- // auto-globals pre-build to avoid claiming the same files via
307
- // Rolldown's default classifier.
308
- if (userBundler.plugins?.length) {
309
- const resolved = await resolveUserPlugins(userBundler.plugins, process.cwd());
310
- userPlugins.push(...resolved);
311
- }
312
-
313
- // --- Auto mode (with optional extras): iterative multi-pass build ---
314
- if (app === "gjs" && autoMode) {
315
- const gjsifyPluginFactory = async (opts: PluginOptions) => {
316
- const cfg = await gjsifyPlugin(
317
- {
318
- input: userBundler.input,
319
- output: { file: outfile, dir: outdir },
320
- userExternal,
321
- userBanner,
322
- userAliases: aliases,
323
- shebang: this.configData.shebang,
324
- },
325
- opts,
326
- );
327
- return cfg.plugins;
328
- };
329
-
330
- const { injectPath } = await detectAutoGlobals(
331
- {
332
- input: userBundler.input,
333
- plugins: [...pnpPlugins, ...userPlugins],
334
- external: userBundler.external,
335
- transform: userBundler.transform,
336
- format,
337
- },
338
- pluginOpts,
339
- gjsifyPluginFactory,
340
- verbose,
341
- { extraGlobalsList: extras, excludeGlobals },
342
- );
343
-
344
- pluginOpts.autoGlobalsInject = injectPath;
345
- } else if (extras) {
346
- pluginOpts.autoGlobalsInject = await this.resolveGlobalsInject(
347
- app,
348
- extras,
349
- verbose,
350
- );
351
- }
352
-
353
- // Final build: orchestrator → rolldown → write
354
- const cfg = await gjsifyPlugin(
355
- {
356
- input: userBundler.input,
357
- output: { file: outfile, dir: outdir },
358
- userExternal,
359
- userBanner,
360
- userAliases: aliases,
361
- shebang: this.configData.shebang,
362
- },
363
- pluginOpts,
364
- );
365
-
366
- const merged = mergeBundlerOptions(cfg.options as BundlerOptions, userBundler);
367
-
368
- const finalOpts: BundlerOptions = {
369
- ...merged,
370
- // Drop user-config plugins from `merged` — they survived
371
- // mergeBundlerOptions via spread but have already been resolved
372
- // and appended into `userPlugins` above. Re-emitting the raw
373
- // entries (which may include `BundlerPluginByName` shapes
374
- // Rolldown doesn't understand) would crash the build.
375
- plugins: [...pnpPlugins, ...userPlugins, ...cfg.plugins],
376
- };
377
-
378
- const build = await rolldown(finalOpts);
379
- let writeResult: RolldownOutput;
380
- try {
381
- writeResult = await build.write(finalOpts.output ?? {});
382
- } finally {
383
- await build.close();
384
- }
385
-
386
- if (app === "gjs" && this.configData.shebang) {
387
- await this.applyShebang(outfile, verbose);
388
- }
389
-
390
- return [writeResult];
391
- }
392
-
393
- async start(buildType: { library?: boolean; app?: App } = { app: "gjs" }) {
394
- if (buildType.library) {
395
- return await this.buildLibrary();
396
- }
397
- return await this.buildApp(buildType.app);
398
- }
399
- }
400
-
401
- interface OneLibraryBuildArgs {
402
- pluginOpts: PluginOptions;
403
- userBundler: BundlerOptions;
404
- output: { file?: string; dir?: string };
405
- userAliases?: Record<string, string>;
406
- pnpPlugins: RolldownPluginOption[];
407
- }
408
-
409
- async function runOneLibraryBuild(args: OneLibraryBuildArgs): Promise<RolldownOutput> {
410
- const cfg = await gjsifyPlugin(
411
- {
412
- input: args.userBundler.input,
413
- output: args.output,
414
- userAliases: args.userAliases,
415
- },
416
- args.pluginOpts,
417
- );
418
-
419
- const merged = mergeBundlerOptions(cfg.options as BundlerOptions, args.userBundler);
420
- const finalOpts: BundlerOptions = {
421
- ...merged,
422
- plugins: [...args.pnpPlugins, ...cfg.plugins],
423
- };
424
-
425
- const build = await rolldown(finalOpts);
426
- try {
427
- return await build.write(finalOpts.output ?? {});
428
- } finally {
429
- await build.close();
430
- }
431
- }
@@ -1 +0,0 @@
1
- export * from './build.js';
@@ -1,146 +0,0 @@
1
- import { Config } from '../config.js';
2
- import { BuildAction } from '../actions/build.js';
3
- import type { Command, CliBuildOptions } from '../types/index.js';
4
-
5
- export const buildCommand: Command<any, CliBuildOptions> = {
6
- command: 'build [entryPoints..]',
7
- description: 'Build and bundle your Gjs project',
8
- builder: (yargs) => {
9
- return yargs
10
- .option('entry-points', {
11
- description: "The entry points you want to bundle. Defaults to bundler.input from package.json#gjsify or .gjsifyrc.js, falling back to src/index.ts when neither is set.",
12
- array: true,
13
- type: 'string',
14
- normalize: true,
15
- // No yargs `default` here on purpose. A yargs default value
16
- // is indistinguishable from "user passed the flag" in the
17
- // parsed args (cliArgs.entryPoints?.length is truthy either
18
- // way), so the merge step in config.ts would unconditionally
19
- // overwrite `bundler.input` declared in package.json#gjsify —
20
- // silently ignoring `gjsify.bundler.input: "src/start.ts"`
21
- // and producing a bundle from the wrong entry point. The
22
- // fallback to src/index.ts is applied in config.ts AFTER
23
- // merging with the cosmiconfig data.
24
- defaultDescription: "src/index.ts (fallback)",
25
- coerce: (arg: string[]) => {
26
- // Removes duplicates
27
- return [...new Set(arg)];
28
- }
29
- })
30
- .option('exclude', {
31
- description: "An array of glob patterns to exclude entry-points and aliases",
32
- array: true,
33
- type: 'string',
34
- normalize: true,
35
- default: []
36
- })
37
- .option('verbose', {
38
- description: "Switch on the verbose mode",
39
- type: 'boolean',
40
- normalize: true,
41
- default: false
42
- })
43
- .option('app', {
44
- description: "Use this if you want to build an application, the platform node is usually only used for tests",
45
- type: 'string',
46
- choices: ['gjs', 'node', 'browser'],
47
- normalize: true,
48
- default: 'gjs'
49
- })
50
- .option('format', {
51
- description: "Override the default output format",
52
- type: 'string',
53
- choices: ['iife', 'esm', 'cjs'],
54
- normalize: true,
55
- })
56
- .option('minify', {
57
- description: "Minify the bundled output. Defaults to true; use --no-minify to emit pretty-printed code (e.g. for debugging or readable bundle review).",
58
- type: 'boolean',
59
- normalize: true,
60
- defaultDescription: 'true',
61
- })
62
- .option('library', {
63
- description: "Use this if you want to build a library for Gjsify",
64
- type: 'boolean',
65
- normalize: true,
66
- default: false
67
- })
68
- .option('outfile', {
69
- alias: 'o',
70
- description: "Sets the output file name for the build operation. If no outfile is specified, the outfile will be parsed from the package.json. Only used if application mode is active",
71
- type: 'string',
72
- normalize: true,
73
- })
74
- .option('outdir', {
75
- alias: 'd',
76
- description: "Sets the output directory for the build operation. If no outdir is specified, the outdir will be parsed from the package.json. Only used if library mode is active",
77
- type: 'string',
78
- normalize: true,
79
- })
80
- .option('reflection', {
81
- alias: 'r',
82
- description: "Enables TypeScript types on runtime using Deepkit's type compiler",
83
- type: 'boolean',
84
- normalize: true,
85
- default: false
86
- })
87
- .option('log-level', {
88
- description: "The log level can be changed to prevent esbuild from printing warning and/or error messages to the terminal",
89
- type: 'string',
90
- choices: ['silent', 'error', 'warning', 'info', 'debug', 'verbose'],
91
- normalize: true,
92
- default: 'warning'
93
- })
94
- .option('console-shim', {
95
- description: "Inject a console shim into GJS builds for clean output without the GLib prefix and with working ANSI colors. Use --no-console-shim to disable. Only applies to GJS app builds.",
96
- type: 'boolean',
97
- normalize: true,
98
- default: true
99
- })
100
- .option('globals', {
101
- description: "Comma-separated list of global identifiers, 'auto' (default) to detect automatically from the bundled output, or 'none' to disable. The 'auto' token may be combined with explicit identifiers/groups (e.g. 'auto,dom') for cases where the detector cannot statically see a global because it's accessed via indirection. Each identifier is mapped to the corresponding `@gjsify/<pkg>/register` module and injected into the bundle. See the CLI Reference docs for the full list of known identifiers. Only applies to GJS app builds.",
102
- type: 'string',
103
- normalize: true,
104
- default: 'auto'
105
- })
106
- .option('shebang', {
107
- description: "Prepend a `#!/usr/bin/env -S gjs -m` shebang to the output and mark it executable (chmod 755). Only applies to GJS app builds with a single --outfile. Default: false (use --shebang to enable, or set `shebang: true` in `.gjsifyrc.js`).",
108
- type: 'boolean',
109
- normalize: true
110
- })
111
- .option('external', {
112
- description: "Module names that should NOT be bundled. Repeat the flag or pass a comma-separated list (e.g. --external typedoc,prettier). Globs are forwarded to esbuild as-is. See https://esbuild.github.io/api/#external",
113
- array: true,
114
- type: 'string',
115
- default: [] as string[],
116
- coerce: (arg: string[]) => arg.flatMap((v) => v.split(',').map((s) => s.trim()).filter(Boolean)),
117
- })
118
- .option('define', {
119
- description: "Substitute compile-time constants. Each entry is KEY=VALUE where VALUE is a JS expression (string literals must be quoted: --define VERSION='\"1.2.3\"'). Repeat the flag or pass comma-separated. See https://esbuild.github.io/api/#define",
120
- array: true,
121
- type: 'string',
122
- default: [] as string[],
123
- })
124
- .option('alias', {
125
- description: "Map module specifiers at bundle time. Each entry is FROM=TO (e.g. --alias typedoc=@gjsify/empty). Layered on top of the built-in alias map. Useful for stubbing heavy deps the test scenario never executes.",
126
- array: true,
127
- type: 'string',
128
- default: [] as string[],
129
- coerce: (arg: string[]) => arg.flatMap((v) => v.split(',').map((s) => s.trim()).filter(Boolean)),
130
- })
131
- .option('exclude-globals', {
132
- description: "Comma-separated global identifiers to remove from auto-detection results. Use for false positives from dead browser-compat code whose polyfills require unavailable native libraries (e.g. --exclude-globals fetch,XMLHttpRequest).",
133
- type: 'string',
134
- normalize: true,
135
- })
136
- },
137
- handler: async (args) => {
138
- const config = new Config();
139
- const configData = await config.forBuild(args);
140
- const action = new BuildAction(configData);
141
- await action.start({
142
- library: args.library,
143
- app: args.app,
144
- })
145
- }
146
- }