@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/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
|
-
};
|
package/develop-5ORIPB7M.js
DELETED
|
@@ -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
|
-
};
|