@b9g/shovel 0.2.0-beta.6 → 0.2.0-beta.8
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 +1 -1
- package/bin/cli.js +208 -85
- package/package.json +19 -19
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ Shovel implements web platform APIs that server-side JavaScript is missing:
|
|
|
29
29
|
|-----|----------|--------------|
|
|
30
30
|
| `fetch` event | [Service Workers](https://w3c.github.io/ServiceWorker/) | Request handling |
|
|
31
31
|
| `self.caches` | [Cache API](https://w3c.github.io/ServiceWorker/#cache-interface) | Response caching |
|
|
32
|
-
| `self.buckets` | [
|
|
32
|
+
| `self.buckets` | [FileSystem API](https://fs.spec.whatwg.org/) | Storage (local, S3, R2) |
|
|
33
33
|
| `self.cookieStore` | [Cookie Store API](https://wicg.github.io/cookie-store/) | Cookie management |
|
|
34
34
|
| `URLPattern` | [URLPattern](https://urlpattern.spec.whatwg.org/) | Route matching (100% WPT) |
|
|
35
35
|
| `AsyncContext.Variable` | [TC39 Stage 2](https://github.com/tc39/proposal-async-context) | Request-scoped state |
|
package/bin/cli.js
CHANGED
|
@@ -20,12 +20,13 @@ var DEFAULTS = {
|
|
|
20
20
|
import { configure, getConsoleSink, getLogger as getLogger2 } from "@logtape/logtape";
|
|
21
21
|
import { AsyncContext } from "@b9g/async-context";
|
|
22
22
|
import * as Platform from "@b9g/platform";
|
|
23
|
+
import { loadConfig } from "@b9g/platform/config";
|
|
23
24
|
|
|
24
25
|
// src/esbuild/watcher.ts
|
|
25
26
|
import * as ESBuild from "esbuild";
|
|
26
|
-
import { existsSync } from "fs";
|
|
27
|
-
import { resolve, join, dirname as
|
|
28
|
-
import { mkdir
|
|
27
|
+
import { existsSync as existsSync2 } from "fs";
|
|
28
|
+
import { resolve, join as join2, dirname as dirname3 } from "path";
|
|
29
|
+
import { mkdir } from "fs/promises";
|
|
29
30
|
import { assetsPlugin } from "@b9g/assets/plugin";
|
|
30
31
|
|
|
31
32
|
// src/esbuild/import-meta-plugin.ts
|
|
@@ -77,16 +78,129 @@ function importMetaPlugin() {
|
|
|
77
78
|
};
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
// src/esbuild/jsx-config.ts
|
|
82
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
83
|
+
import { join, dirname as dirname2 } from "path";
|
|
84
|
+
import { existsSync } from "fs";
|
|
85
|
+
var CRANK_JSX_DEFAULTS = {
|
|
86
|
+
jsx: "automatic",
|
|
87
|
+
jsxImportSource: "@b9g/crank"
|
|
88
|
+
};
|
|
89
|
+
async function findTsConfig(startDir) {
|
|
90
|
+
let dir = startDir;
|
|
91
|
+
while (dir !== dirname2(dir)) {
|
|
92
|
+
const tsconfigPath = join(dir, "tsconfig.json");
|
|
93
|
+
if (existsSync(tsconfigPath)) {
|
|
94
|
+
return tsconfigPath;
|
|
95
|
+
}
|
|
96
|
+
dir = dirname2(dir);
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
async function parseTsConfig(tsconfigPath) {
|
|
101
|
+
const content = await readFile2(tsconfigPath, "utf8");
|
|
102
|
+
const stripped = content.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
103
|
+
const config = JSON.parse(stripped);
|
|
104
|
+
if (config.extends) {
|
|
105
|
+
const baseDir = dirname2(tsconfigPath);
|
|
106
|
+
let extendsPath = config.extends;
|
|
107
|
+
if (extendsPath.startsWith(".")) {
|
|
108
|
+
extendsPath = join(baseDir, extendsPath);
|
|
109
|
+
} else {
|
|
110
|
+
extendsPath = join(baseDir, "node_modules", extendsPath);
|
|
111
|
+
}
|
|
112
|
+
if (!extendsPath.endsWith(".json")) {
|
|
113
|
+
extendsPath += ".json";
|
|
114
|
+
}
|
|
115
|
+
if (existsSync(extendsPath)) {
|
|
116
|
+
const baseConfig = await parseTsConfig(extendsPath);
|
|
117
|
+
return {
|
|
118
|
+
...baseConfig,
|
|
119
|
+
...config,
|
|
120
|
+
compilerOptions: {
|
|
121
|
+
...baseConfig.compilerOptions,
|
|
122
|
+
...config.compilerOptions
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return config;
|
|
128
|
+
}
|
|
129
|
+
function mapTsConfigToEsbuild(compilerOptions) {
|
|
130
|
+
const options = {};
|
|
131
|
+
if (compilerOptions.jsx) {
|
|
132
|
+
switch (compilerOptions.jsx) {
|
|
133
|
+
case "react":
|
|
134
|
+
case "react-native":
|
|
135
|
+
options.jsx = "transform";
|
|
136
|
+
break;
|
|
137
|
+
case "react-jsx":
|
|
138
|
+
case "react-jsxdev":
|
|
139
|
+
options.jsx = "automatic";
|
|
140
|
+
break;
|
|
141
|
+
case "preserve":
|
|
142
|
+
options.jsx = "preserve";
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (compilerOptions.jsxFactory) {
|
|
147
|
+
options.jsxFactory = compilerOptions.jsxFactory;
|
|
148
|
+
}
|
|
149
|
+
if (compilerOptions.jsxFragmentFactory) {
|
|
150
|
+
options.jsxFragment = compilerOptions.jsxFragmentFactory;
|
|
151
|
+
}
|
|
152
|
+
if (compilerOptions.jsxImportSource) {
|
|
153
|
+
options.jsxImportSource = compilerOptions.jsxImportSource;
|
|
154
|
+
}
|
|
155
|
+
return options;
|
|
156
|
+
}
|
|
157
|
+
async function loadJSXConfig(projectRoot) {
|
|
158
|
+
const tsconfigPath = await findTsConfig(projectRoot);
|
|
159
|
+
if (tsconfigPath) {
|
|
160
|
+
try {
|
|
161
|
+
const config = await parseTsConfig(tsconfigPath);
|
|
162
|
+
const compilerOptions = config.compilerOptions || {};
|
|
163
|
+
const hasJsxConfig = compilerOptions.jsx || compilerOptions.jsxFactory || compilerOptions.jsxFragmentFactory || compilerOptions.jsxImportSource;
|
|
164
|
+
if (hasJsxConfig) {
|
|
165
|
+
const tsOptions = mapTsConfigToEsbuild(compilerOptions);
|
|
166
|
+
return {
|
|
167
|
+
...CRANK_JSX_DEFAULTS,
|
|
168
|
+
...tsOptions
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
} catch {
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return { ...CRANK_JSX_DEFAULTS };
|
|
175
|
+
}
|
|
176
|
+
function applyJSXOptions(buildOptions, jsxOptions) {
|
|
177
|
+
if (jsxOptions.jsx) {
|
|
178
|
+
buildOptions.jsx = jsxOptions.jsx;
|
|
179
|
+
}
|
|
180
|
+
if (jsxOptions.jsxFactory) {
|
|
181
|
+
buildOptions.jsxFactory = jsxOptions.jsxFactory;
|
|
182
|
+
}
|
|
183
|
+
if (jsxOptions.jsxFragment) {
|
|
184
|
+
buildOptions.jsxFragment = jsxOptions.jsxFragment;
|
|
185
|
+
}
|
|
186
|
+
if (jsxOptions.jsxImportSource) {
|
|
187
|
+
buildOptions.jsxImportSource = jsxOptions.jsxImportSource;
|
|
188
|
+
}
|
|
189
|
+
if (jsxOptions.jsxSideEffects !== void 0) {
|
|
190
|
+
buildOptions.jsxSideEffects = jsxOptions.jsxSideEffects;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
80
194
|
// src/esbuild/watcher.ts
|
|
81
195
|
import { getLogger } from "@logtape/logtape";
|
|
82
196
|
var logger = getLogger(["watcher"]);
|
|
83
197
|
function findProjectRoot() {
|
|
84
198
|
let dir = process.cwd();
|
|
85
|
-
while (dir !==
|
|
86
|
-
if (
|
|
199
|
+
while (dir !== dirname3(dir)) {
|
|
200
|
+
if (existsSync2(join2(dir, "package.json"))) {
|
|
87
201
|
return dir;
|
|
88
202
|
}
|
|
89
|
-
dir =
|
|
203
|
+
dir = dirname3(dir);
|
|
90
204
|
}
|
|
91
205
|
return process.cwd();
|
|
92
206
|
}
|
|
@@ -97,13 +211,11 @@ var Watcher = class {
|
|
|
97
211
|
#initialBuildComplete;
|
|
98
212
|
#initialBuildResolve;
|
|
99
213
|
#currentEntrypoint;
|
|
100
|
-
#previousEntrypoint;
|
|
101
214
|
constructor(options) {
|
|
102
215
|
this.#options = options;
|
|
103
216
|
this.#projectRoot = findProjectRoot();
|
|
104
217
|
this.#initialBuildComplete = false;
|
|
105
218
|
this.#currentEntrypoint = "";
|
|
106
|
-
this.#previousEntrypoint = "";
|
|
107
219
|
}
|
|
108
220
|
/**
|
|
109
221
|
* Start watching and building
|
|
@@ -112,12 +224,13 @@ var Watcher = class {
|
|
|
112
224
|
async start() {
|
|
113
225
|
const entryPath = resolve(this.#projectRoot, this.#options.entrypoint);
|
|
114
226
|
const outputDir = resolve(this.#projectRoot, this.#options.outDir);
|
|
115
|
-
await mkdir(
|
|
116
|
-
await mkdir(
|
|
227
|
+
await mkdir(join2(outputDir, "server"), { recursive: true });
|
|
228
|
+
await mkdir(join2(outputDir, "static"), { recursive: true });
|
|
229
|
+
const jsxOptions = await loadJSXConfig(this.#projectRoot);
|
|
117
230
|
const initialBuildPromise = new Promise((resolve3) => {
|
|
118
231
|
this.#initialBuildResolve = resolve3;
|
|
119
232
|
});
|
|
120
|
-
|
|
233
|
+
const buildOptions = {
|
|
121
234
|
entryPoints: [entryPath],
|
|
122
235
|
bundle: true,
|
|
123
236
|
format: "esm",
|
|
@@ -130,7 +243,13 @@ var Watcher = class {
|
|
|
130
243
|
plugins: [
|
|
131
244
|
importMetaPlugin(),
|
|
132
245
|
assetsPlugin({
|
|
133
|
-
outDir: outputDir
|
|
246
|
+
outDir: outputDir,
|
|
247
|
+
clientBuild: {
|
|
248
|
+
jsx: jsxOptions.jsx,
|
|
249
|
+
jsxFactory: jsxOptions.jsxFactory,
|
|
250
|
+
jsxFragment: jsxOptions.jsxFragment,
|
|
251
|
+
jsxImportSource: jsxOptions.jsxImportSource
|
|
252
|
+
}
|
|
134
253
|
}),
|
|
135
254
|
// Plugin to detect build completion (works with watch mode)
|
|
136
255
|
{
|
|
@@ -153,29 +272,15 @@ var Watcher = class {
|
|
|
153
272
|
}
|
|
154
273
|
if (success) {
|
|
155
274
|
logger.info("Build complete", { entrypoint: outputPath });
|
|
156
|
-
if (this.#currentEntrypoint && this.#currentEntrypoint !== outputPath) {
|
|
157
|
-
try {
|
|
158
|
-
await unlink(this.#currentEntrypoint);
|
|
159
|
-
await unlink(this.#currentEntrypoint + ".map").catch(
|
|
160
|
-
() => {
|
|
161
|
-
}
|
|
162
|
-
);
|
|
163
|
-
logger.debug("Cleaned up old build", {
|
|
164
|
-
oldEntrypoint: this.#currentEntrypoint
|
|
165
|
-
});
|
|
166
|
-
} catch {
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
275
|
} else {
|
|
170
|
-
logger.error("Build errors", { errors: result.errors });
|
|
276
|
+
logger.error("Build errors: {errors}", { errors: result.errors });
|
|
171
277
|
}
|
|
172
|
-
this.#previousEntrypoint = this.#currentEntrypoint;
|
|
173
278
|
this.#currentEntrypoint = outputPath;
|
|
174
279
|
if (!this.#initialBuildComplete) {
|
|
175
280
|
this.#initialBuildComplete = true;
|
|
176
281
|
this.#initialBuildResolve?.({ success, entrypoint: outputPath });
|
|
177
282
|
} else {
|
|
178
|
-
this.#options.onBuild?.(success, outputPath);
|
|
283
|
+
await this.#options.onBuild?.(success, outputPath);
|
|
179
284
|
}
|
|
180
285
|
});
|
|
181
286
|
}
|
|
@@ -184,7 +289,9 @@ var Watcher = class {
|
|
|
184
289
|
sourcemap: "inline",
|
|
185
290
|
minify: false,
|
|
186
291
|
treeShaking: true
|
|
187
|
-
}
|
|
292
|
+
};
|
|
293
|
+
applyJSXOptions(buildOptions, jsxOptions);
|
|
294
|
+
this.#ctx = await ESBuild.context(buildOptions);
|
|
188
295
|
logger.info("Starting esbuild watch mode");
|
|
189
296
|
await this.#ctx.watch();
|
|
190
297
|
return initialBuildPromise;
|
|
@@ -201,7 +308,6 @@ var Watcher = class {
|
|
|
201
308
|
};
|
|
202
309
|
|
|
203
310
|
// src/commands/develop.ts
|
|
204
|
-
var logger2 = getLogger2(["cli"]);
|
|
205
311
|
await configure({
|
|
206
312
|
contextLocalStorage: new AsyncContext.Variable(),
|
|
207
313
|
sinks: {
|
|
@@ -220,10 +326,12 @@ await configure({
|
|
|
220
326
|
{ category: ["worker"], level: "debug", sinks: ["console"] }
|
|
221
327
|
]
|
|
222
328
|
});
|
|
329
|
+
var logger2 = getLogger2(["cli"]);
|
|
223
330
|
async function developCommand(entrypoint, options) {
|
|
224
331
|
try {
|
|
225
|
-
const
|
|
226
|
-
const
|
|
332
|
+
const config = loadConfig(process.cwd());
|
|
333
|
+
const platformName = Platform.resolvePlatform({ ...options, config });
|
|
334
|
+
const workerCount = getWorkerCount(options, config);
|
|
227
335
|
if (options.verbose) {
|
|
228
336
|
Platform.displayPlatformInfo(platformName);
|
|
229
337
|
logger2.info("Worker configuration", { workerCount });
|
|
@@ -289,13 +397,16 @@ async function developCommand(entrypoint, options) {
|
|
|
289
397
|
process.exit(1);
|
|
290
398
|
}
|
|
291
399
|
}
|
|
292
|
-
function getWorkerCount(options) {
|
|
400
|
+
function getWorkerCount(options, config) {
|
|
293
401
|
if (options.workers) {
|
|
294
402
|
return parseInt(options.workers);
|
|
295
403
|
}
|
|
296
404
|
if (process.env.WORKER_COUNT) {
|
|
297
405
|
return parseInt(process.env.WORKER_COUNT);
|
|
298
406
|
}
|
|
407
|
+
if (config?.workers) {
|
|
408
|
+
return config.workers;
|
|
409
|
+
}
|
|
299
410
|
return DEFAULTS.WORKERS;
|
|
300
411
|
}
|
|
301
412
|
|
|
@@ -330,10 +441,7 @@ async function activateCommand(entrypoint, options) {
|
|
|
330
441
|
await serviceWorker.dispose();
|
|
331
442
|
await platformInstance.dispose();
|
|
332
443
|
} catch (error) {
|
|
333
|
-
logger3.error("ServiceWorker activation failed", { error
|
|
334
|
-
if (options.verbose) {
|
|
335
|
-
logger3.error("Stack trace", { stack: error.stack });
|
|
336
|
-
}
|
|
444
|
+
logger3.error("ServiceWorker activation failed: {error}", { error });
|
|
337
445
|
process.exit(1);
|
|
338
446
|
}
|
|
339
447
|
}
|
|
@@ -360,15 +468,16 @@ async function infoCommand() {
|
|
|
360
468
|
|
|
361
469
|
// src/commands/build.ts
|
|
362
470
|
import * as ESBuild2 from "esbuild";
|
|
363
|
-
import { resolve as resolve2, join as
|
|
364
|
-
import { mkdir as mkdir2, readFile as
|
|
471
|
+
import { resolve as resolve2, join as join3, dirname as dirname4 } from "path";
|
|
472
|
+
import { mkdir as mkdir2, readFile as readFile3, writeFile } from "fs/promises";
|
|
365
473
|
import { fileURLToPath } from "url";
|
|
366
474
|
import { assetsPlugin as assetsPlugin2 } from "@b9g/assets/plugin";
|
|
367
475
|
import { configure as configure2, getConsoleSink as getConsoleSink2, getLogger as getLogger5 } from "@logtape/logtape";
|
|
368
476
|
import { AsyncContext as AsyncContext2 } from "@b9g/async-context";
|
|
477
|
+
import * as Platform3 from "@b9g/platform";
|
|
478
|
+
import { loadConfig as loadConfig2 } from "@b9g/platform/config";
|
|
369
479
|
await configure2({
|
|
370
480
|
reset: true,
|
|
371
|
-
// Allow reconfiguration if already configured
|
|
372
481
|
contextLocalStorage: new AsyncContext2.Variable(),
|
|
373
482
|
sinks: {
|
|
374
483
|
console: getConsoleSink2()
|
|
@@ -418,7 +527,7 @@ async function buildForProduction({
|
|
|
418
527
|
if (verbose) {
|
|
419
528
|
logger5.info("Built app to", { outputDir: buildContext.outputDir });
|
|
420
529
|
logger5.info("Server files", { dir: buildContext.serverDir });
|
|
421
|
-
logger5.info("Static files", { dir:
|
|
530
|
+
logger5.info("Static files", { dir: join3(buildContext.outputDir, "static") });
|
|
422
531
|
}
|
|
423
532
|
}
|
|
424
533
|
async function initializeBuild({
|
|
@@ -442,7 +551,7 @@ async function initializeBuild({
|
|
|
442
551
|
const entryPath = resolve2(entrypoint);
|
|
443
552
|
const outputDir = resolve2(outDir);
|
|
444
553
|
try {
|
|
445
|
-
const stats = await
|
|
554
|
+
const stats = await readFile3(entryPath, "utf8");
|
|
446
555
|
if (stats.length === 0) {
|
|
447
556
|
logger5.warn("Entry point is empty", { entryPath });
|
|
448
557
|
}
|
|
@@ -464,8 +573,8 @@ async function initializeBuild({
|
|
|
464
573
|
}
|
|
465
574
|
try {
|
|
466
575
|
await mkdir2(outputDir, { recursive: true });
|
|
467
|
-
await mkdir2(
|
|
468
|
-
await mkdir2(
|
|
576
|
+
await mkdir2(join3(outputDir, BUILD_STRUCTURE.serverDir), { recursive: true });
|
|
577
|
+
await mkdir2(join3(outputDir, BUILD_STRUCTURE.staticDir), { recursive: true });
|
|
469
578
|
} catch (error) {
|
|
470
579
|
throw new Error(
|
|
471
580
|
`Failed to create output directory structure: ${error.message}`
|
|
@@ -474,7 +583,7 @@ async function initializeBuild({
|
|
|
474
583
|
return {
|
|
475
584
|
entryPath,
|
|
476
585
|
outputDir,
|
|
477
|
-
serverDir:
|
|
586
|
+
serverDir: join3(outputDir, BUILD_STRUCTURE.serverDir),
|
|
478
587
|
workspaceRoot,
|
|
479
588
|
platform,
|
|
480
589
|
verbose,
|
|
@@ -483,37 +592,37 @@ async function initializeBuild({
|
|
|
483
592
|
}
|
|
484
593
|
async function findWorkspaceRoot() {
|
|
485
594
|
let workspaceRoot = process.cwd();
|
|
486
|
-
while (workspaceRoot !==
|
|
595
|
+
while (workspaceRoot !== dirname4(workspaceRoot)) {
|
|
487
596
|
try {
|
|
488
597
|
const packageJSON = JSON.parse(
|
|
489
|
-
await
|
|
598
|
+
await readFile3(resolve2(workspaceRoot, "package.json"), "utf8")
|
|
490
599
|
);
|
|
491
600
|
if (packageJSON.workspaces) {
|
|
492
601
|
return workspaceRoot;
|
|
493
602
|
}
|
|
494
603
|
} catch {
|
|
495
604
|
}
|
|
496
|
-
workspaceRoot =
|
|
605
|
+
workspaceRoot = dirname4(workspaceRoot);
|
|
497
606
|
}
|
|
498
607
|
return workspaceRoot;
|
|
499
608
|
}
|
|
500
609
|
async function findShovelPackageRoot() {
|
|
501
|
-
let currentDir =
|
|
610
|
+
let currentDir = dirname4(fileURLToPath(import.meta.url));
|
|
502
611
|
let packageRoot = currentDir;
|
|
503
|
-
while (packageRoot !==
|
|
612
|
+
while (packageRoot !== dirname4(packageRoot)) {
|
|
504
613
|
try {
|
|
505
|
-
const packageJSONPath =
|
|
506
|
-
const content = await
|
|
614
|
+
const packageJSONPath = join3(packageRoot, "package.json");
|
|
615
|
+
const content = await readFile3(packageJSONPath, "utf8");
|
|
507
616
|
const pkg2 = JSON.parse(content);
|
|
508
617
|
if (pkg2.name === "@b9g/shovel" || pkg2.name === "shovel") {
|
|
509
618
|
if (packageRoot.endsWith("/dist") || packageRoot.endsWith("\\dist")) {
|
|
510
|
-
return
|
|
619
|
+
return dirname4(packageRoot);
|
|
511
620
|
}
|
|
512
621
|
return packageRoot;
|
|
513
622
|
}
|
|
514
623
|
} catch {
|
|
515
624
|
}
|
|
516
|
-
packageRoot =
|
|
625
|
+
packageRoot = dirname4(packageRoot);
|
|
517
626
|
}
|
|
518
627
|
return currentDir;
|
|
519
628
|
}
|
|
@@ -526,6 +635,7 @@ async function createBuildConfig({
|
|
|
526
635
|
workerCount
|
|
527
636
|
}) {
|
|
528
637
|
const isCloudflare = platform === "cloudflare" || platform === "cloudflare-workers";
|
|
638
|
+
const jsxOptions = await loadJSXConfig(workspaceRoot || dirname4(entryPath));
|
|
529
639
|
try {
|
|
530
640
|
const virtualEntry = await createVirtualEntry(
|
|
531
641
|
entryPath,
|
|
@@ -541,19 +651,25 @@ async function createBuildConfig({
|
|
|
541
651
|
format: BUILD_DEFAULTS.format,
|
|
542
652
|
target: BUILD_DEFAULTS.target,
|
|
543
653
|
platform: "node",
|
|
544
|
-
outfile:
|
|
545
|
-
absWorkingDir: workspaceRoot ||
|
|
654
|
+
outfile: join3(serverDir, "server.js"),
|
|
655
|
+
absWorkingDir: workspaceRoot || dirname4(entryPath),
|
|
546
656
|
mainFields: ["module", "main"],
|
|
547
657
|
conditions: ["import", "module"],
|
|
548
658
|
// Allow user code to import @b9g packages from shovel's packages directory
|
|
549
659
|
nodePaths: [
|
|
550
|
-
|
|
551
|
-
|
|
660
|
+
join3(shovelRoot, "packages"),
|
|
661
|
+
join3(shovelRoot, "node_modules")
|
|
552
662
|
],
|
|
553
663
|
plugins: [
|
|
554
664
|
importMetaPlugin(),
|
|
555
665
|
assetsPlugin2({
|
|
556
|
-
outDir: outputDir
|
|
666
|
+
outDir: outputDir,
|
|
667
|
+
clientBuild: {
|
|
668
|
+
jsx: jsxOptions.jsx,
|
|
669
|
+
jsxFactory: jsxOptions.jsxFactory,
|
|
670
|
+
jsxFragment: jsxOptions.jsxFragment,
|
|
671
|
+
jsxImportSource: jsxOptions.jsxImportSource
|
|
672
|
+
}
|
|
557
673
|
})
|
|
558
674
|
],
|
|
559
675
|
metafile: true,
|
|
@@ -565,12 +681,13 @@ async function createBuildConfig({
|
|
|
565
681
|
define: platform === "node" ? { "import.meta.env": "process.env" } : {},
|
|
566
682
|
external
|
|
567
683
|
};
|
|
684
|
+
applyJSXOptions(userBuildConfig, jsxOptions);
|
|
568
685
|
await ESBuild2.build(userBuildConfig);
|
|
569
|
-
const runtimeSourcePath =
|
|
686
|
+
const runtimeSourcePath = join3(
|
|
570
687
|
shovelRoot,
|
|
571
688
|
"packages/platform/dist/src/runtime.js"
|
|
572
689
|
);
|
|
573
|
-
const workerDestPath =
|
|
690
|
+
const workerDestPath = join3(serverDir, "worker.js");
|
|
574
691
|
try {
|
|
575
692
|
await ESBuild2.build({
|
|
576
693
|
entryPoints: [runtimeSourcePath],
|
|
@@ -582,7 +699,7 @@ async function createBuildConfig({
|
|
|
582
699
|
external: ["node:*"]
|
|
583
700
|
});
|
|
584
701
|
} catch (error) {
|
|
585
|
-
const installedRuntimePath =
|
|
702
|
+
const installedRuntimePath = join3(
|
|
586
703
|
shovelRoot,
|
|
587
704
|
"node_modules/@b9g/platform/dist/src/runtime.js"
|
|
588
705
|
);
|
|
@@ -610,17 +727,23 @@ async function createBuildConfig({
|
|
|
610
727
|
platform: isCloudflare ? "browser" : "node",
|
|
611
728
|
// Cloudflare: single-file architecture (server.js contains everything)
|
|
612
729
|
// Node/Bun: multi-file architecture (index.js is entry, server.js is user code)
|
|
613
|
-
outfile:
|
|
730
|
+
outfile: join3(
|
|
614
731
|
serverDir,
|
|
615
732
|
isCloudflare ? "server.js" : BUILD_DEFAULTS.outputFile
|
|
616
733
|
),
|
|
617
|
-
absWorkingDir: workspaceRoot ||
|
|
734
|
+
absWorkingDir: workspaceRoot || dirname4(entryPath),
|
|
618
735
|
mainFields: ["module", "main"],
|
|
619
736
|
conditions: ["import", "module"],
|
|
620
737
|
plugins: isCloudflare ? [
|
|
621
738
|
importMetaPlugin(),
|
|
622
739
|
assetsPlugin2({
|
|
623
|
-
outDir: outputDir
|
|
740
|
+
outDir: outputDir,
|
|
741
|
+
clientBuild: {
|
|
742
|
+
jsx: jsxOptions.jsx,
|
|
743
|
+
jsxFactory: jsxOptions.jsxFactory,
|
|
744
|
+
jsxFragment: jsxOptions.jsxFragment,
|
|
745
|
+
jsxImportSource: jsxOptions.jsxImportSource
|
|
746
|
+
}
|
|
624
747
|
})
|
|
625
748
|
] : [],
|
|
626
749
|
// Assets already handled in user code build
|
|
@@ -634,6 +757,7 @@ async function createBuildConfig({
|
|
|
634
757
|
external
|
|
635
758
|
};
|
|
636
759
|
if (isCloudflare) {
|
|
760
|
+
applyJSXOptions(buildConfig, jsxOptions);
|
|
637
761
|
await configureCloudflareTarget(buildConfig);
|
|
638
762
|
}
|
|
639
763
|
return buildConfig;
|
|
@@ -659,22 +783,22 @@ import "${userEntryPath}";
|
|
|
659
783
|
return await createWorkerEntry(userEntryPath, workerCount, platform);
|
|
660
784
|
}
|
|
661
785
|
async function createWorkerEntry(userEntryPath, workerCount, platform) {
|
|
662
|
-
let currentDir =
|
|
786
|
+
let currentDir = dirname4(fileURLToPath(import.meta.url));
|
|
663
787
|
let packageRoot = currentDir;
|
|
664
|
-
while (packageRoot !==
|
|
788
|
+
while (packageRoot !== dirname4(packageRoot)) {
|
|
665
789
|
try {
|
|
666
|
-
const packageJSONPath =
|
|
667
|
-
await
|
|
790
|
+
const packageJSONPath = join3(packageRoot, "package.json");
|
|
791
|
+
await readFile3(packageJSONPath, "utf8");
|
|
668
792
|
break;
|
|
669
793
|
} catch {
|
|
670
|
-
packageRoot =
|
|
794
|
+
packageRoot = dirname4(packageRoot);
|
|
671
795
|
}
|
|
672
796
|
}
|
|
673
|
-
let templatePath =
|
|
797
|
+
let templatePath = join3(packageRoot, "src/worker-entry.ts");
|
|
674
798
|
try {
|
|
675
|
-
await
|
|
799
|
+
await readFile3(templatePath, "utf8");
|
|
676
800
|
} catch {
|
|
677
|
-
templatePath =
|
|
801
|
+
templatePath = join3(packageRoot, "src/worker-entry.js");
|
|
678
802
|
}
|
|
679
803
|
const transpileResult = await ESBuild2.build({
|
|
680
804
|
entryPoints: [templatePath],
|
|
@@ -697,21 +821,21 @@ async function logBundleAnalysis(metafile) {
|
|
|
697
821
|
const analysis = await ESBuild2.analyzeMetafile(metafile);
|
|
698
822
|
logger5.info(analysis, {});
|
|
699
823
|
} catch (error) {
|
|
700
|
-
logger5.warn("Failed to analyze bundle", { error
|
|
824
|
+
logger5.warn("Failed to analyze bundle: {error}", { error });
|
|
701
825
|
}
|
|
702
826
|
}
|
|
703
827
|
async function generatePackageJSON({ serverDir, platform, verbose, entryPath }) {
|
|
704
|
-
const entryDir =
|
|
828
|
+
const entryDir = dirname4(entryPath);
|
|
705
829
|
const sourcePackageJsonPath = resolve2(entryDir, "package.json");
|
|
706
830
|
try {
|
|
707
|
-
const packageJSONContent = await
|
|
831
|
+
const packageJSONContent = await readFile3(sourcePackageJsonPath, "utf8");
|
|
708
832
|
try {
|
|
709
833
|
JSON.parse(packageJSONContent);
|
|
710
834
|
} catch (parseError) {
|
|
711
835
|
throw new Error(`Invalid package.json format: ${parseError.message}`);
|
|
712
836
|
}
|
|
713
837
|
await writeFile(
|
|
714
|
-
|
|
838
|
+
join3(serverDir, "package.json"),
|
|
715
839
|
packageJSONContent,
|
|
716
840
|
"utf8"
|
|
717
841
|
);
|
|
@@ -720,12 +844,12 @@ async function generatePackageJSON({ serverDir, platform, verbose, entryPath })
|
|
|
720
844
|
}
|
|
721
845
|
} catch (error) {
|
|
722
846
|
if (verbose) {
|
|
723
|
-
logger5.warn("Could not copy package.json", { error
|
|
847
|
+
logger5.warn("Could not copy package.json: {error}", { error });
|
|
724
848
|
}
|
|
725
849
|
try {
|
|
726
850
|
const generatedPackageJson = await generateExecutablePackageJSON(platform);
|
|
727
851
|
await writeFile(
|
|
728
|
-
|
|
852
|
+
join3(serverDir, "package.json"),
|
|
729
853
|
JSON.stringify(generatedPackageJson, null, 2),
|
|
730
854
|
"utf8"
|
|
731
855
|
);
|
|
@@ -737,10 +861,7 @@ async function generatePackageJSON({ serverDir, platform, verbose, entryPath })
|
|
|
737
861
|
}
|
|
738
862
|
} catch (generateError) {
|
|
739
863
|
if (verbose) {
|
|
740
|
-
logger5.warn("Could not generate package.json", {
|
|
741
|
-
error: generateError.message
|
|
742
|
-
});
|
|
743
|
-
logger5.warn("Generation error details", { error: generateError });
|
|
864
|
+
logger5.warn("Could not generate package.json: {error}", { error: generateError });
|
|
744
865
|
}
|
|
745
866
|
}
|
|
746
867
|
}
|
|
@@ -777,12 +898,14 @@ async function generateExecutablePackageJSON(platform) {
|
|
|
777
898
|
return packageJSON;
|
|
778
899
|
}
|
|
779
900
|
async function buildCommand(entrypoint, options) {
|
|
901
|
+
const config = loadConfig2(process.cwd());
|
|
902
|
+
const platform = Platform3.resolvePlatform({ ...options, config });
|
|
780
903
|
await buildForProduction({
|
|
781
904
|
entrypoint,
|
|
782
905
|
outDir: "dist",
|
|
783
906
|
verbose: options.verbose || false,
|
|
784
|
-
platform
|
|
785
|
-
workerCount: options.workers ? parseInt(options.workers, 10) :
|
|
907
|
+
platform,
|
|
908
|
+
workerCount: options.workers ? parseInt(options.workers, 10) : config.workers
|
|
786
909
|
});
|
|
787
910
|
process.exit(0);
|
|
788
911
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@b9g/shovel",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.8",
|
|
4
4
|
"description": "ServiceWorker-first universal deployment platform. Write ServiceWorker apps once, deploy anywhere (Node/Bun/Cloudflare). Registry-based multi-app orchestration.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"create": "bin/create.js"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@b9g/async-context": "^0.1.
|
|
13
|
+
"@b9g/async-context": "^0.1.2",
|
|
14
14
|
"@clack/prompts": "^0.7.0",
|
|
15
15
|
"@logtape/logtape": "^1.2.0",
|
|
16
16
|
"commander": "^13.1.0",
|
|
@@ -21,30 +21,30 @@
|
|
|
21
21
|
"source-map": "^0.7.4"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@b9g/assets": "^0.1.
|
|
25
|
-
"@b9g/cache": "^0.1.
|
|
24
|
+
"@b9g/assets": "^0.1.14",
|
|
25
|
+
"@b9g/cache": "^0.1.5",
|
|
26
26
|
"@b9g/crank": "^0.7.2",
|
|
27
|
-
"@b9g/filesystem": "^0.1.
|
|
28
|
-
"@b9g/http-errors": "^0.1.
|
|
27
|
+
"@b9g/filesystem": "^0.1.7",
|
|
28
|
+
"@b9g/http-errors": "^0.1.6",
|
|
29
29
|
"@b9g/libuild": "^0.1.17",
|
|
30
|
-
"@b9g/platform": "^0.1.
|
|
31
|
-
"@b9g/platform-bun": "^0.1.
|
|
32
|
-
"@b9g/platform-cloudflare": "^0.1.
|
|
33
|
-
"@b9g/platform-node": "^0.1.
|
|
34
|
-
"@b9g/router": "^0.1.
|
|
30
|
+
"@b9g/platform": "^0.1.11",
|
|
31
|
+
"@b9g/platform-bun": "^0.1.9",
|
|
32
|
+
"@b9g/platform-cloudflare": "^0.1.8",
|
|
33
|
+
"@b9g/platform-node": "^0.1.11",
|
|
34
|
+
"@b9g/router": "^0.1.9",
|
|
35
35
|
"@types/bun": "^1.2.2",
|
|
36
36
|
"mitata": "^1.0.34",
|
|
37
37
|
"typescript": "^5.7.3"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
|
-
"@b9g/node-webworker": "^0.1.
|
|
41
|
-
"@b9g/platform": "^0.1.
|
|
42
|
-
"@b9g/platform-node": "^0.1.
|
|
43
|
-
"@b9g/platform-cloudflare": "^0.1.
|
|
44
|
-
"@b9g/platform-bun": "^0.1.
|
|
45
|
-
"@b9g/cache": "^0.1.
|
|
46
|
-
"@b9g/filesystem": "^0.1.
|
|
47
|
-
"@b9g/http-errors": "^0.1.
|
|
40
|
+
"@b9g/node-webworker": "^0.1.4",
|
|
41
|
+
"@b9g/platform": "^0.1.11",
|
|
42
|
+
"@b9g/platform-node": "^0.1.11",
|
|
43
|
+
"@b9g/platform-cloudflare": "^0.1.8",
|
|
44
|
+
"@b9g/platform-bun": "^0.1.9",
|
|
45
|
+
"@b9g/cache": "^0.1.5",
|
|
46
|
+
"@b9g/filesystem": "^0.1.7",
|
|
47
|
+
"@b9g/http-errors": "^0.1.6"
|
|
48
48
|
},
|
|
49
49
|
"peerDependenciesMeta": {
|
|
50
50
|
"@b9g/platform": {
|