@b9g/shovel 0.2.7 → 0.2.9
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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Shovel will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.2.8] - 2026-02-10
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- **Config file watching during develop** - `shovel.json` and `package.json` changes are now correctly detected during `shovel develop`. The previous `fs.watch()` approach broke on atomic saves (most editors write to a temp file then rename). Now uses esbuild's native `watchFiles` mechanism which handles this correctly. ([#59](https://github.com/bikeshaving/shovel/issues/59))
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
- **Dev server keyboard shortcuts** - `Ctrl+R` (reload), `Ctrl+L` (clear), `Ctrl+C` (quit), `?` (help) shortcuts in the dev server terminal. Only active when stdin is a TTY. ([#60](https://github.com/bikeshaving/shovel/issues/60))
|
|
14
|
+
|
|
5
15
|
## [0.2.7] - 2026-02-06
|
|
6
16
|
|
|
7
17
|
### Features
|
package/bin/cli.js
CHANGED
|
@@ -75,7 +75,7 @@ program.command("develop <entrypoint>").description("Start development server wi
|
|
|
75
75
|
DEFAULTS.WORKERS
|
|
76
76
|
).option("--platform <name>", "Runtime platform (node, cloudflare, bun)").action(async (entrypoint, options) => {
|
|
77
77
|
checkPlatformReexec(options);
|
|
78
|
-
const { developCommand } = await import("../src/_chunks/develop-
|
|
78
|
+
const { developCommand } = await import("../src/_chunks/develop-N265AMEN.js");
|
|
79
79
|
await developCommand(entrypoint, options, config);
|
|
80
80
|
});
|
|
81
81
|
program.command("create [name]").description("Create a new Shovel project").action(async (name) => {
|
|
@@ -91,7 +91,7 @@ program.command("build <entrypoint>").description("Build app for production").op
|
|
|
91
91
|
"Run ServiceWorker lifecycle after build (install or activate, default: activate)"
|
|
92
92
|
).action(async (entrypoint, options) => {
|
|
93
93
|
checkPlatformReexec(options);
|
|
94
|
-
const { buildCommand } = await import("../src/_chunks/build-
|
|
94
|
+
const { buildCommand } = await import("../src/_chunks/build-O5LLUOND.js");
|
|
95
95
|
await buildCommand(entrypoint, options, config);
|
|
96
96
|
process.exit(0);
|
|
97
97
|
});
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@b9g/shovel",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
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
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "https://github.com/bikeshaving/shovel.git"
|
|
8
|
+
"url": "git+https://github.com/bikeshaving/shovel.git"
|
|
9
9
|
},
|
|
10
10
|
"bin": {
|
|
11
11
|
"shovel": "bin/cli.js",
|
|
@@ -16,17 +16,17 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@b9g/async-context": "^0.2.1",
|
|
18
18
|
"@b9g/cache": "^0.2.2",
|
|
19
|
-
"@b9g/filesystem": "^0.
|
|
19
|
+
"@b9g/filesystem": "^0.2.0",
|
|
20
20
|
"@b9g/http-errors": "^0.2.1",
|
|
21
21
|
"@b9g/node-webworker": "^0.2.1",
|
|
22
22
|
"@b9g/platform": "^0.1.17",
|
|
23
|
-
"@b9g/platform-bun": "^0.1.
|
|
23
|
+
"@b9g/platform-bun": "^0.1.16",
|
|
24
24
|
"@b9g/platform-cloudflare": "^0.1.15",
|
|
25
25
|
"@b9g/platform-node": "^0.1.17",
|
|
26
26
|
"@clack/prompts": "^0.7.0",
|
|
27
27
|
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
|
|
28
28
|
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
|
|
29
|
-
"@logtape/logtape": "^
|
|
29
|
+
"@logtape/logtape": "^2.0.0",
|
|
30
30
|
"commander": "^13.1.0",
|
|
31
31
|
"esbuild": "^0.27.2",
|
|
32
32
|
"mime": "^4.0.4",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@b9g/crank": "^0.7.2",
|
|
38
38
|
"@b9g/libuild": "^0.1.22",
|
|
39
39
|
"@b9g/router": "^0.2.2",
|
|
40
|
-
"@logtape/file": "^
|
|
40
|
+
"@logtape/file": "^2.0.0",
|
|
41
41
|
"@types/bun": "^1.3.4",
|
|
42
42
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
43
43
|
"@typescript-eslint/parser": "^8.0.0",
|
|
@@ -448,10 +448,15 @@ function createConfigPlugin(projectRoot, outDir = "dist", options = {}) {
|
|
|
448
448
|
const typesPath = join2(serverOutDir, "shovel.d.ts");
|
|
449
449
|
writeFileSync2(typesPath, typesCode);
|
|
450
450
|
}
|
|
451
|
+
const watchFiles = [
|
|
452
|
+
join2(projectRoot, "shovel.json"),
|
|
453
|
+
join2(projectRoot, "package.json")
|
|
454
|
+
];
|
|
451
455
|
return {
|
|
452
456
|
contents: configModuleCode,
|
|
453
457
|
loader: "js",
|
|
454
|
-
resolveDir: projectRoot
|
|
458
|
+
resolveDir: projectRoot,
|
|
459
|
+
watchFiles
|
|
455
460
|
};
|
|
456
461
|
});
|
|
457
462
|
}
|
|
@@ -720,7 +725,6 @@ var ServerBundler = class {
|
|
|
720
725
|
#initialBuildComplete;
|
|
721
726
|
#initialBuildResolve;
|
|
722
727
|
#currentOutputs;
|
|
723
|
-
#configWatchers;
|
|
724
728
|
#dirWatchers;
|
|
725
729
|
#userEntryPath;
|
|
726
730
|
#watchOptions;
|
|
@@ -732,7 +736,6 @@ var ServerBundler = class {
|
|
|
732
736
|
this.#projectRoot = findProjectRoot();
|
|
733
737
|
this.#initialBuildComplete = false;
|
|
734
738
|
this.#currentOutputs = { worker: "" };
|
|
735
|
-
this.#configWatchers = [];
|
|
736
739
|
this.#dirWatchers = /* @__PURE__ */ new Map();
|
|
737
740
|
this.#userEntryPath = "";
|
|
738
741
|
this.#changedFiles = /* @__PURE__ */ new Set();
|
|
@@ -798,9 +801,18 @@ var ServerBundler = class {
|
|
|
798
801
|
this.#ctx = await ESBuild2.context(buildOptions);
|
|
799
802
|
logger3.debug("Starting esbuild watch mode");
|
|
800
803
|
await this.#ctx.watch();
|
|
801
|
-
this.#watchConfigFiles();
|
|
802
804
|
return initialBuildPromise;
|
|
803
805
|
}
|
|
806
|
+
/**
|
|
807
|
+
* Trigger an immediate rebuild.
|
|
808
|
+
* Only works in watch mode (after calling watch()).
|
|
809
|
+
*/
|
|
810
|
+
async rebuild() {
|
|
811
|
+
if (!this.#ctx) {
|
|
812
|
+
throw new Error("Cannot rebuild: bundler is not in watch mode");
|
|
813
|
+
}
|
|
814
|
+
await this.#ctx.rebuild();
|
|
815
|
+
}
|
|
804
816
|
/**
|
|
805
817
|
* Stop watching and dispose of resources.
|
|
806
818
|
*/
|
|
@@ -809,10 +821,6 @@ var ServerBundler = class {
|
|
|
809
821
|
clearTimeout(this.#rebuildTimeout);
|
|
810
822
|
this.#rebuildTimeout = void 0;
|
|
811
823
|
}
|
|
812
|
-
for (const watcher of this.#configWatchers) {
|
|
813
|
-
watcher.close();
|
|
814
|
-
}
|
|
815
|
-
this.#configWatchers = [];
|
|
816
824
|
for (const entry of this.#dirWatchers.values()) {
|
|
817
825
|
entry.watcher.close();
|
|
818
826
|
}
|
|
@@ -1123,30 +1131,6 @@ These modules are not bundled and won't be available at runtime.`
|
|
|
1123
1131
|
}
|
|
1124
1132
|
}
|
|
1125
1133
|
}
|
|
1126
|
-
/**
|
|
1127
|
-
* Watch config files for changes.
|
|
1128
|
-
*/
|
|
1129
|
-
#watchConfigFiles() {
|
|
1130
|
-
const configFiles = ["shovel.json", "package.json"];
|
|
1131
|
-
for (const filename of configFiles) {
|
|
1132
|
-
const filepath = join5(this.#projectRoot, filename);
|
|
1133
|
-
if (!existsSync4(filepath))
|
|
1134
|
-
continue;
|
|
1135
|
-
try {
|
|
1136
|
-
const watcher = watch(filepath, { persistent: false }, (event) => {
|
|
1137
|
-
if (event === "change") {
|
|
1138
|
-
this.#scheduleRebuild(filepath);
|
|
1139
|
-
}
|
|
1140
|
-
});
|
|
1141
|
-
this.#configWatchers.push(watcher);
|
|
1142
|
-
} catch (err) {
|
|
1143
|
-
logger3.warn("Failed to watch {file}: {error}", {
|
|
1144
|
-
file: filename,
|
|
1145
|
-
error: err
|
|
1146
|
-
});
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
1134
|
/**
|
|
1151
1135
|
* Update source file watchers from metafile.
|
|
1152
1136
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ServerBundler,
|
|
3
3
|
loadPlatformModule
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-DQDUKJQ4.js";
|
|
5
5
|
import {
|
|
6
6
|
DEFAULTS
|
|
7
7
|
} from "./chunk-7GONPLNW.js";
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import { getLogger } from "@logtape/logtape";
|
|
11
11
|
import { resolvePlatform } from "@b9g/platform";
|
|
12
12
|
import { networkInterfaces } from "os";
|
|
13
|
+
import { exec } from "child_process";
|
|
13
14
|
var logger = getLogger(["shovel", "develop"]);
|
|
14
15
|
function getDisplayUrls(host, port) {
|
|
15
16
|
const urls = {
|
|
@@ -56,6 +57,8 @@ async function developCommand(entrypoint, options, config) {
|
|
|
56
57
|
const platformModule = await loadPlatformModule(platformName);
|
|
57
58
|
const platformESBuildConfig = platformModule.getESBuildConfig();
|
|
58
59
|
let devServer = null;
|
|
60
|
+
const localUrl = `http://localhost:${port}`;
|
|
61
|
+
const SHORTCUTS_HELP = "Ctrl+R (reload) Ctrl+O (open) Ctrl+C (quit) ? (help)";
|
|
59
62
|
const startOrReloadServer = async (workerPath) => {
|
|
60
63
|
if (!devServer) {
|
|
61
64
|
devServer = await platformModule.createDevServer({
|
|
@@ -73,6 +76,9 @@ async function developCommand(entrypoint, options, config) {
|
|
|
73
76
|
} else {
|
|
74
77
|
logger.info("Server running at {url}", { url: urls.local });
|
|
75
78
|
}
|
|
79
|
+
if (process.stdin.isTTY) {
|
|
80
|
+
logger.info(SHORTCUTS_HELP);
|
|
81
|
+
}
|
|
76
82
|
} else {
|
|
77
83
|
await devServer.reload(workerPath);
|
|
78
84
|
}
|
|
@@ -109,6 +115,56 @@ async function developCommand(entrypoint, options, config) {
|
|
|
109
115
|
};
|
|
110
116
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
111
117
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
118
|
+
if (process.stdin.isTTY) {
|
|
119
|
+
process.stdin.setRawMode(true);
|
|
120
|
+
process.stdin.resume();
|
|
121
|
+
process.stdin.setEncoding("utf8");
|
|
122
|
+
process.on("SIGCONT", () => {
|
|
123
|
+
process.stdin.setRawMode(true);
|
|
124
|
+
process.stdin.resume();
|
|
125
|
+
});
|
|
126
|
+
process.stdin.on("data", async (key) => {
|
|
127
|
+
switch (key) {
|
|
128
|
+
case "":
|
|
129
|
+
logger.info("Manual reload...");
|
|
130
|
+
await bundler.rebuild();
|
|
131
|
+
break;
|
|
132
|
+
case "\f":
|
|
133
|
+
console.clear();
|
|
134
|
+
break;
|
|
135
|
+
case "":
|
|
136
|
+
await shutdown("SIGINT");
|
|
137
|
+
break;
|
|
138
|
+
case "":
|
|
139
|
+
process.stdin.setRawMode(false);
|
|
140
|
+
process.kill(process.pid, "SIGTSTP");
|
|
141
|
+
break;
|
|
142
|
+
case "":
|
|
143
|
+
await shutdown("SIGQUIT");
|
|
144
|
+
break;
|
|
145
|
+
case "":
|
|
146
|
+
await shutdown("SIGINT");
|
|
147
|
+
break;
|
|
148
|
+
case "": {
|
|
149
|
+
const cmd = process.platform === "win32" ? "start" : process.platform === "darwin" ? "open" : "xdg-open";
|
|
150
|
+
exec(`${cmd} ${localUrl}`);
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
case "\r":
|
|
154
|
+
process.stdout.write("\n");
|
|
155
|
+
break;
|
|
156
|
+
case "\x7F":
|
|
157
|
+
process.stdout.write("\b \b");
|
|
158
|
+
break;
|
|
159
|
+
case "?":
|
|
160
|
+
logger.info(SHORTCUTS_HELP);
|
|
161
|
+
break;
|
|
162
|
+
default:
|
|
163
|
+
process.stdout.write(key);
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
112
168
|
await new Promise(() => {
|
|
113
169
|
});
|
|
114
170
|
} catch (error) {
|