@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/CHANGELOG.md +160 -0
- package/README.md +301 -42
- package/bin/cli.js +29 -9
- package/bin/create.js +22 -22
- package/package.json +21 -13
- package/{activate-5LWUTBLL.js → src/_chunks/activate-TP6RQP47.js} +14 -11
- package/src/_chunks/build-V3IPZGKC.js +434 -0
- package/src/_chunks/chunk-ADR5RW57.js +78 -0
- package/src/_chunks/chunk-GRAFMTEH.js +1150 -0
- package/src/_chunks/chunk-JJFM7PO2.js +468 -0
- package/src/_chunks/develop-A7EU2ZDY.js +404 -0
- package/{info-PRYEMZS4.js → src/_chunks/info-TDUY3FZN.js} +1 -1
- package/build-NDUV2F2Z.js +0 -386
- package/chunk-CSH7M4MK.js +0 -861
- package/chunk-ILQUUH2L.js +0 -164
- package/develop-5ORIPB7M.js +0 -264
package/build-NDUV2F2Z.js
DELETED
|
@@ -1,386 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
applyJSXOptions,
|
|
3
|
-
importMetaPlugin,
|
|
4
|
-
loadJSXConfig
|
|
5
|
-
} from "./chunk-ILQUUH2L.js";
|
|
6
|
-
import {
|
|
7
|
-
findProjectRoot,
|
|
8
|
-
findWorkspaceRoot,
|
|
9
|
-
generateConfigModule,
|
|
10
|
-
getNodeModulesPath,
|
|
11
|
-
loadRawConfig
|
|
12
|
-
} from "./chunk-CSH7M4MK.js";
|
|
13
|
-
|
|
14
|
-
// src/commands/build.ts
|
|
15
|
-
import * as ESBuild from "esbuild";
|
|
16
|
-
import { resolve, join, dirname } from "path";
|
|
17
|
-
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
18
|
-
import { assetsPlugin } from "@b9g/assets/plugin";
|
|
19
|
-
import { getLogger } from "@logtape/logtape";
|
|
20
|
-
import * as Platform from "@b9g/platform";
|
|
21
|
-
var logger = getLogger(["cli"]);
|
|
22
|
-
function validateDynamicImports(result, context) {
|
|
23
|
-
const dynamicImportWarnings = (result.warnings || []).filter(
|
|
24
|
-
(w) => w.text.includes("cannot be bundled") || w.text.includes("import() call") || w.text.includes("dynamic import")
|
|
25
|
-
);
|
|
26
|
-
if (dynamicImportWarnings.length > 0) {
|
|
27
|
-
const locations = dynamicImportWarnings.map((w) => {
|
|
28
|
-
const loc = w.location;
|
|
29
|
-
const file = loc?.file || "unknown";
|
|
30
|
-
const line = loc?.line || "?";
|
|
31
|
-
return ` ${file}:${line} - ${w.text}`;
|
|
32
|
-
}).join("\n");
|
|
33
|
-
throw new Error(
|
|
34
|
-
`Build failed (${context}): Non-analyzable dynamic imports found:
|
|
35
|
-
${locations}
|
|
36
|
-
|
|
37
|
-
Dynamic imports must use literal strings, not variables.
|
|
38
|
-
For config-driven providers, ensure they are registered in shovel.json.`
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
var BUILD_DEFAULTS = {
|
|
43
|
-
format: "esm",
|
|
44
|
-
target: "es2022",
|
|
45
|
-
outputFile: "index.js",
|
|
46
|
-
sourcemap: false,
|
|
47
|
-
minify: false,
|
|
48
|
-
treeShaking: true
|
|
49
|
-
};
|
|
50
|
-
var BUILD_STRUCTURE = {
|
|
51
|
-
serverDir: "server",
|
|
52
|
-
staticDir: "static"
|
|
53
|
-
};
|
|
54
|
-
function createConfigPlugin(projectRoot) {
|
|
55
|
-
const rawConfig = loadRawConfig(projectRoot);
|
|
56
|
-
const configModuleCode = generateConfigModule(rawConfig);
|
|
57
|
-
return {
|
|
58
|
-
name: "shovel-config",
|
|
59
|
-
setup(build2) {
|
|
60
|
-
build2.onResolve({ filter: /^shovel:config$/ }, (args) => ({
|
|
61
|
-
path: args.path,
|
|
62
|
-
namespace: "shovel-config"
|
|
63
|
-
}));
|
|
64
|
-
build2.onLoad({ filter: /.*/, namespace: "shovel-config" }, () => ({
|
|
65
|
-
contents: configModuleCode,
|
|
66
|
-
loader: "js",
|
|
67
|
-
resolveDir: projectRoot
|
|
68
|
-
}));
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
async function buildForProduction({
|
|
73
|
-
entrypoint,
|
|
74
|
-
outDir,
|
|
75
|
-
verbose,
|
|
76
|
-
platform = "node",
|
|
77
|
-
workerCount = 1
|
|
78
|
-
}) {
|
|
79
|
-
const buildContext = await initializeBuild({
|
|
80
|
-
entrypoint,
|
|
81
|
-
outDir,
|
|
82
|
-
verbose,
|
|
83
|
-
platform,
|
|
84
|
-
workerCount
|
|
85
|
-
});
|
|
86
|
-
const buildConfig = await createBuildConfig(buildContext);
|
|
87
|
-
const result = await ESBuild.build(buildConfig);
|
|
88
|
-
validateDynamicImports(result, "main bundle");
|
|
89
|
-
if (verbose && result.metafile) {
|
|
90
|
-
await logBundleAnalysis(result.metafile);
|
|
91
|
-
}
|
|
92
|
-
await generatePackageJSON({
|
|
93
|
-
...buildContext,
|
|
94
|
-
entryPath: buildContext.entryPath
|
|
95
|
-
});
|
|
96
|
-
if (verbose) {
|
|
97
|
-
logger.info("Built app to", { outputDir: buildContext.outputDir });
|
|
98
|
-
logger.info("Server files", { dir: buildContext.serverDir });
|
|
99
|
-
logger.info("Static files", { dir: join(buildContext.outputDir, "static") });
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
async function initializeBuild({
|
|
103
|
-
entrypoint,
|
|
104
|
-
outDir,
|
|
105
|
-
verbose,
|
|
106
|
-
platform,
|
|
107
|
-
workerCount = 1
|
|
108
|
-
}) {
|
|
109
|
-
if (!entrypoint) {
|
|
110
|
-
throw new Error("Entry point is required");
|
|
111
|
-
}
|
|
112
|
-
if (!outDir) {
|
|
113
|
-
throw new Error("Output directory is required");
|
|
114
|
-
}
|
|
115
|
-
if (verbose) {
|
|
116
|
-
logger.info("Entry:", { path: entrypoint });
|
|
117
|
-
logger.info("Output:", { dir: outDir });
|
|
118
|
-
logger.info("Target platform:", { platform });
|
|
119
|
-
}
|
|
120
|
-
const entryPath = resolve(entrypoint);
|
|
121
|
-
const outputDir = resolve(outDir);
|
|
122
|
-
try {
|
|
123
|
-
const stats = await readFile(entryPath, "utf8");
|
|
124
|
-
if (stats.length === 0) {
|
|
125
|
-
logger.warn("Entry point is empty", { entryPath });
|
|
126
|
-
}
|
|
127
|
-
} catch (error) {
|
|
128
|
-
throw new Error(`Entry point not found or not accessible: ${entryPath}`);
|
|
129
|
-
}
|
|
130
|
-
const validPlatforms = ["node", "bun", "cloudflare", "cloudflare-workers"];
|
|
131
|
-
if (!validPlatforms.includes(platform)) {
|
|
132
|
-
throw new Error(
|
|
133
|
-
`Invalid platform: ${platform}. Valid platforms: ${validPlatforms.join(", ")}`
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
const projectRoot = findProjectRoot();
|
|
137
|
-
if (verbose) {
|
|
138
|
-
logger.info("Entry:", { entryPath });
|
|
139
|
-
logger.info("Output:", { outputDir });
|
|
140
|
-
logger.info("Target platform:", { platform });
|
|
141
|
-
logger.info("Project root:", { projectRoot });
|
|
142
|
-
}
|
|
143
|
-
try {
|
|
144
|
-
await mkdir(outputDir, { recursive: true });
|
|
145
|
-
await mkdir(join(outputDir, BUILD_STRUCTURE.serverDir), { recursive: true });
|
|
146
|
-
await mkdir(join(outputDir, BUILD_STRUCTURE.staticDir), { recursive: true });
|
|
147
|
-
} catch (error) {
|
|
148
|
-
throw new Error(`Failed to create output directory structure: ${error}`);
|
|
149
|
-
}
|
|
150
|
-
return {
|
|
151
|
-
entryPath,
|
|
152
|
-
outputDir,
|
|
153
|
-
serverDir: join(outputDir, BUILD_STRUCTURE.serverDir),
|
|
154
|
-
projectRoot,
|
|
155
|
-
platform,
|
|
156
|
-
verbose,
|
|
157
|
-
workerCount
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
async function createBuildConfig({
|
|
161
|
-
entryPath,
|
|
162
|
-
outputDir,
|
|
163
|
-
serverDir,
|
|
164
|
-
projectRoot,
|
|
165
|
-
platform: platformName
|
|
166
|
-
}) {
|
|
167
|
-
const platform = await Platform.createPlatform(platformName);
|
|
168
|
-
const platformEsbuildConfig = platform.getEsbuildConfig();
|
|
169
|
-
const entryWrapper = platform.getEntryWrapper(entryPath);
|
|
170
|
-
const bundlesUserCodeInline = platformEsbuildConfig.bundlesUserCodeInline ?? false;
|
|
171
|
-
const jsxOptions = await loadJSXConfig(projectRoot || dirname(entryPath));
|
|
172
|
-
try {
|
|
173
|
-
const external = platformEsbuildConfig.external ?? ["node:*"];
|
|
174
|
-
if (!bundlesUserCodeInline) {
|
|
175
|
-
const userBuildConfig = {
|
|
176
|
-
entryPoints: [entryPath],
|
|
177
|
-
bundle: true,
|
|
178
|
-
format: BUILD_DEFAULTS.format,
|
|
179
|
-
target: BUILD_DEFAULTS.target,
|
|
180
|
-
platform: platformEsbuildConfig.platform ?? "node",
|
|
181
|
-
outfile: join(serverDir, "server.js"),
|
|
182
|
-
absWorkingDir: projectRoot,
|
|
183
|
-
mainFields: ["module", "main"],
|
|
184
|
-
conditions: platformEsbuildConfig.conditions ?? ["import", "module"],
|
|
185
|
-
// Resolve packages from the user's project node_modules
|
|
186
|
-
nodePaths: [getNodeModulesPath()],
|
|
187
|
-
plugins: [
|
|
188
|
-
importMetaPlugin(),
|
|
189
|
-
assetsPlugin({
|
|
190
|
-
outDir: outputDir,
|
|
191
|
-
clientBuild: {
|
|
192
|
-
jsx: jsxOptions.jsx,
|
|
193
|
-
jsxFactory: jsxOptions.jsxFactory,
|
|
194
|
-
jsxFragment: jsxOptions.jsxFragment,
|
|
195
|
-
jsxImportSource: jsxOptions.jsxImportSource
|
|
196
|
-
}
|
|
197
|
-
})
|
|
198
|
-
],
|
|
199
|
-
metafile: true,
|
|
200
|
-
sourcemap: BUILD_DEFAULTS.sourcemap,
|
|
201
|
-
minify: BUILD_DEFAULTS.minify,
|
|
202
|
-
treeShaking: BUILD_DEFAULTS.treeShaking,
|
|
203
|
-
define: platformEsbuildConfig.define ?? {},
|
|
204
|
-
external
|
|
205
|
-
};
|
|
206
|
-
applyJSXOptions(userBuildConfig, jsxOptions);
|
|
207
|
-
const userBuildResult = await ESBuild.build(userBuildConfig);
|
|
208
|
-
validateDynamicImports(userBuildResult, "user code");
|
|
209
|
-
const workerDestPath = join(serverDir, "worker.js");
|
|
210
|
-
const virtualWorkerEntry = `
|
|
211
|
-
import {configureLogging} from "@b9g/platform/runtime";
|
|
212
|
-
import {config} from "shovel:config";
|
|
213
|
-
await configureLogging(config.logging);
|
|
214
|
-
|
|
215
|
-
// Import the actual worker (runs its initialization code)
|
|
216
|
-
import "@b9g/platform/worker";
|
|
217
|
-
`;
|
|
218
|
-
await ESBuild.build({
|
|
219
|
-
stdin: {
|
|
220
|
-
contents: virtualWorkerEntry,
|
|
221
|
-
resolveDir: projectRoot,
|
|
222
|
-
sourcefile: "virtual-worker-entry.js"
|
|
223
|
-
},
|
|
224
|
-
bundle: true,
|
|
225
|
-
format: "esm",
|
|
226
|
-
target: "es2022",
|
|
227
|
-
platform: "node",
|
|
228
|
-
outfile: workerDestPath,
|
|
229
|
-
external: ["node:*"],
|
|
230
|
-
plugins: [createConfigPlugin(projectRoot)]
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
const buildConfig = {
|
|
234
|
-
stdin: {
|
|
235
|
-
contents: entryWrapper,
|
|
236
|
-
// Use serverDir so ./server.js resolves to the built user code
|
|
237
|
-
resolveDir: serverDir,
|
|
238
|
-
sourcefile: "virtual-entry.js"
|
|
239
|
-
},
|
|
240
|
-
bundle: true,
|
|
241
|
-
format: BUILD_DEFAULTS.format,
|
|
242
|
-
target: BUILD_DEFAULTS.target,
|
|
243
|
-
platform: platformEsbuildConfig.platform ?? "node",
|
|
244
|
-
// Inline bundling (Cloudflare): single-file (server.js contains everything)
|
|
245
|
-
// Separate bundling (Node/Bun): multi-file (index.js is entry, server.js is user code)
|
|
246
|
-
outfile: join(
|
|
247
|
-
serverDir,
|
|
248
|
-
bundlesUserCodeInline ? "server.js" : BUILD_DEFAULTS.outputFile
|
|
249
|
-
),
|
|
250
|
-
absWorkingDir: projectRoot,
|
|
251
|
-
mainFields: ["module", "main"],
|
|
252
|
-
conditions: platformEsbuildConfig.conditions ?? ["import", "module"],
|
|
253
|
-
// Resolve packages from the user's project node_modules
|
|
254
|
-
nodePaths: [getNodeModulesPath()],
|
|
255
|
-
plugins: bundlesUserCodeInline ? [
|
|
256
|
-
createConfigPlugin(projectRoot),
|
|
257
|
-
importMetaPlugin(),
|
|
258
|
-
assetsPlugin({
|
|
259
|
-
outDir: outputDir,
|
|
260
|
-
clientBuild: {
|
|
261
|
-
jsx: jsxOptions.jsx,
|
|
262
|
-
jsxFactory: jsxOptions.jsxFactory,
|
|
263
|
-
jsxFragment: jsxOptions.jsxFragment,
|
|
264
|
-
jsxImportSource: jsxOptions.jsxImportSource
|
|
265
|
-
}
|
|
266
|
-
})
|
|
267
|
-
] : [createConfigPlugin(projectRoot)],
|
|
268
|
-
// Config plugin needed for entry wrapper
|
|
269
|
-
metafile: true,
|
|
270
|
-
sourcemap: BUILD_DEFAULTS.sourcemap,
|
|
271
|
-
minify: BUILD_DEFAULTS.minify,
|
|
272
|
-
treeShaking: BUILD_DEFAULTS.treeShaking,
|
|
273
|
-
define: platformEsbuildConfig.define ?? {},
|
|
274
|
-
external
|
|
275
|
-
};
|
|
276
|
-
if (bundlesUserCodeInline) {
|
|
277
|
-
applyJSXOptions(buildConfig, jsxOptions);
|
|
278
|
-
}
|
|
279
|
-
return buildConfig;
|
|
280
|
-
} catch (error) {
|
|
281
|
-
throw new Error(`Failed to create build configuration: ${error}`);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
async function logBundleAnalysis(metafile) {
|
|
285
|
-
try {
|
|
286
|
-
logger.info("Bundle analysis:", {});
|
|
287
|
-
const analysis = await ESBuild.analyzeMetafile(metafile);
|
|
288
|
-
logger.info(analysis, {});
|
|
289
|
-
} catch (error) {
|
|
290
|
-
logger.warn("Failed to analyze bundle: {error}", { error });
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
async function generatePackageJSON({
|
|
294
|
-
serverDir,
|
|
295
|
-
platform,
|
|
296
|
-
verbose,
|
|
297
|
-
entryPath
|
|
298
|
-
}) {
|
|
299
|
-
const entryDir = dirname(entryPath);
|
|
300
|
-
const sourcePackageJsonPath = resolve(entryDir, "package.json");
|
|
301
|
-
try {
|
|
302
|
-
const packageJSONContent = await readFile(sourcePackageJsonPath, "utf8");
|
|
303
|
-
try {
|
|
304
|
-
JSON.parse(packageJSONContent);
|
|
305
|
-
} catch (parseError) {
|
|
306
|
-
throw new Error(`Invalid package.json format: ${parseError}`);
|
|
307
|
-
}
|
|
308
|
-
await writeFile(
|
|
309
|
-
join(serverDir, "package.json"),
|
|
310
|
-
packageJSONContent,
|
|
311
|
-
"utf8"
|
|
312
|
-
);
|
|
313
|
-
if (verbose) {
|
|
314
|
-
logger.info("Copied package.json", { serverDir });
|
|
315
|
-
}
|
|
316
|
-
} catch (error) {
|
|
317
|
-
if (verbose) {
|
|
318
|
-
logger.warn("Could not copy package.json: {error}", { error });
|
|
319
|
-
}
|
|
320
|
-
try {
|
|
321
|
-
const generatedPackageJson = await generateExecutablePackageJSON(platform);
|
|
322
|
-
await writeFile(
|
|
323
|
-
join(serverDir, "package.json"),
|
|
324
|
-
JSON.stringify(generatedPackageJson, null, 2),
|
|
325
|
-
"utf8"
|
|
326
|
-
);
|
|
327
|
-
if (verbose) {
|
|
328
|
-
logger.info("Generated package.json", { platform });
|
|
329
|
-
logger.info("Package.json contents", {
|
|
330
|
-
contents: JSON.stringify(generatedPackageJson, null, 2)
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
} catch (generateError) {
|
|
334
|
-
if (verbose) {
|
|
335
|
-
logger.warn("Could not generate package.json: {error}", {
|
|
336
|
-
error: generateError
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
async function generateExecutablePackageJSON(platform) {
|
|
343
|
-
const packageJSON = {
|
|
344
|
-
name: "shovel-executable",
|
|
345
|
-
version: "1.0.0",
|
|
346
|
-
type: "module",
|
|
347
|
-
private: true,
|
|
348
|
-
dependencies: {}
|
|
349
|
-
};
|
|
350
|
-
const isWorkspaceEnvironment = findWorkspaceRoot() !== null;
|
|
351
|
-
if (isWorkspaceEnvironment) {
|
|
352
|
-
packageJSON.dependencies = {};
|
|
353
|
-
} else {
|
|
354
|
-
switch (platform) {
|
|
355
|
-
case "node":
|
|
356
|
-
packageJSON.dependencies["@b9g/platform-node"] = "^0.1.0";
|
|
357
|
-
break;
|
|
358
|
-
case "bun":
|
|
359
|
-
packageJSON.dependencies["@b9g/platform-bun"] = "^0.1.0";
|
|
360
|
-
break;
|
|
361
|
-
case "cloudflare":
|
|
362
|
-
packageJSON.dependencies["@b9g/platform-cloudflare"] = "^0.1.0";
|
|
363
|
-
break;
|
|
364
|
-
default:
|
|
365
|
-
packageJSON.dependencies["@b9g/platform"] = "^0.1.0";
|
|
366
|
-
}
|
|
367
|
-
packageJSON.dependencies["@b9g/cache"] = "^0.1.0";
|
|
368
|
-
packageJSON.dependencies["@b9g/filesystem"] = "^0.1.0";
|
|
369
|
-
}
|
|
370
|
-
return packageJSON;
|
|
371
|
-
}
|
|
372
|
-
async function buildCommand(entrypoint, options, config) {
|
|
373
|
-
const platform = Platform.resolvePlatform({ ...options, config });
|
|
374
|
-
await buildForProduction({
|
|
375
|
-
entrypoint,
|
|
376
|
-
outDir: "dist",
|
|
377
|
-
verbose: options.verbose || false,
|
|
378
|
-
platform,
|
|
379
|
-
workerCount: options.workers ? parseInt(options.workers, 10) : config.workers
|
|
380
|
-
});
|
|
381
|
-
process.exit(0);
|
|
382
|
-
}
|
|
383
|
-
export {
|
|
384
|
-
buildCommand,
|
|
385
|
-
buildForProduction
|
|
386
|
-
};
|