@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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/bin/cli.js +208 -85
  3. 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` | [FileSystemDirectoryHandle](https://fs.spec.whatwg.org/#api-filesystemdirectoryhandle) | Storage (local, S3, R2) |
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 dirname2 } from "path";
28
- import { mkdir, unlink } from "fs/promises";
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 !== dirname2(dir)) {
86
- if (existsSync(join(dir, "package.json"))) {
199
+ while (dir !== dirname3(dir)) {
200
+ if (existsSync2(join2(dir, "package.json"))) {
87
201
  return dir;
88
202
  }
89
- dir = dirname2(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(join(outputDir, "server"), { recursive: true });
116
- await mkdir(join(outputDir, "static"), { recursive: true });
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
- this.#ctx = await ESBuild.context({
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 platformName = Platform.resolvePlatform(options);
226
- const workerCount = getWorkerCount(options);
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: error.message });
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 join2, dirname as dirname3 } from "path";
364
- import { mkdir as mkdir2, readFile as readFile2, writeFile } from "fs/promises";
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: join2(buildContext.outputDir, "static") });
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 readFile2(entryPath, "utf8");
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(join2(outputDir, BUILD_STRUCTURE.serverDir), { recursive: true });
468
- await mkdir2(join2(outputDir, BUILD_STRUCTURE.staticDir), { recursive: true });
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: join2(outputDir, BUILD_STRUCTURE.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 !== dirname3(workspaceRoot)) {
595
+ while (workspaceRoot !== dirname4(workspaceRoot)) {
487
596
  try {
488
597
  const packageJSON = JSON.parse(
489
- await readFile2(resolve2(workspaceRoot, "package.json"), "utf8")
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 = dirname3(workspaceRoot);
605
+ workspaceRoot = dirname4(workspaceRoot);
497
606
  }
498
607
  return workspaceRoot;
499
608
  }
500
609
  async function findShovelPackageRoot() {
501
- let currentDir = dirname3(fileURLToPath(import.meta.url));
610
+ let currentDir = dirname4(fileURLToPath(import.meta.url));
502
611
  let packageRoot = currentDir;
503
- while (packageRoot !== dirname3(packageRoot)) {
612
+ while (packageRoot !== dirname4(packageRoot)) {
504
613
  try {
505
- const packageJSONPath = join2(packageRoot, "package.json");
506
- const content = await readFile2(packageJSONPath, "utf8");
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 dirname3(packageRoot);
619
+ return dirname4(packageRoot);
511
620
  }
512
621
  return packageRoot;
513
622
  }
514
623
  } catch {
515
624
  }
516
- packageRoot = dirname3(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: join2(serverDir, "server.js"),
545
- absWorkingDir: workspaceRoot || dirname3(entryPath),
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
- join2(shovelRoot, "packages"),
551
- join2(shovelRoot, "node_modules")
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 = join2(
686
+ const runtimeSourcePath = join3(
570
687
  shovelRoot,
571
688
  "packages/platform/dist/src/runtime.js"
572
689
  );
573
- const workerDestPath = join2(serverDir, "worker.js");
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 = join2(
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: join2(
730
+ outfile: join3(
614
731
  serverDir,
615
732
  isCloudflare ? "server.js" : BUILD_DEFAULTS.outputFile
616
733
  ),
617
- absWorkingDir: workspaceRoot || dirname3(entryPath),
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 = dirname3(fileURLToPath(import.meta.url));
786
+ let currentDir = dirname4(fileURLToPath(import.meta.url));
663
787
  let packageRoot = currentDir;
664
- while (packageRoot !== dirname3(packageRoot)) {
788
+ while (packageRoot !== dirname4(packageRoot)) {
665
789
  try {
666
- const packageJSONPath = join2(packageRoot, "package.json");
667
- await readFile2(packageJSONPath, "utf8");
790
+ const packageJSONPath = join3(packageRoot, "package.json");
791
+ await readFile3(packageJSONPath, "utf8");
668
792
  break;
669
793
  } catch {
670
- packageRoot = dirname3(packageRoot);
794
+ packageRoot = dirname4(packageRoot);
671
795
  }
672
796
  }
673
- let templatePath = join2(packageRoot, "src/worker-entry.ts");
797
+ let templatePath = join3(packageRoot, "src/worker-entry.ts");
674
798
  try {
675
- await readFile2(templatePath, "utf8");
799
+ await readFile3(templatePath, "utf8");
676
800
  } catch {
677
- templatePath = join2(packageRoot, "src/worker-entry.js");
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: error.message });
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 = dirname3(entryPath);
828
+ const entryDir = dirname4(entryPath);
705
829
  const sourcePackageJsonPath = resolve2(entryDir, "package.json");
706
830
  try {
707
- const packageJSONContent = await readFile2(sourcePackageJsonPath, "utf8");
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
- join2(serverDir, "package.json"),
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: error.message });
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
- join2(serverDir, "package.json"),
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: options.platform || "node",
785
- workerCount: options.workers ? parseInt(options.workers, 10) : 1
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.6",
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.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.13",
25
- "@b9g/cache": "^0.1.4",
24
+ "@b9g/assets": "^0.1.14",
25
+ "@b9g/cache": "^0.1.5",
26
26
  "@b9g/crank": "^0.7.2",
27
- "@b9g/filesystem": "^0.1.6",
28
- "@b9g/http-errors": "^0.1.5",
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.9",
31
- "@b9g/platform-bun": "^0.1.7",
32
- "@b9g/platform-cloudflare": "^0.1.6",
33
- "@b9g/platform-node": "^0.1.9",
34
- "@b9g/router": "^0.1.8",
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.3",
41
- "@b9g/platform": "^0.1.9",
42
- "@b9g/platform-node": "^0.1.9",
43
- "@b9g/platform-cloudflare": "^0.1.6",
44
- "@b9g/platform-bun": "^0.1.7",
45
- "@b9g/cache": "^0.1.4",
46
- "@b9g/filesystem": "^0.1.6",
47
- "@b9g/http-errors": "^0.1.5"
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": {