@gjsify/cli 0.3.2 → 0.3.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/lib/actions/build.d.ts +3 -3
- package/lib/actions/build.js +91 -30
- package/package.json +10 -10
- package/src/actions/build.ts +364 -264
package/lib/actions/build.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ConfigData } from
|
|
2
|
-
import type { App } from
|
|
3
|
-
import { BuildOptions, BuildResult, Plugin } from
|
|
1
|
+
import type { ConfigData } from "../types/index.js";
|
|
2
|
+
import type { App } from "@gjsify/esbuild-plugin-gjsify";
|
|
3
|
+
import { BuildOptions, BuildResult, Plugin } from "esbuild";
|
|
4
4
|
export declare class BuildAction {
|
|
5
5
|
readonly configData: ConfigData;
|
|
6
6
|
constructor(configData?: ConfigData);
|
package/lib/actions/build.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { build } from
|
|
2
|
-
import { gjsifyPlugin } from
|
|
3
|
-
import { resolveGlobalsList, writeRegisterInjectFile, detectAutoGlobals } from
|
|
4
|
-
import { dirname, extname, join } from
|
|
5
|
-
import { chmod, readFile, writeFile } from
|
|
6
|
-
import { existsSync } from
|
|
1
|
+
import { build } from "esbuild";
|
|
2
|
+
import { gjsifyPlugin } from "@gjsify/esbuild-plugin-gjsify";
|
|
3
|
+
import { resolveGlobalsList, writeRegisterInjectFile, detectAutoGlobals, } from "@gjsify/esbuild-plugin-gjsify/globals";
|
|
4
|
+
import { dirname, extname, join } from "node:path";
|
|
5
|
+
import { chmod, readFile, writeFile } from "node:fs/promises";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
7
|
const GJS_SHEBANG = "#!/usr/bin/env -S gjs -m\n";
|
|
8
8
|
/** Walk up from dir until .pnp.cjs is found; return its directory or null. */
|
|
9
9
|
function findPnpRoot(dir) {
|
|
@@ -21,13 +21,36 @@ function findPnpRoot(dir) {
|
|
|
21
21
|
* If the current project uses Yarn PnP, return the official
|
|
22
22
|
* @yarnpkg/esbuild-plugin-pnp plugin so esbuild can resolve
|
|
23
23
|
* modules from zip archives without manual extraction.
|
|
24
|
+
*
|
|
25
|
+
* Custom onResolve: fall through on UNDECLARED_DEPENDENCY errors so the
|
|
26
|
+
* gjsify alias plugin can handle bare specifiers (e.g. `abort-controller`)
|
|
27
|
+
* that PnP can't resolve from the inject file's issuer context but that
|
|
28
|
+
* gjsify maps to `@gjsify/*` packages the project DOES have available.
|
|
24
29
|
*/
|
|
25
30
|
async function getPnpPlugin() {
|
|
26
31
|
if (!findPnpRoot(process.cwd()))
|
|
27
32
|
return null;
|
|
28
33
|
try {
|
|
29
34
|
const { pnpPlugin } = await import("@yarnpkg/esbuild-plugin-pnp");
|
|
30
|
-
return pnpPlugin(
|
|
35
|
+
return pnpPlugin({
|
|
36
|
+
onResolve: async (_args, { resolvedPath, error, watchFiles }) => {
|
|
37
|
+
if (resolvedPath !== null) {
|
|
38
|
+
return { namespace: "pnp", path: resolvedPath, watchFiles };
|
|
39
|
+
}
|
|
40
|
+
// UNDECLARED_DEPENDENCY: package exists transitively but isn't
|
|
41
|
+
// in the issuer's direct deps. Fall through so the gjsify alias
|
|
42
|
+
// plugin can resolve it (e.g. bare → @gjsify/* mappings).
|
|
43
|
+
if (error?.pnpCode ===
|
|
44
|
+
"UNDECLARED_DEPENDENCY") {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
external: true,
|
|
49
|
+
errors: error ? [{ text: error.message }] : [],
|
|
50
|
+
watchFiles,
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
});
|
|
31
54
|
}
|
|
32
55
|
catch {
|
|
33
56
|
return null;
|
|
@@ -40,7 +63,7 @@ export class BuildAction {
|
|
|
40
63
|
}
|
|
41
64
|
getEsBuildDefaults() {
|
|
42
65
|
const defaults = {
|
|
43
|
-
allowOverwrite: true
|
|
66
|
+
allowOverwrite: true,
|
|
44
67
|
};
|
|
45
68
|
return defaults;
|
|
46
69
|
}
|
|
@@ -59,7 +82,9 @@ export class BuildAction {
|
|
|
59
82
|
const pnpPlugins = pnpPlugin ? [pnpPlugin] : [];
|
|
60
83
|
const results = [];
|
|
61
84
|
if (multipleBuilds) {
|
|
62
|
-
const moduleFormat = moduleOutdir.includes("/cjs") || moduleOutExt === ".cjs"
|
|
85
|
+
const moduleFormat = moduleOutdir.includes("/cjs") || moduleOutExt === ".cjs"
|
|
86
|
+
? "cjs"
|
|
87
|
+
: "esm";
|
|
63
88
|
results.push(await build({
|
|
64
89
|
...this.getEsBuildDefaults(),
|
|
65
90
|
...esbuild,
|
|
@@ -67,7 +92,13 @@ export class BuildAction {
|
|
|
67
92
|
outdir: moduleOutdir,
|
|
68
93
|
plugins: [
|
|
69
94
|
...pnpPlugins,
|
|
70
|
-
gjsifyPlugin({
|
|
95
|
+
gjsifyPlugin({
|
|
96
|
+
debug: verbose,
|
|
97
|
+
library: moduleFormat,
|
|
98
|
+
exclude,
|
|
99
|
+
reflection: typescript?.reflection,
|
|
100
|
+
jsExtension: moduleOutExt,
|
|
101
|
+
}),
|
|
71
102
|
],
|
|
72
103
|
}));
|
|
73
104
|
const mainFormat = mainOutdir.includes("/cjs") || mainOutExt === ".cjs" ? "cjs" : "esm";
|
|
@@ -78,7 +109,13 @@ export class BuildAction {
|
|
|
78
109
|
outdir: mainOutdir,
|
|
79
110
|
plugins: [
|
|
80
111
|
...pnpPlugins,
|
|
81
|
-
gjsifyPlugin({
|
|
112
|
+
gjsifyPlugin({
|
|
113
|
+
debug: verbose,
|
|
114
|
+
library: mainFormat,
|
|
115
|
+
exclude,
|
|
116
|
+
reflection: typescript?.reflection,
|
|
117
|
+
jsExtension: mainOutdir,
|
|
118
|
+
}),
|
|
82
119
|
],
|
|
83
120
|
}));
|
|
84
121
|
}
|
|
@@ -86,7 +123,8 @@ export class BuildAction {
|
|
|
86
123
|
const outfilePath = esbuild?.outfile || library?.module || library?.main;
|
|
87
124
|
const outExt = outfilePath ? extname(outfilePath) : ".js";
|
|
88
125
|
const outdir = esbuild?.outdir || (outfilePath ? dirname(outfilePath) : undefined);
|
|
89
|
-
const format = esbuild?.format ??
|
|
126
|
+
const format = esbuild?.format ??
|
|
127
|
+
(outdir?.includes("/cjs") || outExt === ".cjs" ? "cjs" : "esm");
|
|
90
128
|
results.push(await build({
|
|
91
129
|
...this.getEsBuildDefaults(),
|
|
92
130
|
...esbuild,
|
|
@@ -94,7 +132,13 @@ export class BuildAction {
|
|
|
94
132
|
outdir,
|
|
95
133
|
plugins: [
|
|
96
134
|
...pnpPlugins,
|
|
97
|
-
gjsifyPlugin({
|
|
135
|
+
gjsifyPlugin({
|
|
136
|
+
debug: verbose,
|
|
137
|
+
library: format,
|
|
138
|
+
exclude,
|
|
139
|
+
reflection: typescript?.reflection,
|
|
140
|
+
jsExtension: outExt,
|
|
141
|
+
}),
|
|
98
142
|
],
|
|
99
143
|
}));
|
|
100
144
|
}
|
|
@@ -111,12 +155,15 @@ export class BuildAction {
|
|
|
111
155
|
*/
|
|
112
156
|
parseGlobalsValue(value) {
|
|
113
157
|
if (value === undefined)
|
|
114
|
-
return { autoMode: true, extras:
|
|
115
|
-
if (value ===
|
|
116
|
-
return { autoMode: false, extras:
|
|
117
|
-
const tokens = value
|
|
118
|
-
|
|
119
|
-
|
|
158
|
+
return { autoMode: true, extras: "" };
|
|
159
|
+
if (value === "none" || value === "")
|
|
160
|
+
return { autoMode: false, extras: "" };
|
|
161
|
+
const tokens = value
|
|
162
|
+
.split(",")
|
|
163
|
+
.map((t) => t.trim())
|
|
164
|
+
.filter(Boolean);
|
|
165
|
+
const hasAuto = tokens.includes("auto");
|
|
166
|
+
const extras = tokens.filter((t) => t !== "auto").join(",");
|
|
120
167
|
return { autoMode: hasAuto, extras };
|
|
121
168
|
}
|
|
122
169
|
/**
|
|
@@ -128,7 +175,7 @@ export class BuildAction {
|
|
|
128
175
|
* The auto path is handled in `buildApp` via the two-pass build.
|
|
129
176
|
*/
|
|
130
177
|
async resolveGlobalsInject(app, globals, verbose) {
|
|
131
|
-
if (app !==
|
|
178
|
+
if (app !== "gjs")
|
|
132
179
|
return undefined;
|
|
133
180
|
if (!globals)
|
|
134
181
|
return undefined;
|
|
@@ -148,11 +195,11 @@ export class BuildAction {
|
|
|
148
195
|
async applyShebang(outfile, verbose) {
|
|
149
196
|
if (!outfile) {
|
|
150
197
|
if (verbose)
|
|
151
|
-
console.warn(
|
|
198
|
+
console.warn("[gjsify] --shebang skipped: no single outfile (use --outfile for GJS executables)");
|
|
152
199
|
return;
|
|
153
200
|
}
|
|
154
|
-
const content = await readFile(outfile,
|
|
155
|
-
if (content.startsWith(
|
|
201
|
+
const content = await readFile(outfile, "utf-8");
|
|
202
|
+
if (content.startsWith("#!")) {
|
|
156
203
|
if (verbose)
|
|
157
204
|
console.debug(`[gjsify] --shebang skipped: ${outfile} already starts with a shebang`);
|
|
158
205
|
}
|
|
@@ -165,11 +212,18 @@ export class BuildAction {
|
|
|
165
212
|
}
|
|
166
213
|
/** Application mode */
|
|
167
214
|
async buildApp(app = "gjs") {
|
|
168
|
-
const { verbose, esbuild, typescript, exclude, library: pgk, aliases, excludeGlobals } = this.configData;
|
|
169
|
-
const format = esbuild?.format ??
|
|
215
|
+
const { verbose, esbuild, typescript, exclude, library: pgk, aliases, excludeGlobals, } = this.configData;
|
|
216
|
+
const format = esbuild?.format ??
|
|
217
|
+
(esbuild?.outfile?.endsWith(".cjs") ? "cjs" : "esm");
|
|
170
218
|
// Set default outfile if no outdir is set
|
|
171
|
-
if (esbuild &&
|
|
172
|
-
esbuild
|
|
219
|
+
if (esbuild &&
|
|
220
|
+
!esbuild?.outfile &&
|
|
221
|
+
!esbuild?.outdir &&
|
|
222
|
+
(pgk?.main || pgk?.module)) {
|
|
223
|
+
esbuild.outfile =
|
|
224
|
+
esbuild?.format === "cjs"
|
|
225
|
+
? pgk.main || pgk.module
|
|
226
|
+
: pgk.module || pgk.main;
|
|
173
227
|
}
|
|
174
228
|
const { consoleShim, globals } = this.configData;
|
|
175
229
|
const pluginOpts = {
|
|
@@ -189,7 +243,12 @@ export class BuildAction {
|
|
|
189
243
|
// statically see a global (e.g. Excalibur indirects globalThis via
|
|
190
244
|
// BrowserComponent.nativeComponent). Common pattern: --globals auto,dom
|
|
191
245
|
if (app === "gjs" && autoMode) {
|
|
192
|
-
const { injectPath } = await detectAutoGlobals({
|
|
246
|
+
const { injectPath } = await detectAutoGlobals({
|
|
247
|
+
...this.getEsBuildDefaults(),
|
|
248
|
+
...esbuild,
|
|
249
|
+
format,
|
|
250
|
+
plugins: pnpPlugins,
|
|
251
|
+
}, pluginOpts, verbose, { extraGlobalsList: extras, excludeGlobals });
|
|
193
252
|
const result = await build({
|
|
194
253
|
...this.getEsBuildDefaults(),
|
|
195
254
|
...esbuild,
|
|
@@ -208,7 +267,9 @@ export class BuildAction {
|
|
|
208
267
|
return [result];
|
|
209
268
|
}
|
|
210
269
|
// --- Explicit list (no `auto` token) or none mode ---
|
|
211
|
-
const autoGlobalsInject = extras
|
|
270
|
+
const autoGlobalsInject = extras
|
|
271
|
+
? await this.resolveGlobalsInject(app, extras, verbose)
|
|
272
|
+
: undefined;
|
|
212
273
|
const result = await build({
|
|
213
274
|
...this.getEsBuildDefaults(),
|
|
214
275
|
...esbuild,
|
|
@@ -226,7 +287,7 @@ export class BuildAction {
|
|
|
226
287
|
}
|
|
227
288
|
return [result];
|
|
228
289
|
}
|
|
229
|
-
async start(buildType = { app:
|
|
290
|
+
async start(buildType = { app: "gjs" }) {
|
|
230
291
|
const results = [];
|
|
231
292
|
if (buildType.library) {
|
|
232
293
|
results.push(...(await this.buildLibrary()));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "CLI for Gjsify",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -23,15 +23,15 @@
|
|
|
23
23
|
"cli"
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@gjsify/create-app": "^0.3.
|
|
27
|
-
"@gjsify/esbuild-plugin-gjsify": "^0.3.
|
|
28
|
-
"@gjsify/example-dom-canvas2d-fireworks": "^0.3.
|
|
29
|
-
"@gjsify/example-dom-excalibur-jelly-jumper": "^0.3.
|
|
30
|
-
"@gjsify/example-dom-three-geometry-teapot": "^0.3.
|
|
31
|
-
"@gjsify/example-dom-three-postprocessing-pixel": "^0.3.
|
|
32
|
-
"@gjsify/example-node-express-webserver": "^0.3.
|
|
33
|
-
"@gjsify/node-polyfills": "^0.3.
|
|
34
|
-
"@gjsify/web-polyfills": "^0.3.
|
|
26
|
+
"@gjsify/create-app": "^0.3.3",
|
|
27
|
+
"@gjsify/esbuild-plugin-gjsify": "^0.3.3",
|
|
28
|
+
"@gjsify/example-dom-canvas2d-fireworks": "^0.3.3",
|
|
29
|
+
"@gjsify/example-dom-excalibur-jelly-jumper": "^0.3.3",
|
|
30
|
+
"@gjsify/example-dom-three-geometry-teapot": "^0.3.3",
|
|
31
|
+
"@gjsify/example-dom-three-postprocessing-pixel": "^0.3.3",
|
|
32
|
+
"@gjsify/example-node-express-webserver": "^0.3.3",
|
|
33
|
+
"@gjsify/node-polyfills": "^0.3.3",
|
|
34
|
+
"@gjsify/web-polyfills": "^0.3.3",
|
|
35
35
|
"@yarnpkg/esbuild-plugin-pnp": "^3.0.0-rc.15",
|
|
36
36
|
"cosmiconfig": "^9.0.1",
|
|
37
37
|
"esbuild": "^0.28.0",
|
package/src/actions/build.ts
CHANGED
|
@@ -1,280 +1,380 @@
|
|
|
1
|
-
import type { ConfigData } from
|
|
2
|
-
import type { App } from
|
|
3
|
-
import { build, BuildOptions, BuildResult, Plugin } from
|
|
4
|
-
import { gjsifyPlugin } from
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import type { ConfigData } from "../types/index.js";
|
|
2
|
+
import type { App } from "@gjsify/esbuild-plugin-gjsify";
|
|
3
|
+
import { build, BuildOptions, BuildResult, Plugin } from "esbuild";
|
|
4
|
+
import { gjsifyPlugin } from "@gjsify/esbuild-plugin-gjsify";
|
|
5
|
+
import {
|
|
6
|
+
resolveGlobalsList,
|
|
7
|
+
writeRegisterInjectFile,
|
|
8
|
+
detectAutoGlobals,
|
|
9
|
+
} from "@gjsify/esbuild-plugin-gjsify/globals";
|
|
10
|
+
import { dirname, extname, join } from "node:path";
|
|
11
|
+
import { chmod, readFile, writeFile } from "node:fs/promises";
|
|
12
|
+
import { existsSync } from "node:fs";
|
|
9
13
|
|
|
10
14
|
const GJS_SHEBANG = "#!/usr/bin/env -S gjs -m\n";
|
|
11
15
|
|
|
12
16
|
/** Walk up from dir until .pnp.cjs is found; return its directory or null. */
|
|
13
17
|
function findPnpRoot(dir: string): string | null {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
let current = dir;
|
|
19
|
+
while (true) {
|
|
20
|
+
if (existsSync(join(current, ".pnp.cjs"))) return current;
|
|
21
|
+
const parent = dirname(current);
|
|
22
|
+
if (parent === current) return null;
|
|
23
|
+
current = parent;
|
|
24
|
+
}
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
/**
|
|
24
28
|
* If the current project uses Yarn PnP, return the official
|
|
25
29
|
* @yarnpkg/esbuild-plugin-pnp plugin so esbuild can resolve
|
|
26
30
|
* modules from zip archives without manual extraction.
|
|
31
|
+
*
|
|
32
|
+
* Custom onResolve: fall through on UNDECLARED_DEPENDENCY errors so the
|
|
33
|
+
* gjsify alias plugin can handle bare specifiers (e.g. `abort-controller`)
|
|
34
|
+
* that PnP can't resolve from the inject file's issuer context but that
|
|
35
|
+
* gjsify maps to `@gjsify/*` packages the project DOES have available.
|
|
27
36
|
*/
|
|
28
37
|
async function getPnpPlugin(): Promise<Plugin | null> {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
38
|
+
if (!findPnpRoot(process.cwd())) return null;
|
|
39
|
+
try {
|
|
40
|
+
const { pnpPlugin } = await import("@yarnpkg/esbuild-plugin-pnp");
|
|
41
|
+
return pnpPlugin({
|
|
42
|
+
onResolve: async (_args, { resolvedPath, error, watchFiles }) => {
|
|
43
|
+
if (resolvedPath !== null) {
|
|
44
|
+
return { namespace: "pnp", path: resolvedPath, watchFiles };
|
|
45
|
+
}
|
|
46
|
+
// UNDECLARED_DEPENDENCY: package exists transitively but isn't
|
|
47
|
+
// in the issuer's direct deps. Fall through so the gjsify alias
|
|
48
|
+
// plugin can resolve it (e.g. bare → @gjsify/* mappings).
|
|
49
|
+
if (
|
|
50
|
+
(error as { pnpCode?: string } | null)?.pnpCode ===
|
|
51
|
+
"UNDECLARED_DEPENDENCY"
|
|
52
|
+
) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
external: true,
|
|
57
|
+
errors: error ? [{ text: error.message }] : [],
|
|
58
|
+
watchFiles,
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
} catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
36
65
|
}
|
|
37
66
|
|
|
38
67
|
export class BuildAction {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
68
|
+
constructor(readonly configData: ConfigData = {}) {}
|
|
69
|
+
|
|
70
|
+
getEsBuildDefaults() {
|
|
71
|
+
const defaults: BuildOptions = {
|
|
72
|
+
allowOverwrite: true,
|
|
73
|
+
};
|
|
74
|
+
return defaults;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Library mode */
|
|
78
|
+
async buildLibrary() {
|
|
79
|
+
let { verbose, library, esbuild, typescript, exclude } = this.configData;
|
|
80
|
+
library ||= {};
|
|
81
|
+
esbuild ||= {};
|
|
82
|
+
typescript ||= {};
|
|
83
|
+
|
|
84
|
+
const moduleOutdir = library?.module ? dirname(library.module) : undefined;
|
|
85
|
+
const mainOutdir = library?.main ? dirname(library.main) : undefined;
|
|
86
|
+
|
|
87
|
+
const moduleOutExt = library.module ? extname(library.module) : ".js";
|
|
88
|
+
const mainOutExt = library.main ? extname(library.main) : ".js";
|
|
89
|
+
|
|
90
|
+
const multipleBuilds =
|
|
91
|
+
moduleOutdir && mainOutdir && moduleOutdir !== mainOutdir;
|
|
92
|
+
|
|
93
|
+
const pnpPlugin = await getPnpPlugin();
|
|
94
|
+
const pnpPlugins: Plugin[] = pnpPlugin ? [pnpPlugin] : [];
|
|
95
|
+
|
|
96
|
+
const results: BuildResult[] = [];
|
|
97
|
+
|
|
98
|
+
if (multipleBuilds) {
|
|
99
|
+
const moduleFormat =
|
|
100
|
+
moduleOutdir.includes("/cjs") || moduleOutExt === ".cjs"
|
|
101
|
+
? "cjs"
|
|
102
|
+
: "esm";
|
|
103
|
+
results.push(
|
|
104
|
+
await build({
|
|
105
|
+
...this.getEsBuildDefaults(),
|
|
106
|
+
...esbuild,
|
|
107
|
+
format: moduleFormat,
|
|
108
|
+
outdir: moduleOutdir,
|
|
109
|
+
plugins: [
|
|
110
|
+
...pnpPlugins,
|
|
111
|
+
gjsifyPlugin({
|
|
112
|
+
debug: verbose,
|
|
113
|
+
library: moduleFormat,
|
|
114
|
+
exclude,
|
|
115
|
+
reflection: typescript?.reflection,
|
|
116
|
+
jsExtension: moduleOutExt,
|
|
117
|
+
}),
|
|
118
|
+
],
|
|
119
|
+
}),
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const mainFormat =
|
|
123
|
+
mainOutdir.includes("/cjs") || mainOutExt === ".cjs" ? "cjs" : "esm";
|
|
124
|
+
results.push(
|
|
125
|
+
await build({
|
|
126
|
+
...this.getEsBuildDefaults(),
|
|
127
|
+
...esbuild,
|
|
128
|
+
format: moduleFormat,
|
|
129
|
+
outdir: mainOutdir,
|
|
130
|
+
plugins: [
|
|
131
|
+
...pnpPlugins,
|
|
132
|
+
gjsifyPlugin({
|
|
133
|
+
debug: verbose,
|
|
134
|
+
library: mainFormat,
|
|
135
|
+
exclude,
|
|
136
|
+
reflection: typescript?.reflection,
|
|
137
|
+
jsExtension: mainOutdir,
|
|
138
|
+
}),
|
|
139
|
+
],
|
|
140
|
+
}),
|
|
141
|
+
);
|
|
142
|
+
} else {
|
|
143
|
+
const outfilePath = esbuild?.outfile || library?.module || library?.main;
|
|
144
|
+
const outExt = outfilePath ? extname(outfilePath) : ".js";
|
|
145
|
+
const outdir =
|
|
146
|
+
esbuild?.outdir || (outfilePath ? dirname(outfilePath) : undefined);
|
|
147
|
+
const format: "esm" | "cjs" =
|
|
148
|
+
(esbuild?.format as "esm" | "cjs") ??
|
|
149
|
+
(outdir?.includes("/cjs") || outExt === ".cjs" ? "cjs" : "esm");
|
|
150
|
+
results.push(
|
|
151
|
+
await build({
|
|
152
|
+
...this.getEsBuildDefaults(),
|
|
153
|
+
...esbuild,
|
|
154
|
+
format,
|
|
155
|
+
outdir,
|
|
156
|
+
plugins: [
|
|
157
|
+
...pnpPlugins,
|
|
158
|
+
gjsifyPlugin({
|
|
159
|
+
debug: verbose,
|
|
160
|
+
library: format,
|
|
161
|
+
exclude,
|
|
162
|
+
reflection: typescript?.reflection,
|
|
163
|
+
jsExtension: outExt,
|
|
164
|
+
}),
|
|
165
|
+
],
|
|
166
|
+
}),
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
return results;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Parse the `--globals` value into { autoMode, extras }.
|
|
174
|
+
* - `auto` → { autoMode: true, extras: '' }
|
|
175
|
+
* - `auto,dom` → { autoMode: true, extras: 'dom' }
|
|
176
|
+
* - `auto,dom,fetch` → { autoMode: true, extras: 'dom,fetch' }
|
|
177
|
+
* - `dom,fetch` → { autoMode: false, extras: 'dom,fetch' }
|
|
178
|
+
* - `none` / `` → { autoMode: false, extras: '' }
|
|
179
|
+
* - `undefined` → { autoMode: true, extras: '' } (default)
|
|
180
|
+
*/
|
|
181
|
+
private parseGlobalsValue(value: string | undefined): {
|
|
182
|
+
autoMode: boolean;
|
|
183
|
+
extras: string;
|
|
184
|
+
} {
|
|
185
|
+
if (value === undefined) return { autoMode: true, extras: "" };
|
|
186
|
+
if (value === "none" || value === "")
|
|
187
|
+
return { autoMode: false, extras: "" };
|
|
188
|
+
|
|
189
|
+
const tokens = value
|
|
190
|
+
.split(",")
|
|
191
|
+
.map((t) => t.trim())
|
|
192
|
+
.filter(Boolean);
|
|
193
|
+
const hasAuto = tokens.includes("auto");
|
|
194
|
+
const extras = tokens.filter((t) => t !== "auto").join(",");
|
|
195
|
+
|
|
196
|
+
return { autoMode: hasAuto, extras };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Resolve the `--globals` CLI list into a pre-computed inject stub path
|
|
201
|
+
* that the esbuild plugin will append to its `inject` list. Only runs
|
|
202
|
+
* for `--app gjs` — Node and browser builds rely on native globals.
|
|
203
|
+
*
|
|
204
|
+
* Used only for the explicit-only path (no `auto` token in the value).
|
|
205
|
+
* The auto path is handled in `buildApp` via the two-pass build.
|
|
206
|
+
*/
|
|
207
|
+
private async resolveGlobalsInject(
|
|
208
|
+
app: App,
|
|
209
|
+
globals: string,
|
|
210
|
+
verbose: boolean | undefined,
|
|
211
|
+
): Promise<string | undefined> {
|
|
212
|
+
if (app !== "gjs") return undefined;
|
|
213
|
+
if (!globals) return undefined;
|
|
214
|
+
|
|
215
|
+
const registerPaths = resolveGlobalsList(globals);
|
|
216
|
+
if (registerPaths.size === 0) return undefined;
|
|
217
|
+
|
|
218
|
+
const injectPath = await writeRegisterInjectFile(
|
|
219
|
+
registerPaths,
|
|
220
|
+
process.cwd(),
|
|
221
|
+
);
|
|
222
|
+
if (verbose && injectPath) {
|
|
223
|
+
console.debug(
|
|
224
|
+
`[gjsify] globals: injected ${registerPaths.size} register module(s) from --globals ${globals}`,
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
return injectPath ?? undefined;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Post-processing: prepend GJS shebang and mark the output file executable.
|
|
232
|
+
* Only runs for GJS app builds with a resolvable single outfile.
|
|
233
|
+
*/
|
|
234
|
+
private async applyShebang(
|
|
235
|
+
outfile: string | undefined,
|
|
236
|
+
verbose: boolean | undefined,
|
|
237
|
+
): Promise<void> {
|
|
238
|
+
if (!outfile) {
|
|
239
|
+
if (verbose)
|
|
240
|
+
console.warn(
|
|
241
|
+
"[gjsify] --shebang skipped: no single outfile (use --outfile for GJS executables)",
|
|
242
|
+
);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const content = await readFile(outfile, "utf-8");
|
|
247
|
+
if (content.startsWith("#!")) {
|
|
248
|
+
if (verbose)
|
|
249
|
+
console.debug(
|
|
250
|
+
`[gjsify] --shebang skipped: ${outfile} already starts with a shebang`,
|
|
251
|
+
);
|
|
252
|
+
} else {
|
|
253
|
+
await writeFile(outfile, GJS_SHEBANG + content);
|
|
254
|
+
}
|
|
255
|
+
await chmod(outfile, 0o755);
|
|
256
|
+
if (verbose)
|
|
257
|
+
console.debug(
|
|
258
|
+
`[gjsify] --shebang: wrote shebang + chmod 0o755 to ${outfile}`,
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** Application mode */
|
|
263
|
+
async buildApp(app: App = "gjs") {
|
|
264
|
+
const {
|
|
265
|
+
verbose,
|
|
266
|
+
esbuild,
|
|
267
|
+
typescript,
|
|
268
|
+
exclude,
|
|
269
|
+
library: pgk,
|
|
270
|
+
aliases,
|
|
271
|
+
excludeGlobals,
|
|
272
|
+
} = this.configData;
|
|
273
|
+
|
|
274
|
+
const format: "esm" | "cjs" =
|
|
275
|
+
(esbuild?.format as "esm" | "cjs") ??
|
|
276
|
+
(esbuild?.outfile?.endsWith(".cjs") ? "cjs" : "esm");
|
|
277
|
+
|
|
278
|
+
// Set default outfile if no outdir is set
|
|
279
|
+
if (
|
|
280
|
+
esbuild &&
|
|
281
|
+
!esbuild?.outfile &&
|
|
282
|
+
!esbuild?.outdir &&
|
|
283
|
+
(pgk?.main || pgk?.module)
|
|
284
|
+
) {
|
|
285
|
+
esbuild.outfile =
|
|
286
|
+
esbuild?.format === "cjs"
|
|
287
|
+
? pgk.main || pgk.module
|
|
288
|
+
: pgk.module || pgk.main;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const { consoleShim, globals } = this.configData;
|
|
292
|
+
|
|
293
|
+
const pluginOpts = {
|
|
294
|
+
debug: verbose,
|
|
295
|
+
app,
|
|
296
|
+
format,
|
|
297
|
+
exclude,
|
|
298
|
+
reflection: typescript?.reflection,
|
|
299
|
+
consoleShim,
|
|
300
|
+
...(aliases ? { aliases } : {}),
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const { autoMode, extras } = this.parseGlobalsValue(globals);
|
|
304
|
+
|
|
305
|
+
const pnpPlugin = await getPnpPlugin();
|
|
306
|
+
const pnpPlugins: Plugin[] = pnpPlugin ? [pnpPlugin] : [];
|
|
307
|
+
|
|
308
|
+
// --- Auto mode (with optional extras): iterative multi-pass build ---
|
|
309
|
+
// The extras token is used for cases where the detector cannot
|
|
310
|
+
// statically see a global (e.g. Excalibur indirects globalThis via
|
|
311
|
+
// BrowserComponent.nativeComponent). Common pattern: --globals auto,dom
|
|
312
|
+
if (app === "gjs" && autoMode) {
|
|
313
|
+
const { injectPath } = await detectAutoGlobals(
|
|
314
|
+
{
|
|
315
|
+
...this.getEsBuildDefaults(),
|
|
316
|
+
...esbuild,
|
|
317
|
+
format,
|
|
318
|
+
plugins: pnpPlugins,
|
|
319
|
+
},
|
|
320
|
+
pluginOpts,
|
|
321
|
+
verbose,
|
|
322
|
+
{ extraGlobalsList: extras, excludeGlobals },
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
const result = await build({
|
|
326
|
+
...this.getEsBuildDefaults(),
|
|
327
|
+
...esbuild,
|
|
328
|
+
format,
|
|
329
|
+
plugins: [
|
|
330
|
+
...pnpPlugins,
|
|
331
|
+
gjsifyPlugin({
|
|
332
|
+
...pluginOpts,
|
|
333
|
+
autoGlobalsInject: injectPath,
|
|
334
|
+
}),
|
|
335
|
+
],
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
if (app === "gjs" && this.configData.shebang) {
|
|
339
|
+
await this.applyShebang(esbuild?.outfile, verbose);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return [result];
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// --- Explicit list (no `auto` token) or none mode ---
|
|
346
|
+
const autoGlobalsInject = extras
|
|
347
|
+
? await this.resolveGlobalsInject(app, extras, verbose)
|
|
348
|
+
: undefined;
|
|
349
|
+
|
|
350
|
+
const result = await build({
|
|
351
|
+
...this.getEsBuildDefaults(),
|
|
352
|
+
...esbuild,
|
|
353
|
+
format,
|
|
354
|
+
plugins: [
|
|
355
|
+
...pnpPlugins,
|
|
356
|
+
gjsifyPlugin({
|
|
357
|
+
...pluginOpts,
|
|
358
|
+
autoGlobalsInject,
|
|
359
|
+
}),
|
|
360
|
+
],
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
if (app === "gjs" && this.configData.shebang) {
|
|
364
|
+
await this.applyShebang(esbuild?.outfile, verbose);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return [result];
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async start(buildType: { library?: boolean; app?: App } = { app: "gjs" }) {
|
|
371
|
+
const results: BuildResult[] = [];
|
|
372
|
+
if (buildType.library) {
|
|
373
|
+
results.push(...(await this.buildLibrary()));
|
|
374
|
+
} else {
|
|
375
|
+
results.push(...(await this.buildApp(buildType.app)));
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return results;
|
|
379
|
+
}
|
|
380
|
+
}
|