@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
@@ -1,195 +0,0 @@
1
- // `gjsify install [pkg...]` — install packages with gjsify-aware post-checks.
2
- //
3
- // Modes:
4
- // gjsify install → project install (npm install)
5
- // gjsify install <pkg> [<pkg>...] → add package(s) to project (npm install <pkg>...)
6
- // gjsify install -g <pkg> [...] → user-global install (XDG, GJS-runnable bin)
7
- //
8
- // Project mode delegates to `npm install` in cwd and runs `runMinimalChecks()`
9
- // + `detectNativePackages()` to surface missing system deps and `@gjsify/*`
10
- // packages with native prebuilds.
11
- //
12
- // Global mode is the GJS equivalent of `npm i -g`: extracts the package tree
13
- // into `${XDG_DATA_HOME}/gjsify/global/node_modules/<pkg>/` via the native
14
- // install backend (no Node/npm required at runtime), then symlinks the bins
15
- // declared by `gjsify.bin` (preferred) or `bin` (fallback) into
16
- // `~/.local/bin/`. Subsequent commands invoked by name resolve to the
17
- // extracted package, so package-relative assets like `@ts-for-gir/cli`'s
18
- // `dist-templates/` are found by ordinary `__dirname/..` resolution — no
19
- // embedded asset stores, no separate release tarballs.
20
-
21
- import { spawn } from 'node:child_process';
22
- import { mkdirSync } from 'node:fs';
23
- import type { Command } from '../types/index.js';
24
- import {
25
- buildInstallCommand,
26
- detectPackageManager,
27
- runMinimalChecks,
28
- } from '../utils/check-system-deps.js';
29
- import { detectNativePackages } from '../utils/detect-native-packages.js';
30
- import { installPackages } from '../utils/install-backend.js';
31
- import {
32
- binDirOnPath,
33
- defaultGlobalLayout,
34
- linkGlobalBins,
35
- specToPackageName,
36
- } from '../utils/install-global.js';
37
-
38
- interface InstallOptions {
39
- packages?: string[];
40
- global?: boolean;
41
- 'save-dev'?: boolean;
42
- 'save-peer'?: boolean;
43
- 'save-optional'?: boolean;
44
- verbose: boolean;
45
- }
46
-
47
- export const installCommand: Command<any, InstallOptions> = {
48
- command: 'install [packages..]',
49
- description:
50
- 'Install npm dependencies in the current project (or globally with -g), then run gjsify-aware post-checks.',
51
- builder: (yargs) =>
52
- yargs
53
- .positional('packages', {
54
- description: 'Optional package specs. With none, runs a full project install.',
55
- type: 'string',
56
- array: true,
57
- })
58
- .option('global', {
59
- description:
60
- 'Install into the user-global XDG location and symlink bins into ~/.local/bin.',
61
- type: 'boolean',
62
- alias: 'g',
63
- default: false,
64
- })
65
- .option('save-dev', { type: 'boolean', alias: 'D' })
66
- .option('save-peer', { type: 'boolean' })
67
- .option('save-optional', { type: 'boolean', alias: 'O' })
68
- .option('verbose', {
69
- description: 'Verbose install logging.',
70
- type: 'boolean',
71
- default: false,
72
- }),
73
- handler: async (args) => {
74
- if (args.global) {
75
- if (!args.packages || args.packages.length === 0) {
76
- console.error(
77
- 'gjsify install --global requires at least one <pkg> argument.',
78
- );
79
- process.exit(1);
80
- }
81
- for (const flag of ['save-dev', 'save-peer', 'save-optional'] as const) {
82
- if (args[flag]) {
83
- console.warn(
84
- `gjsify install --global ignores --${flag}: global installs do not modify a project package.json.`,
85
- );
86
- }
87
- }
88
- await installGlobalAndLink(args.packages, { verbose: args.verbose });
89
- return;
90
- }
91
-
92
- const npmArgs = ['install'];
93
- if (args['save-dev']) npmArgs.push('--save-dev');
94
- if (args['save-peer']) npmArgs.push('--save-peer');
95
- if (args['save-optional']) npmArgs.push('--save-optional');
96
- if (args.verbose) npmArgs.push('--loglevel', 'verbose');
97
- if (args.packages && args.packages.length > 0) {
98
- npmArgs.push(...args.packages);
99
- }
100
-
101
- await spawnNpm(npmArgs);
102
-
103
- await runPostInstallChecks();
104
- },
105
- };
106
-
107
- async function spawnNpm(npmArgs: string[]): Promise<void> {
108
- return new Promise<void>((resolve, reject) => {
109
- const child = spawn('npm', npmArgs, { stdio: 'inherit' });
110
- child.on('close', (code) => {
111
- if (code === 0) resolve();
112
- else reject(new Error(`npm install exited with code ${code}`));
113
- });
114
- child.on('error', (err) => {
115
- const code = (err as NodeJS.ErrnoException).code;
116
- const msg = code === 'ENOENT'
117
- ? 'npm not found on PATH — install Node.js first.'
118
- : `npm install failed: ${err.message}`;
119
- reject(new Error(msg));
120
- });
121
- }).catch((err: Error) => {
122
- console.error(err.message);
123
- process.exit(1);
124
- });
125
- }
126
-
127
- async function installGlobalAndLink(
128
- specs: string[],
129
- opts: { verbose: boolean },
130
- ): Promise<void> {
131
- const layout = defaultGlobalLayout();
132
- mkdirSync(layout.prefix, { recursive: true });
133
-
134
- console.log(`gjsify install --global → ${layout.prefix}`);
135
- console.log(` bins → ${layout.binDir}`);
136
-
137
- await installPackages({
138
- prefix: layout.prefix,
139
- specs,
140
- verbose: opts.verbose,
141
- });
142
-
143
- const packageNames = specs.map(specToPackageName);
144
- const created = linkGlobalBins(packageNames, layout);
145
-
146
- if (created.length === 0) {
147
- console.warn(
148
- '\nNo bins declared (neither `gjsify.bin` nor `bin` in package.json) — nothing was symlinked.',
149
- );
150
- } else {
151
- console.log(`\nLinked ${created.length} bin(s):`);
152
- for (const e of created) {
153
- console.log(` • ${e.link} → ${e.target}`);
154
- }
155
- }
156
-
157
- if (created.length > 0 && !binDirOnPath(layout.binDir)) {
158
- console.warn(
159
- `\nNote: ${layout.binDir} is not on your PATH.\n` +
160
- `Add it to your shell rc file:\n export PATH="${layout.binDir}:$PATH"`,
161
- );
162
- }
163
- }
164
-
165
- async function runPostInstallChecks(): Promise<void> {
166
- console.log('\n--- gjsify post-install checks ---');
167
-
168
- // 1. System deps that GJS apps typically need.
169
- const results = runMinimalChecks();
170
- const missing = results.filter((r) => !r.found && r.severity === 'required');
171
- if (missing.length > 0) {
172
- console.warn('Missing required system dependencies:\n');
173
- for (const dep of missing) {
174
- console.warn(` ✗ ${dep.name}`);
175
- }
176
- const pm = detectPackageManager();
177
- const cmd = buildInstallCommand(pm, missing);
178
- if (cmd) console.warn(`\nInstall with:\n ${cmd}`);
179
- } else {
180
- console.log('System dependencies OK.');
181
- }
182
-
183
- // 2. Surface @gjsify/* packages with native prebuilds — `gjsify run`
184
- // will set LD_LIBRARY_PATH / GI_TYPELIB_PATH for these automatically.
185
- const native = detectNativePackages(process.cwd());
186
- if (native.length > 0) {
187
- console.log(
188
- `\nDetected ${native.length} @gjsify/* package(s) with native prebuilds:`,
189
- );
190
- for (const pkg of native) {
191
- console.log(` • ${pkg.name}`);
192
- }
193
- console.log('\nUse `gjsify run <bundle>` to launch with LD_LIBRARY_PATH/GI_TYPELIB_PATH set.');
194
- }
195
- }
@@ -1,33 +0,0 @@
1
- import { resolve } from 'node:path';
2
- import type { Command } from '../types/index.js';
3
- import { runGjsBundle } from '../utils/run-gjs.js';
4
-
5
- interface RunOptions {
6
- file: string;
7
- args: string[];
8
- }
9
-
10
- export const runCommand: Command<any, RunOptions> = {
11
- command: 'run <file> [args..]',
12
- description: 'Run a GJS bundle, automatically setting LD_LIBRARY_PATH and GI_TYPELIB_PATH for any installed native gjsify packages (e.g. @gjsify/webgl).',
13
- builder: (yargs) => {
14
- return yargs
15
- .positional('file', {
16
- description: 'The GJS bundle to run (e.g. dist/gjs.js)',
17
- type: 'string',
18
- normalize: true,
19
- demandOption: true,
20
- })
21
- .positional('args', {
22
- description: 'Extra arguments passed through to gjs',
23
- type: 'string',
24
- array: true,
25
- default: [],
26
- });
27
- },
28
- handler: async (args) => {
29
- const file = resolve(args.file as string);
30
- const extraArgs = (args.args as string[]) ?? [];
31
- await runGjsBundle(file, extraArgs);
32
- },
33
- };
@@ -1,149 +0,0 @@
1
- import type { Command } from '../types/index.js';
2
- import { discoverShowcases, findShowcase } from '../utils/discover-showcases.js';
3
- import {
4
- runMinimalChecks,
5
- detectPackageManager,
6
- buildInstallCommand,
7
- } from '../utils/check-system-deps.js';
8
- import { spawn } from 'node:child_process';
9
- import { fileURLToPath } from 'node:url';
10
- import { readFileSync } from 'node:fs';
11
-
12
- function readCliVersion(): string | undefined {
13
- try {
14
- const pkgUrl = new URL('../../package.json', import.meta.url);
15
- const pkg = JSON.parse(readFileSync(pkgUrl, 'utf8')) as { version?: unknown };
16
- return typeof pkg.version === 'string' ? pkg.version : undefined;
17
- } catch {
18
- return undefined;
19
- }
20
- }
21
-
22
- interface ShowcaseOptions {
23
- name?: string;
24
- json: boolean;
25
- list: boolean;
26
- }
27
-
28
- export const showcaseCommand: Command<any, ShowcaseOptions> = {
29
- command: 'showcase [name]',
30
- description: 'List or run curated gjsify showcase applications.',
31
- builder: (yargs) =>
32
- yargs
33
- .positional('name', {
34
- description: 'Showcase name to run (omit to list all)',
35
- type: 'string',
36
- })
37
- .option('json', {
38
- description: 'Output as JSON',
39
- type: 'boolean',
40
- default: false,
41
- })
42
- .option('list', {
43
- description: 'List available showcases',
44
- type: 'boolean',
45
- default: false,
46
- }),
47
- handler: async (args) => {
48
- // List mode: no name given, or --list flag
49
- if (!args.name || args.list) {
50
- const showcases = discoverShowcases();
51
-
52
- if (args.json) {
53
- console.log(JSON.stringify(showcases, null, 2));
54
- return;
55
- }
56
-
57
- if (showcases.length === 0) {
58
- console.log('No showcases found. The CLI ships a curated list in `showcases.json`; if it is missing the CLI install is incomplete.');
59
- return;
60
- }
61
-
62
- const grouped = new Map<string, typeof showcases>();
63
- for (const sc of showcases) {
64
- const list = grouped.get(sc.category) ?? [];
65
- list.push(sc);
66
- grouped.set(sc.category, list);
67
- }
68
-
69
- console.log('Available gjsify showcases:\n');
70
- for (const [category, list] of grouped) {
71
- console.log(` ${category.toUpperCase()}:`);
72
- const maxNameLen = Math.max(...list.map((e) => e.name.length));
73
- for (const sc of list) {
74
- const pad = ' '.repeat(maxNameLen - sc.name.length + 2);
75
- const desc = sc.description ? `${pad}${sc.description}` : '';
76
- console.log(` ${sc.name}${desc}`);
77
- }
78
- console.log('');
79
- }
80
-
81
- console.log('Run a showcase: gjsify showcase <name>');
82
- return;
83
- }
84
-
85
- const showcase = findShowcase(args.name);
86
- if (!showcase) {
87
- console.error(`Unknown showcase: "${args.name}"`);
88
- console.error('Run "gjsify showcase" to list available showcases.');
89
- process.exit(1);
90
- }
91
-
92
- // System dependency check before delegating — only system libs (gjs,
93
- // gtk4, …). The showcase's npm deps (incl. `@gjsify/webgl` with the
94
- // gwebgl Vala prebuild) are fetched by `gjsify dlx` into the npm
95
- // cache, and `runGjsBundle()` picks the prebuild up from the bundle
96
- // dir via `detectNativePackages()`. Pre-flight-checking npm deps
97
- // here would fail for `npx @gjsify/cli showcase` (no project
98
- // node_modules, CLI doesn't dep on the showcase libs).
99
- const results = runMinimalChecks();
100
- const missingHard = results.filter(
101
- (r) => !r.found && r.severity === 'required',
102
- );
103
- if (missingHard.length > 0) {
104
- console.error('Missing system dependencies:\n');
105
- for (const dep of missingHard) {
106
- console.error(` ✗ ${dep.name}`);
107
- }
108
- const pm = detectPackageManager();
109
- const cmd = buildInstallCommand(pm, missingHard);
110
- if (cmd) {
111
- console.error(`\nInstall with:\n ${cmd}`);
112
- }
113
- process.exit(1);
114
- }
115
-
116
- // Delegate to `gjsify dlx <package>@<cli-version>` — same npm-cache,
117
- // same atomic symlink-swap, same `gjsify.main` resolution. Re-spawning
118
- // the CLI keeps the dlx logic in one place.
119
- //
120
- // Pinning to the CLI's own version is load-bearing: showcases ship in
121
- // lockstep with the CLI, so users running `npx @gjsify/cli@X showcase
122
- // <name>` expect the matching `@gjsify/example-*@X`. Without the pin,
123
- // dlx caches the first resolved-latest on disk; subsequent CLI
124
- // releases leave that cache untouched until the 7-day TTL expires,
125
- // and the user gets a stale showcase that may be missing deps the
126
- // newer CLI assumes (the `@gjsify/http-soup-bridge` regression
127
- // reported against `@gjsify/cli@0.3.17`).
128
- const cliVersion = readCliVersion();
129
- const dlxSpec = cliVersion ? `${showcase.packageName}@${cliVersion}` : showcase.packageName;
130
- console.log(`Running showcase: ${showcase.name} (via gjsify dlx ${dlxSpec})\n`);
131
- const cliBin = fileURLToPath(new URL('../index.js', import.meta.url));
132
- const child = spawn(process.execPath, [cliBin, 'dlx', dlxSpec], {
133
- stdio: 'inherit',
134
- });
135
- await new Promise<void>((resolvePromise, reject) => {
136
- child.on('close', (code) => {
137
- if (code !== 0) {
138
- reject(new Error(`gjsify dlx exited with code ${code}`));
139
- } else {
140
- resolvePromise();
141
- }
142
- });
143
- child.on('error', reject);
144
- }).catch((err) => {
145
- console.error(err.message);
146
- process.exit(1);
147
- });
148
- },
149
- };
package/src/config.ts DELETED
@@ -1,304 +0,0 @@
1
- import { APP_NAME } from './constants.js';
2
- import { cosmiconfig, type Options as LoadOptions } from 'cosmiconfig';
3
-
4
- /** Default cosmiconfig search places for a given module name (matches cosmiconfig defaults). */
5
- function defaultSearchPlaces(name: string): string[] {
6
- return [
7
- 'package.json',
8
- `.${name}rc`,
9
- `.${name}rc.json`,
10
- `.${name}rc.yaml`,
11
- `.${name}rc.yml`,
12
- `.${name}rc.js`,
13
- `.${name}rc.ts`,
14
- `.${name}rc.mjs`,
15
- `.${name}rc.cjs`,
16
- `${name}.config.js`,
17
- `${name}.config.ts`,
18
- `${name}.config.mjs`,
19
- `${name}.config.cjs`,
20
- ];
21
- }
22
- import { readPackageJSON, resolvePackageJSON } from 'pkg-types';
23
- import { getTsconfig } from 'get-tsconfig';
24
-
25
- /** Deep merge objects (replaces lodash.merge) */
26
- function merge<T extends Record<string, any>>(target: T, ...sources: Record<string, any>[]): T {
27
- for (const source of sources) {
28
- if (!source) continue;
29
- for (const key of Object.keys(source)) {
30
- const targetVal = (target as any)[key];
31
- const sourceVal = source[key];
32
- if (sourceVal !== undefined) {
33
- if (isPlainObject(targetVal) && isPlainObject(sourceVal)) {
34
- merge(targetVal, sourceVal);
35
- } else {
36
- (target as any)[key] = sourceVal;
37
- }
38
- }
39
- }
40
- }
41
- return target;
42
- }
43
-
44
- function isPlainObject(val: unknown): val is Record<string, any> {
45
- return typeof val === 'object' && val !== null && !Array.isArray(val) && Object.getPrototypeOf(val) === Object.prototype;
46
- }
47
-
48
- /**
49
- * Read a dotted path (`a.b.c`) from a plain object. Returns `undefined` for
50
- * any missing segment. Intentionally narrow — only used for surfacing
51
- * `package.json` fields into compile-time defines, not for arbitrary deep
52
- * traversal.
53
- */
54
- function readDottedPath(obj: Record<string, unknown>, path: string): unknown {
55
- if (!path.includes('.')) return obj[path];
56
- let cursor: unknown = obj;
57
- for (const segment of path.split('.')) {
58
- if (cursor === null || cursor === undefined || typeof cursor !== 'object') return undefined;
59
- cursor = (cursor as Record<string, unknown>)[segment];
60
- }
61
- return cursor;
62
- }
63
-
64
- import type { CliBuildOptions, ConfigData, CosmiconfigResult, ConfigDataTypescript, ConfigDataLibrary} from './types/index.js';
65
- import type { ArgumentsCamelCase } from 'yargs';
66
-
67
- export class Config {
68
-
69
- readonly loadOptions: Partial<LoadOptions> = {}
70
-
71
- constructor(loadOptions: Partial<LoadOptions> = {}) {
72
- if(Object.keys(loadOptions).length) {
73
- this.loadOptions = loadOptions;
74
- }
75
- }
76
-
77
- /** Loads gjsify config file, e.g `.gjsifyrc.js` */
78
- private async load(searchFrom?: string) {
79
- // cosmiconfig's default first-match-wins behaviour silently drops one
80
- // source when both `package.json#gjsify` and an explicit config file
81
- // (`.gjsifyrc.js`, `gjsify.config.mjs`, ...) are present. Project hits
82
- // this footgun: adding `gjsify.bin` to package.json (so `gjsify dlx`
83
- // resolves the GJS bundle) silently disables `.gjsifyrc.js`. We
84
- // explicitly load both sources and merge — package.json is the lower
85
- // layer, the explicit file wins on key collisions.
86
- //
87
- // Run two searches:
88
- // 1. Default (includes package.json) — for projects that only use
89
- // package.json#gjsify and no separate file.
90
- // 2. Explicit-file only (package.json excluded) — to find the
91
- // `.gjsifyrc.*` / `gjsify.config.*` regardless of whether
92
- // package.json#gjsify exists.
93
- const fileExplorer = cosmiconfig(APP_NAME, {
94
- ...this.loadOptions,
95
- searchPlaces: (this.loadOptions.searchPlaces ?? defaultSearchPlaces(APP_NAME))
96
- .filter((p) => p !== 'package.json'),
97
- });
98
- const fileResult = await fileExplorer.search(searchFrom) as CosmiconfigResult<ConfigData> | null;
99
-
100
- const merged: ConfigData = {};
101
- try {
102
- const pkg = await this.readPackageJSON(searchFrom) as { gjsify?: ConfigData };
103
- if (isPlainObject(pkg?.gjsify)) merge(merged, pkg.gjsify);
104
- } catch {
105
- // Missing or unreadable package.json — skip.
106
- }
107
- if (fileResult?.config && isPlainObject(fileResult.config)) {
108
- merge(merged, fileResult.config);
109
- }
110
-
111
- merged.bundler ||= {};
112
- merged.library ||= {};
113
- merged.typescript ||= {};
114
-
115
- return {
116
- config: merged,
117
- filepath: fileResult?.filepath ?? '',
118
- isEmpty: !fileResult && Object.keys(merged).length === 3, // only the three default-empty objects
119
- };
120
- }
121
-
122
- /** Loads package.json of the current project */
123
- private async readPackageJSON(dirPath?: string) {
124
- dirPath = await resolvePackageJSON(dirPath)
125
- const pkg = await readPackageJSON(dirPath);
126
- return pkg;
127
- }
128
-
129
- /** Loads tsconfig.json of the current project */
130
- private async readTSConfig(dirPath?: string) {
131
- const tsconfig = getTsconfig(dirPath)?.config || {};
132
- return tsconfig;
133
- }
134
-
135
- async forBuild(cliArgs: ArgumentsCamelCase<CliBuildOptions>) {
136
- const configFile = await this.load(process.cwd());
137
- const configData: ConfigData = {...configFile.config};
138
- const configFilePath = configFile.filepath || process.cwd();
139
- const pkg = await this.readPackageJSON(configFilePath) as ConfigDataLibrary;
140
- const tsConfig = await this.readTSConfig(configFilePath) as ConfigDataTypescript;
141
-
142
- tsConfig.reflection ||= cliArgs.reflection;
143
-
144
- // TODO replace with `cliArgs.logLevel`
145
- configData.verbose = cliArgs.verbose || false;
146
- configData.exclude = cliArgs.exclude || [];
147
- if (cliArgs.consoleShim !== undefined) configData.consoleShim = cliArgs.consoleShim;
148
- if (cliArgs.globals !== undefined) configData.globals = cliArgs.globals;
149
- if (cliArgs.shebang !== undefined) configData.shebang = cliArgs.shebang;
150
- if (cliArgs.excludeGlobals) {
151
- const raw = Array.isArray(cliArgs.excludeGlobals)
152
- ? cliArgs.excludeGlobals.join(',')
153
- : String(cliArgs.excludeGlobals);
154
- const ids = raw.split(',').map((s: string) => s.trim()).filter(Boolean);
155
- if (ids.length) configData.excludeGlobals = [...(configData.excludeGlobals ?? []), ...ids];
156
- }
157
-
158
- merge(configData.library ??= {}, pkg, configData.library);
159
- merge(configData.typescript ??= {}, tsConfig, configData.typescript);
160
-
161
- // Parse `KEY=VALUE` style flags into Record<string, string>.
162
- // - `--define`: VALUE is a JS expression (string literals must be
163
- // pre-quoted by the caller, e.g. `'"1.2.3"'`).
164
- // - `--alias`: VALUE is the substitute module specifier.
165
- const parseKvPairs = (entries: readonly string[], flag: string): Record<string, string> => {
166
- const out: Record<string, string> = {};
167
- for (const entry of entries) {
168
- const idx = entry.indexOf('=');
169
- if (idx === -1) {
170
- throw new Error(`Invalid --${flag} value '${entry}'. Expected KEY=VALUE.`);
171
- }
172
- const key = entry.slice(0, idx).trim();
173
- const value = entry.slice(idx + 1);
174
- if (!key) {
175
- throw new Error(`Invalid --${flag} value '${entry}'. Empty key.`);
176
- }
177
- out[key] = value;
178
- }
179
- return out;
180
- };
181
- const defineMap = parseKvPairs(cliArgs.define ?? [], 'define');
182
- const aliasMap = parseKvPairs(cliArgs.alias ?? [], 'alias');
183
- if (Object.keys(aliasMap).length) {
184
- configData.aliases = { ...(configData.aliases ?? {}), ...aliasMap };
185
- }
186
-
187
- // Resolve `defineFromPackageJson` / `defineFromEnv` into raw
188
- // KEY=<JSON-stringified value> entries that get merged into the
189
- // bundler's `transform.define` map below. Both produce JS expressions
190
- // (the value side of a Rolldown define is substituted at the call
191
- // site, not stringified again) — so a missing env variable resolves
192
- // to the literal `undefined`, letting consumer code use
193
- // `typeof X === 'undefined'` or `X ?? fallback` guards.
194
- const fromPkgDefines: Record<string, string> = {};
195
- if (configData.defineFromPackageJson) {
196
- for (const [name, spec] of Object.entries(configData.defineFromPackageJson)) {
197
- if (!spec || typeof spec.field !== 'string' || !spec.field) {
198
- throw new Error(
199
- `gjsify config: defineFromPackageJson["${name}"] is missing a "field" string`,
200
- );
201
- }
202
- const value = readDottedPath(pkg as Record<string, unknown>, spec.field);
203
- fromPkgDefines[name] = value === undefined ? 'undefined' : JSON.stringify(value);
204
- }
205
- }
206
- const fromEnvDefines: Record<string, string> = {};
207
- if (configData.defineFromEnv) {
208
- for (const [name, spec] of Object.entries(configData.defineFromEnv)) {
209
- if (!spec || typeof spec.env !== 'string' || !spec.env) {
210
- throw new Error(
211
- `gjsify config: defineFromEnv["${name}"] is missing an "env" string`,
212
- );
213
- }
214
- const raw = process.env[spec.env];
215
- const value = raw !== undefined ? raw : spec.default;
216
- fromEnvDefines[name] = value === undefined ? 'undefined' : JSON.stringify(value);
217
- }
218
- }
219
-
220
- // Merge CLI flags into the Rolldown-shape `bundler` field. Mappings:
221
- // --entry-points → bundler.input
222
- // --outfile → bundler.output.file
223
- // --outdir → bundler.output.dir
224
- // --format → bundler.output.format
225
- // --minify → bundler.output.minify
226
- // --log-level → bundler.logLevel
227
- // --external → bundler.external
228
- // --define → bundler.transform.define
229
- const bundler = (configData.bundler ??= {});
230
- const output = (bundler.output ??= {});
231
- const transform = (bundler.transform ??= {});
232
-
233
- if (cliArgs.entryPoints?.length) bundler.input = cliArgs.entryPoints;
234
- // Fallback when neither the CLI flag nor the cosmiconfig data set an
235
- // entry point. Applied here (post-merge) rather than as a yargs
236
- // `default:` because yargs defaults are indistinguishable from
237
- // user-set values, and would silently overwrite `bundler.input`
238
- // declared in package.json#gjsify.
239
- if (!bundler.input) bundler.input = ['src/index.ts'];
240
- if (cliArgs.outfile !== undefined) output.file = cliArgs.outfile;
241
- if (cliArgs.outdir !== undefined) output.dir = cliArgs.outdir;
242
- if (cliArgs.format !== undefined) output.format = cliArgs.format as 'esm' | 'cjs' | 'iife';
243
- // CLI flag wins over config; if neither is set, minify by default.
244
- // Pretty-printed output is opt-in via `--no-minify` or
245
- // `bundler.output.minify: false` in the config.
246
- //
247
- // When minify is enabled (boolean true) we expand it to a MinifyOptions
248
- // object that PRESERVES function and class .name properties. Rolldown's
249
- // default mangler renames every top-level class to short identifiers
250
- // (`e`, `t`, ...), which collapses Function.name → 'e' for many
251
- // distinct classes. Libraries like Excalibur key runtime data
252
- // structures off `c.name` (e.g. `Query.createId` hashes
253
- // `c_${component.name}` to dedupe ECS queries), so once class names
254
- // collide every query with N components is treated as identical and
255
- // the wrong filter wins. Keeping the .name property only costs a few
256
- // bytes per class but keeps name-driven library code working
257
- // (Excalibur ECS, deepkit reflection, error stacks, etc.).
258
- if (cliArgs.minify !== undefined) output.minify = cliArgs.minify;
259
- if (output.minify === undefined) output.minify = true;
260
- if (output.minify === true) {
261
- output.minify = { mangle: { keepNames: { function: true, class: true } } };
262
- }
263
- if (cliArgs.logLevel) {
264
- // Map esbuild log levels to Rolldown's narrower set:
265
- // esbuild → rolldown
266
- // silent → silent
267
- // error → warn (rolldown has no error-only)
268
- // warning → warn
269
- // info → info
270
- // debug → debug
271
- // verbose → debug (rolldown has no verbose)
272
- const map: Record<string, 'silent' | 'warn' | 'info' | 'debug'> = {
273
- silent: 'silent',
274
- error: 'warn',
275
- warning: 'warn',
276
- warn: 'warn',
277
- info: 'info',
278
- debug: 'debug',
279
- verbose: 'debug',
280
- };
281
- const level = map[cliArgs.logLevel] ?? 'warn';
282
- bundler.logLevel = level;
283
- }
284
- if (cliArgs.external?.length) {
285
- const userExternal = Array.isArray(bundler.external) ? bundler.external : [];
286
- bundler.external = [...userExternal, ...cliArgs.external];
287
- }
288
- if (Object.keys(defineMap).length || Object.keys(fromPkgDefines).length || Object.keys(fromEnvDefines).length) {
289
- // CLI --define wins over package.json/env (manual overrides during
290
- // debugging beat declarative config).
291
- transform.define = {
292
- ...(transform.define ?? {}),
293
- ...fromPkgDefines,
294
- ...fromEnvDefines,
295
- ...defineMap,
296
- };
297
- }
298
-
299
- if(configData.verbose) console.debug("configData", configData);
300
-
301
- return configData;
302
-
303
- }
304
- }
package/src/constants.ts DELETED
@@ -1 +0,0 @@
1
- export const APP_NAME = 'gjsify'