@b9g/shovel 0.2.0-beta.10 → 0.2.0-beta.11

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/chunk-ILQUUH2L.js DELETED
@@ -1,164 +0,0 @@
1
- // src/utils/import-meta-plugin.ts
2
- import { readFile } from "fs/promises";
3
- import { dirname } from "path";
4
- import { pathToFileURL } from "url";
5
- function importMetaPlugin() {
6
- return {
7
- name: "import-meta-transform",
8
- setup(build) {
9
- build.onLoad({ filter: /\.[jt]sx?$/, namespace: "file" }, async (args) => {
10
- if (args.path.includes("node_modules")) {
11
- return null;
12
- }
13
- const contents = await readFile(args.path, "utf8");
14
- if (!contents.includes("import.meta.url") && !contents.includes("import.meta.dirname") && !contents.includes("import.meta.filename")) {
15
- return null;
16
- }
17
- const fileUrl = pathToFileURL(args.path).href;
18
- const fileDirname = dirname(args.path);
19
- const fileFilename = args.path;
20
- let transformed = contents;
21
- transformed = transformed.replace(
22
- /\bimport\.meta\.url\b/g,
23
- JSON.stringify(fileUrl)
24
- );
25
- transformed = transformed.replace(
26
- /\bimport\.meta\.dirname\b/g,
27
- JSON.stringify(fileDirname)
28
- );
29
- transformed = transformed.replace(
30
- /\bimport\.meta\.filename\b/g,
31
- JSON.stringify(fileFilename)
32
- );
33
- const ext = args.path.split(".").pop();
34
- let loader = "js";
35
- if (ext === "ts")
36
- loader = "ts";
37
- else if (ext === "tsx")
38
- loader = "tsx";
39
- else if (ext === "jsx")
40
- loader = "jsx";
41
- return {
42
- contents: transformed,
43
- loader
44
- };
45
- });
46
- }
47
- };
48
- }
49
-
50
- // src/utils/jsx-config.ts
51
- import { readFile as readFile2 } from "fs/promises";
52
- import { join, dirname as dirname2 } from "path";
53
- import { existsSync } from "fs";
54
- var CRANK_JSX_DEFAULTS = {
55
- jsx: "automatic",
56
- jsxImportSource: "@b9g/crank"
57
- };
58
- async function findTsConfig(startDir) {
59
- let dir = startDir;
60
- while (dir !== dirname2(dir)) {
61
- const tsconfigPath = join(dir, "tsconfig.json");
62
- if (existsSync(tsconfigPath)) {
63
- return tsconfigPath;
64
- }
65
- dir = dirname2(dir);
66
- }
67
- return null;
68
- }
69
- async function parseTsConfig(tsconfigPath) {
70
- const content = await readFile2(tsconfigPath, "utf8");
71
- const stripped = content.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
72
- const config = JSON.parse(stripped);
73
- if (config.extends) {
74
- const baseDir = dirname2(tsconfigPath);
75
- let extendsPath = config.extends;
76
- if (extendsPath.startsWith(".")) {
77
- extendsPath = join(baseDir, extendsPath);
78
- } else {
79
- extendsPath = join(baseDir, "node_modules", extendsPath);
80
- }
81
- if (!extendsPath.endsWith(".json")) {
82
- extendsPath += ".json";
83
- }
84
- if (existsSync(extendsPath)) {
85
- const baseConfig = await parseTsConfig(extendsPath);
86
- return {
87
- ...baseConfig,
88
- ...config,
89
- compilerOptions: {
90
- ...baseConfig.compilerOptions,
91
- ...config.compilerOptions
92
- }
93
- };
94
- }
95
- }
96
- return config;
97
- }
98
- function mapTSConfigToESBuild(compilerOptions) {
99
- const options = {};
100
- if (compilerOptions.jsx) {
101
- switch (compilerOptions.jsx) {
102
- case "react":
103
- case "react-native":
104
- options.jsx = "transform";
105
- break;
106
- case "react-jsx":
107
- case "react-jsxdev":
108
- options.jsx = "automatic";
109
- break;
110
- case "preserve":
111
- options.jsx = "preserve";
112
- break;
113
- }
114
- }
115
- if (compilerOptions.jsxFactory) {
116
- options.jsxFactory = compilerOptions.jsxFactory;
117
- }
118
- if (compilerOptions.jsxFragmentFactory) {
119
- options.jsxFragment = compilerOptions.jsxFragmentFactory;
120
- }
121
- if (compilerOptions.jsxImportSource) {
122
- options.jsxImportSource = compilerOptions.jsxImportSource;
123
- }
124
- return options;
125
- }
126
- async function loadJSXConfig(projectRoot) {
127
- const tsconfigPath = await findTsConfig(projectRoot);
128
- if (tsconfigPath) {
129
- const config = await parseTsConfig(tsconfigPath);
130
- const compilerOptions = config.compilerOptions || {};
131
- const hasJSXConfig = compilerOptions.jsx || compilerOptions.jsxFactory || compilerOptions.jsxFragmentFactory || compilerOptions.jsxImportSource;
132
- if (hasJSXConfig) {
133
- const tsOptions = mapTSConfigToESBuild(compilerOptions);
134
- return {
135
- ...CRANK_JSX_DEFAULTS,
136
- ...tsOptions
137
- };
138
- }
139
- }
140
- return { ...CRANK_JSX_DEFAULTS };
141
- }
142
- function applyJSXOptions(buildOptions, jsxOptions) {
143
- if (jsxOptions.jsx) {
144
- buildOptions.jsx = jsxOptions.jsx;
145
- }
146
- if (jsxOptions.jsxFactory) {
147
- buildOptions.jsxFactory = jsxOptions.jsxFactory;
148
- }
149
- if (jsxOptions.jsxFragment) {
150
- buildOptions.jsxFragment = jsxOptions.jsxFragment;
151
- }
152
- if (jsxOptions.jsxImportSource) {
153
- buildOptions.jsxImportSource = jsxOptions.jsxImportSource;
154
- }
155
- if (jsxOptions.jsxSideEffects !== void 0) {
156
- buildOptions.jsxSideEffects = jsxOptions.jsxSideEffects;
157
- }
158
- }
159
-
160
- export {
161
- importMetaPlugin,
162
- loadJSXConfig,
163
- applyJSXOptions
164
- };
@@ -1,264 +0,0 @@
1
- import {
2
- applyJSXOptions,
3
- importMetaPlugin,
4
- loadJSXConfig
5
- } from "./chunk-ILQUUH2L.js";
6
- import {
7
- DEFAULTS,
8
- findProjectRoot,
9
- generateConfigModule,
10
- loadRawConfig
11
- } from "./chunk-CSH7M4MK.js";
12
-
13
- // src/commands/develop.ts
14
- import { getLogger as getLogger2 } from "@logtape/logtape";
15
- import * as Platform from "@b9g/platform";
16
-
17
- // src/utils/watcher.ts
18
- import * as ESBuild from "esbuild";
19
- import { resolve, join } from "path";
20
- import { mkdir } from "fs/promises";
21
- import { assetsPlugin } from "@b9g/assets/plugin";
22
- import { getLogger } from "@logtape/logtape";
23
- var logger = getLogger(["build"]);
24
- function createConfigPlugin(projectRoot) {
25
- const rawConfig = loadRawConfig(projectRoot);
26
- const configModuleCode = generateConfigModule(rawConfig);
27
- return {
28
- name: "shovel-config",
29
- setup(build2) {
30
- build2.onResolve({ filter: /^shovel:config$/ }, (args) => ({
31
- path: args.path,
32
- namespace: "shovel-config"
33
- }));
34
- build2.onLoad({ filter: /.*/, namespace: "shovel-config" }, () => ({
35
- contents: configModuleCode,
36
- loader: "js",
37
- resolveDir: projectRoot
38
- }));
39
- }
40
- };
41
- }
42
- async function buildWorker(projectRoot, outputDir) {
43
- const workerDestPath = join(outputDir, "server", "worker.js");
44
- const virtualWorkerEntry = `
45
- import {configureLogging} from "@b9g/platform/runtime";
46
- import {config} from "shovel:config";
47
- await configureLogging(config.logging);
48
-
49
- // Import the actual worker (runs its initialization code)
50
- import "@b9g/platform/worker";
51
- `;
52
- await ESBuild.build({
53
- stdin: {
54
- contents: virtualWorkerEntry,
55
- resolveDir: projectRoot,
56
- sourcefile: "virtual-worker-entry.js"
57
- },
58
- bundle: true,
59
- format: "esm",
60
- target: "es2022",
61
- platform: "node",
62
- outfile: workerDestPath,
63
- external: ["node:*"],
64
- plugins: [createConfigPlugin(projectRoot)]
65
- });
66
- logger.info("Built worker", { workerDestPath });
67
- }
68
- var Watcher = class {
69
- #options;
70
- #ctx;
71
- #projectRoot;
72
- #initialBuildComplete;
73
- #initialBuildResolve;
74
- #currentEntrypoint;
75
- constructor(options) {
76
- this.#options = options;
77
- this.#projectRoot = findProjectRoot();
78
- this.#initialBuildComplete = false;
79
- this.#currentEntrypoint = "";
80
- }
81
- /**
82
- * Start watching and building
83
- * @returns Result with success status and the hashed entrypoint path
84
- */
85
- async start() {
86
- const entryPath = resolve(this.#projectRoot, this.#options.entrypoint);
87
- const outputDir = resolve(this.#projectRoot, this.#options.outDir);
88
- await mkdir(join(outputDir, "server"), { recursive: true });
89
- await mkdir(join(outputDir, "static"), { recursive: true });
90
- await buildWorker(this.#projectRoot, outputDir);
91
- const jsxOptions = await loadJSXConfig(this.#projectRoot);
92
- const initialBuildPromise = new Promise((resolve2) => {
93
- this.#initialBuildResolve = resolve2;
94
- });
95
- const buildOptions = {
96
- entryPoints: [entryPath],
97
- bundle: true,
98
- format: "esm",
99
- target: "es2022",
100
- platform: "node",
101
- outdir: `${outputDir}/server`,
102
- entryNames: "[name]-[hash]",
103
- metafile: true,
104
- absWorkingDir: this.#projectRoot,
105
- plugins: [
106
- importMetaPlugin(),
107
- assetsPlugin({
108
- outDir: outputDir,
109
- clientBuild: {
110
- jsx: jsxOptions.jsx,
111
- jsxFactory: jsxOptions.jsxFactory,
112
- jsxFragment: jsxOptions.jsxFragment,
113
- jsxImportSource: jsxOptions.jsxImportSource
114
- }
115
- }),
116
- // Plugin to detect build completion (works with watch mode)
117
- {
118
- name: "build-notify",
119
- setup: (build2) => {
120
- build2.onStart(() => {
121
- logger.info("Building", {
122
- entrypoint: this.#options.entrypoint
123
- });
124
- });
125
- build2.onEnd(async (result) => {
126
- let success = result.errors.length === 0;
127
- const dynamicImportWarnings = (result.warnings || []).filter(
128
- (w) => w.text.includes("cannot be bundled") || w.text.includes("import() call") || w.text.includes("dynamic import")
129
- );
130
- if (dynamicImportWarnings.length > 0) {
131
- success = false;
132
- for (const warning of dynamicImportWarnings) {
133
- const loc = warning.location;
134
- const file = loc?.file || "unknown";
135
- const line = loc?.line || "?";
136
- logger.error(
137
- "Non-analyzable dynamic import at {file}:{line}: {text}",
138
- { file, line, text: warning.text }
139
- );
140
- }
141
- logger.error(
142
- "Dynamic imports must use literal strings, not variables. For config-driven providers, ensure they are registered in shovel.json."
143
- );
144
- }
145
- let outputPath = "";
146
- if (result.metafile) {
147
- const outputs = Object.keys(result.metafile.outputs);
148
- const jsOutput = outputs.find((p) => p.endsWith(".js"));
149
- if (jsOutput) {
150
- outputPath = resolve(this.#projectRoot, jsOutput);
151
- }
152
- }
153
- if (success) {
154
- logger.info("Build complete", { entrypoint: outputPath });
155
- } else {
156
- logger.error("Build errors: {errors}", { errors: result.errors });
157
- }
158
- this.#currentEntrypoint = outputPath;
159
- if (!this.#initialBuildComplete) {
160
- this.#initialBuildComplete = true;
161
- this.#initialBuildResolve?.({ success, entrypoint: outputPath });
162
- } else {
163
- await this.#options.onBuild?.(success, outputPath);
164
- }
165
- });
166
- }
167
- }
168
- ],
169
- sourcemap: "inline",
170
- minify: false,
171
- treeShaking: true
172
- };
173
- applyJSXOptions(buildOptions, jsxOptions);
174
- this.#ctx = await ESBuild.context(buildOptions);
175
- logger.info("Starting esbuild watch mode");
176
- await this.#ctx.watch();
177
- return initialBuildPromise;
178
- }
179
- /**
180
- * Stop watching and dispose of esbuild context
181
- */
182
- async stop() {
183
- if (this.#ctx) {
184
- await this.#ctx.dispose();
185
- this.#ctx = void 0;
186
- }
187
- }
188
- };
189
-
190
- // src/commands/develop.ts
191
- var logger2 = getLogger2(["cli"]);
192
- async function developCommand(entrypoint, options, config) {
193
- try {
194
- const platformName = Platform.resolvePlatform({ ...options, config });
195
- const workerCount = getWorkerCount(options, config);
196
- if (options.verbose) {
197
- logger2.info("Platform: {platform}", { platform: platformName });
198
- logger2.info("Worker count: {workerCount}", { workerCount });
199
- }
200
- const platformInstance = await Platform.createPlatform(platformName, {
201
- port: parseInt(options.port || String(DEFAULTS.SERVER.PORT), 10),
202
- host: options.host || DEFAULTS.SERVER.HOST
203
- });
204
- logger2.info("Starting development server");
205
- logger2.info("Workers: {workerCount}", { workerCount });
206
- let serviceWorker;
207
- const outDir = "dist";
208
- const watcher = new Watcher({
209
- entrypoint,
210
- outDir,
211
- onBuild: async (success, builtEntrypoint2) => {
212
- if (success && serviceWorker) {
213
- logger2.info("Reloading Workers with {entrypoint}", {
214
- entrypoint: builtEntrypoint2
215
- });
216
- if (platformInstance && typeof platformInstance.reloadWorkers === "function") {
217
- await platformInstance.reloadWorkers(builtEntrypoint2);
218
- }
219
- logger2.info("Workers reloaded");
220
- }
221
- }
222
- });
223
- const { success: buildSuccess, entrypoint: builtEntrypoint } = await watcher.start();
224
- if (!buildSuccess) {
225
- logger2.error("Initial build failed, watching for changes to retry");
226
- }
227
- serviceWorker = await platformInstance.loadServiceWorker(builtEntrypoint, {
228
- hotReload: true,
229
- workerCount
230
- });
231
- const server = platformInstance.createServer(serviceWorker.handleRequest, {
232
- port: parseInt(options.port || String(DEFAULTS.SERVER.PORT), 10),
233
- host: options.host || DEFAULTS.SERVER.HOST
234
- });
235
- await server.listen();
236
- logger2.info("Server running at {url}", {
237
- url: `http://${options.host}:${options.port}`
238
- });
239
- logger2.info("Serving {entrypoint}", { entrypoint });
240
- const shutdown = async (signal) => {
241
- logger2.info("Shutting down gracefully ({signal})", { signal });
242
- await watcher.stop();
243
- await serviceWorker?.dispose();
244
- await platformInstance.dispose();
245
- await server.close();
246
- logger2.info("Shutdown complete");
247
- process.exit(0);
248
- };
249
- process.on("SIGINT", () => shutdown("SIGINT"));
250
- process.on("SIGTERM", () => shutdown("SIGTERM"));
251
- } catch (error) {
252
- logger2.error("Failed to start development server: {error}", { error });
253
- process.exit(1);
254
- }
255
- }
256
- function getWorkerCount(options, config) {
257
- if (options.workers) {
258
- return parseInt(options.workers, 10);
259
- }
260
- return config?.workers ?? DEFAULTS.WORKERS;
261
- }
262
- export {
263
- developCommand
264
- };