@b9g/shovel 0.2.0-beta.4 → 0.2.0-beta.6

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 (2) hide show
  1. package/bin/cli.js +64 -40
  2. package/package.json +15 -15
package/bin/cli.js CHANGED
@@ -23,9 +23,9 @@ import * as Platform from "@b9g/platform";
23
23
 
24
24
  // src/esbuild/watcher.ts
25
25
  import * as ESBuild from "esbuild";
26
- import { resolve, dirname as dirname2, join } from "path";
27
- import { readFileSync } from "fs";
28
- import { mkdir } from "fs/promises";
26
+ import { existsSync } from "fs";
27
+ import { resolve, join, dirname as dirname2 } from "path";
28
+ import { mkdir, unlink } from "fs/promises";
29
29
  import { assetsPlugin } from "@b9g/assets/plugin";
30
30
 
31
31
  // src/esbuild/import-meta-plugin.ts
@@ -80,23 +80,38 @@ function importMetaPlugin() {
80
80
  // src/esbuild/watcher.ts
81
81
  import { getLogger } from "@logtape/logtape";
82
82
  var logger = getLogger(["watcher"]);
83
+ function findProjectRoot() {
84
+ let dir = process.cwd();
85
+ while (dir !== dirname2(dir)) {
86
+ if (existsSync(join(dir, "package.json"))) {
87
+ return dir;
88
+ }
89
+ dir = dirname2(dir);
90
+ }
91
+ return process.cwd();
92
+ }
83
93
  var Watcher = class {
84
94
  #options;
85
95
  #ctx;
86
- #initialBuildComplete = false;
87
- #initialBuildSuccess = false;
96
+ #projectRoot;
97
+ #initialBuildComplete;
88
98
  #initialBuildResolve;
99
+ #currentEntrypoint;
100
+ #previousEntrypoint;
89
101
  constructor(options) {
90
102
  this.#options = options;
103
+ this.#projectRoot = findProjectRoot();
104
+ this.#initialBuildComplete = false;
105
+ this.#currentEntrypoint = "";
106
+ this.#previousEntrypoint = "";
91
107
  }
92
108
  /**
93
109
  * Start watching and building
94
- * @returns true if initial build succeeded, false if it failed
110
+ * @returns Result with success status and the hashed entrypoint path
95
111
  */
96
112
  async start() {
97
- const entryPath = resolve(this.#options.entrypoint);
98
- const outputDir = resolve(this.#options.outDir);
99
- const workspaceRoot = this.#findWorkspaceRoot();
113
+ const entryPath = resolve(this.#projectRoot, this.#options.entrypoint);
114
+ const outputDir = resolve(this.#projectRoot, this.#options.outDir);
100
115
  await mkdir(join(outputDir, "server"), { recursive: true });
101
116
  await mkdir(join(outputDir, "static"), { recursive: true });
102
117
  const initialBuildPromise = new Promise((resolve3) => {
@@ -108,9 +123,10 @@ var Watcher = class {
108
123
  format: "esm",
109
124
  target: "es2022",
110
125
  platform: "node",
111
- outfile: `${outputDir}/server/app.js`,
112
- // No packages: "external" - bundle everything for dev/prod parity
113
- absWorkingDir: workspaceRoot,
126
+ outdir: `${outputDir}/server`,
127
+ entryNames: "[name]-[hash]",
128
+ metafile: true,
129
+ absWorkingDir: this.#projectRoot,
114
130
  plugins: [
115
131
  importMetaPlugin(),
116
132
  assetsPlugin({
@@ -125,20 +141,41 @@ var Watcher = class {
125
141
  entrypoint: this.#options.entrypoint
126
142
  });
127
143
  });
128
- build2.onEnd((result) => {
129
- const version = Date.now();
144
+ build2.onEnd(async (result) => {
130
145
  const success = result.errors.length === 0;
146
+ let outputPath = "";
147
+ if (result.metafile) {
148
+ const outputs = Object.keys(result.metafile.outputs);
149
+ const jsOutput = outputs.find((p) => p.endsWith(".js"));
150
+ if (jsOutput) {
151
+ outputPath = resolve(this.#projectRoot, jsOutput);
152
+ }
153
+ }
131
154
  if (success) {
132
- logger.info("Build complete", { version });
155
+ 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
+ }
133
169
  } else {
134
170
  logger.error("Build errors", { errors: result.errors });
135
171
  }
172
+ this.#previousEntrypoint = this.#currentEntrypoint;
173
+ this.#currentEntrypoint = outputPath;
136
174
  if (!this.#initialBuildComplete) {
137
175
  this.#initialBuildComplete = true;
138
- this.#initialBuildSuccess = success;
139
- this.#initialBuildResolve?.(success);
176
+ this.#initialBuildResolve?.({ success, entrypoint: outputPath });
140
177
  } else {
141
- this.#options.onBuild?.(success, version);
178
+ this.#options.onBuild?.(success, outputPath);
142
179
  }
143
180
  });
144
181
  }
@@ -161,23 +198,6 @@ var Watcher = class {
161
198
  this.#ctx = void 0;
162
199
  }
163
200
  }
164
- #findWorkspaceRoot() {
165
- const initialCwd = process.cwd();
166
- let workspaceRoot = initialCwd;
167
- while (workspaceRoot !== dirname2(workspaceRoot)) {
168
- try {
169
- const packageJSON = JSON.parse(
170
- readFileSync(resolve(workspaceRoot, "package.json"), "utf8")
171
- );
172
- if (packageJSON.workspaces) {
173
- return workspaceRoot;
174
- }
175
- } catch {
176
- }
177
- workspaceRoot = dirname2(workspaceRoot);
178
- }
179
- return initialCwd;
180
- }
181
201
  };
182
202
 
183
203
  // src/commands/develop.ts
@@ -224,21 +244,20 @@ async function developCommand(entrypoint, options) {
224
244
  const watcher = new Watcher({
225
245
  entrypoint,
226
246
  outDir,
227
- onBuild: async (success, version) => {
247
+ onBuild: async (success, builtEntrypoint2) => {
228
248
  if (success && serviceWorker) {
229
- logger2.info("Reloading Workers", { version });
249
+ logger2.info("Reloading Workers", { entrypoint: builtEntrypoint2 });
230
250
  if (platformInstance && typeof platformInstance.reloadWorkers === "function") {
231
- await platformInstance.reloadWorkers(version);
251
+ await platformInstance.reloadWorkers(builtEntrypoint2);
232
252
  }
233
253
  logger2.info("Workers reloaded", {});
234
254
  }
235
255
  }
236
256
  });
237
- const buildSuccess = await watcher.start();
257
+ const { success: buildSuccess, entrypoint: builtEntrypoint } = await watcher.start();
238
258
  if (!buildSuccess) {
239
259
  logger2.error("Initial build failed, watching for changes to retry", {});
240
260
  }
241
- const builtEntrypoint = `${outDir}/server/app.js`;
242
261
  serviceWorker = await platformInstance.loadServiceWorker(builtEntrypoint, {
243
262
  hotReload: true,
244
263
  workerCount
@@ -526,6 +545,11 @@ async function createBuildConfig({
526
545
  absWorkingDir: workspaceRoot || dirname3(entryPath),
527
546
  mainFields: ["module", "main"],
528
547
  conditions: ["import", "module"],
548
+ // Allow user code to import @b9g packages from shovel's packages directory
549
+ nodePaths: [
550
+ join2(shovelRoot, "packages"),
551
+ join2(shovelRoot, "node_modules")
552
+ ],
529
553
  plugins: [
530
554
  importMetaPlugin(),
531
555
  assetsPlugin2({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b9g/shovel",
3
- "version": "0.2.0-beta.4",
3
+ "version": "0.2.0-beta.6",
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": {
@@ -21,30 +21,30 @@
21
21
  "source-map": "^0.7.4"
22
22
  },
23
23
  "devDependencies": {
24
- "@b9g/assets": "^0.1.9",
24
+ "@b9g/assets": "^0.1.13",
25
25
  "@b9g/cache": "^0.1.4",
26
26
  "@b9g/crank": "^0.7.2",
27
- "@b9g/filesystem": "^0.1.5",
28
- "@b9g/http-errors": "^0.1.4",
27
+ "@b9g/filesystem": "^0.1.6",
28
+ "@b9g/http-errors": "^0.1.5",
29
29
  "@b9g/libuild": "^0.1.17",
30
- "@b9g/platform": "^0.1.7",
31
- "@b9g/platform-bun": "^0.1.6",
32
- "@b9g/platform-cloudflare": "^0.1.5",
33
- "@b9g/platform-node": "^0.1.8",
34
- "@b9g/router": "^0.1.6",
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",
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
40
  "@b9g/node-webworker": "^0.1.3",
41
- "@b9g/platform": "^0.1.7",
42
- "@b9g/platform-node": "^0.1.8",
43
- "@b9g/platform-cloudflare": "^0.1.5",
44
- "@b9g/platform-bun": "^0.1.6",
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
45
  "@b9g/cache": "^0.1.4",
46
- "@b9g/filesystem": "^0.1.5",
47
- "@b9g/http-errors": "^0.1.4"
46
+ "@b9g/filesystem": "^0.1.6",
47
+ "@b9g/http-errors": "^0.1.5"
48
48
  },
49
49
  "peerDependenciesMeta": {
50
50
  "@b9g/platform": {