@reckona/mreact-router 0.0.74 → 0.0.76
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/README.md +8 -2
- package/dist/build.d.ts +15 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +172 -12
- package/dist/build.js.map +1 -1
- package/dist/bundle-pipeline.d.ts +1 -0
- package/dist/bundle-pipeline.d.ts.map +1 -1
- package/dist/bundle-pipeline.js +1 -0
- package/dist/bundle-pipeline.js.map +1 -1
- package/dist/cli-options.d.ts +17 -0
- package/dist/cli-options.d.ts.map +1 -1
- package/dist/cli-options.js +113 -6
- package/dist/cli-options.js.map +1 -1
- package/dist/cli.js +22 -10
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
- package/src/build.ts +256 -14
- package/src/bundle-pipeline.ts +2 -0
- package/src/cli-options.ts +155 -6
- package/src/cli.ts +27 -11
- package/src/index.ts +3 -1
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ removal after `0.1.0`.
|
|
|
48
48
|
|
|
49
49
|
Production client source maps are disabled by default. Set `clientSourceMaps: "linked"` to emit public `.js.map` files beside route scripts and include `sourceMappingURL` comments, or set `clientSourceMaps: "hidden"` to emit upload-only maps under `.mreact/source-maps/client/` without exposing them in the client manifest. The CLI accepts the same modes with `mreact-router build --client-source-maps=hidden`, `linked`, or `none`.
|
|
50
50
|
|
|
51
|
-
`mreact-router dev` reads the same config, preserves route-agnostic Vite plugins and CSS settings, and uses `server.port` from `vite.config.ts` when `PORT` is
|
|
51
|
+
`mreact-router dev` reads the same config, preserves route-agnostic Vite plugins and CSS settings, and uses `server.port` from `vite.config.ts` when neither `--port` nor `PORT` is set. Use `mreact-router dev --port 15174` for one-off E2E or local port overrides without creating a separate Vite config. This keeps Vite CSS transforms, Playwright `webServer` setups, and local dev commands on the same configured port.
|
|
52
52
|
|
|
53
53
|
For TypeScript projects that type-check route modules directly, include the
|
|
54
54
|
app-router global declarations so route files can use `<Slot />` without a
|
|
@@ -291,6 +291,8 @@ MREACT_ROUTER_LOG=requests mreact-router dev
|
|
|
291
291
|
|
|
292
292
|
Each line includes method, path, status, duration, and runtime. Query strings, headers, and request bodies are intentionally omitted.
|
|
293
293
|
|
|
294
|
+
Built Node output binds to `127.0.0.1` by default. Use `mreact-router start .mreact --host 0.0.0.0 --host-policy=strict` or `HOST=0.0.0.0 MREACT_ROUTER_HOST_POLICY=strict mreact-router start .mreact` inside containers behind explicit port publishing or a reverse proxy. This bind address is separate from Host header trust; configure `--allowed-hosts`, `MREACT_ROUTER_ALLOWED_HOSTS`, or `hostPolicy` for public deployments.
|
|
295
|
+
|
|
294
296
|
Use `mreact-router --help`, `mreact-router help build`, or command-level help such as `mreact-router build --help` to inspect supported commands, build targets, and generated artifacts.
|
|
295
297
|
|
|
296
298
|
Server-only pages can opt into the lightweight navigation runtime without becoming hydrated client routes:
|
|
@@ -307,7 +309,11 @@ export default function Page() {
|
|
|
307
309
|
|
|
308
310
|
The build manifest records this separately from `client: true`, emits a shared navigation runtime asset, prefetches client route scripts when present, and falls back to `x-mreact-navigation: 1` HTML prefetches for server-only targets.
|
|
309
311
|
|
|
310
|
-
For Cloudflare Workers, `mreact-router build --target=cloudflare` emits `.mreact/cloudflare/worker.mjs`, `.mreact/cloudflare/route-modules.mjs`, and per-route module chunks for non-prerendered and dynamic App Router pages plus `route.ts` server routes and metadata conventions, so deployments do not need a hand-written Worker entrypoint or Vite-only `import.meta.glob()` transforms.
|
|
312
|
+
For Cloudflare Workers, `mreact-router build --target=cloudflare` emits `.mreact/cloudflare/worker.mjs`, `.mreact/cloudflare/route-modules.mjs`, and per-route module chunks for non-prerendered and dynamic App Router pages plus `route.ts` server routes and metadata conventions, so deployments do not need a hand-written Worker entrypoint or Vite-only `import.meta.glob()` transforms. Deploy that output with a Worker config whose `main` is `.mreact/cloudflare/worker.mjs` and whose `ASSETS` binding points at `.mreact/client`.
|
|
313
|
+
|
|
314
|
+
For Cloudflare Pages advanced mode, run `mreact-router package cloudflare-pages --from .mreact --out .mreact/pages` after `mreact-router build --target=cloudflare`, then deploy with `wrangler pages deploy .mreact/pages`. The package command creates a Pages output directory with bundled `_worker.js`, `_mreact/client/*` static route assets, root public assets, and `mreact-cloudflare-pages-artifact.json`. Pages provides the `ASSETS` binding to `_worker.js`; mreact's generated worker reads that binding and serves only manifest-listed route assets and public files. The package step bundles the generated Worker entry, route registry, route chunks, and router adapter code, so application deploy scripts do not need to copy `worker.mjs` manually or decide whether Wrangler should bundle package imports.
|
|
315
|
+
|
|
316
|
+
User Vite plugins from the project config are forwarded into router bundle builds after the `mreact-router` plugin itself is removed, so MDX-style syntax transforms and custom content loaders can participate in server, client, Cloudflare, and prerender bundles. Client assets, route stylesheet assets imported by pages/layouts/templates, and copied public assets are served only when they appear in the generated manifest allow-list. Dynamic routes resolve modules through a build-time registry keyed by `route.file`, not by constructing module ids from request input. Generated Cloudflare route modules preserve app-router layout/template shells and named slots for both string and `stream = true` pages, including route-local `<Await>` boundaries and local server-component imports. Generated Cloudflare server route modules dispatch method exports such as `GET`, `POST`, and `ALL` with decoded dynamic params plus `context.env`, `context.context`, `context.request`, and `context.route`, so handlers can use Worker bindings directly. The Cloudflare adapter marks streamed HTML with `Cache-Control: no-transform` and `Content-Encoding: identity` so Workers compression does not gzip-buffer the first shell before placeholders can paint. Pass `onResponse` to add cross-cutting response headers to rendered, static, asset, reload, not-found, and error responses. If a generated Cloudflare route module cannot produce the `data-mreact-route-id` marker contract required by client navigation, the adapter returns a reload signal for `x-mreact-navigation: 1` requests so the browser performs a normal document navigation without first buffering the full HTML response.
|
|
311
317
|
|
|
312
318
|
For AWS Lambda, use `createPreloadedAwsLambdaRequestHandler()` with API Gateway
|
|
313
319
|
HTTP API v2 or Lambda Function URL payload format 2.0:
|
package/dist/build.d.ts
CHANGED
|
@@ -26,10 +26,24 @@ export interface AwsLambdaArtifactManifest {
|
|
|
26
26
|
totalBytes: number;
|
|
27
27
|
version: 1;
|
|
28
28
|
}
|
|
29
|
+
export interface CloudflarePagesArtifactManifest {
|
|
30
|
+
files: Array<{
|
|
31
|
+
bytes: number;
|
|
32
|
+
path: string;
|
|
33
|
+
}>;
|
|
34
|
+
runtime: "cloudflare-pages";
|
|
35
|
+
totalBytes: number;
|
|
36
|
+
version: 1;
|
|
37
|
+
worker: "_worker.js";
|
|
38
|
+
}
|
|
29
39
|
export interface PackageAwsLambdaArtifactOptions {
|
|
30
40
|
fromDir: string;
|
|
31
41
|
outDir: string;
|
|
32
42
|
}
|
|
43
|
+
export interface PackageCloudflarePagesArtifactOptions {
|
|
44
|
+
fromDir: string;
|
|
45
|
+
outDir: string;
|
|
46
|
+
}
|
|
33
47
|
export interface BuiltServerManifest {
|
|
34
48
|
allowedSourceDirs?: readonly string[];
|
|
35
49
|
assetBaseUrl?: string;
|
|
@@ -96,4 +110,5 @@ export interface BuiltPrerenderedRoute {
|
|
|
96
110
|
}
|
|
97
111
|
export declare function buildApp(options: BuildAppOptions): Promise<BuildAppResult>;
|
|
98
112
|
export declare function packageAwsLambdaArtifact(options: PackageAwsLambdaArtifactOptions): Promise<AwsLambdaArtifactManifest>;
|
|
113
|
+
export declare function packageCloudflarePagesArtifact(options: PackageCloudflarePagesArtifactOptions): Promise<CloudflarePagesArtifactManifest>;
|
|
99
114
|
//# sourceMappingURL=build.d.ts.map
|
package/dist/build.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"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"}
|
package/dist/build.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { copyFile, cp, mkdir, readdir, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
3
3
|
import { builtinModules } from "node:module";
|
|
4
|
-
import { dirname, join, relative, sep } from "node:path";
|
|
4
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { collectStaticImportReferences, collectTopLevelValueExportNames, formatDiagnostic, hasModuleDirective, transform, } from "@reckona/mreact-compiler";
|
|
6
6
|
import { transformCompilerModuleContext } from "@reckona/mreact-compiler/internal";
|
|
7
7
|
import { buildClientRouteBatchOutput, buildNavigationRuntimeBundle, clientScriptForPath, compilerModuleContextForSource, collectClientRouteReferences, createClientRouteInferenceCache, detectClientNavigationHint, detectNavigationRuntimeHint, formatClientRouteInferenceDiagnostic, inferClientRouteModule, routeIdForPath, } from "./client.js";
|
|
@@ -65,7 +65,7 @@ export async function buildApp(options) {
|
|
|
65
65
|
routes,
|
|
66
66
|
vitePlugins,
|
|
67
67
|
});
|
|
68
|
-
const generatedImportPolicy = buildGeneratedImportPolicy({
|
|
68
|
+
const generatedImportPolicy = await buildGeneratedImportPolicy({
|
|
69
69
|
files,
|
|
70
70
|
projectRoot: project.projectRoot,
|
|
71
71
|
routes,
|
|
@@ -257,15 +257,18 @@ const frameworkRuntimePackages = new Set([
|
|
|
257
257
|
"@reckona/mreact-router",
|
|
258
258
|
"@reckona/mreact-server",
|
|
259
259
|
]);
|
|
260
|
-
|
|
260
|
+
const maxRuntimePackageManifestReads = 1000;
|
|
261
|
+
async function buildGeneratedImportPolicy(options) {
|
|
261
262
|
const routePackages = new Map();
|
|
262
263
|
const allPackages = new Set();
|
|
263
264
|
const relativeRoutesDir = relative(options.projectRoot, options.routesDir);
|
|
265
|
+
const packageJsonLookupCache = new Map();
|
|
264
266
|
for (const route of options.routes) {
|
|
265
267
|
const file = relative(options.projectRoot, route.file);
|
|
266
|
-
const packages = collectRuntimePackagesForFile({
|
|
268
|
+
const packages = await collectRuntimePackagesForFile({
|
|
267
269
|
file,
|
|
268
270
|
files: options.files,
|
|
271
|
+
packageJsonLookupCache,
|
|
269
272
|
projectRoot: options.projectRoot,
|
|
270
273
|
seen: new Set(),
|
|
271
274
|
});
|
|
@@ -280,9 +283,10 @@ function buildGeneratedImportPolicy(options) {
|
|
|
280
283
|
.map((file) => (relativeRoutesDir === "" ? file : `${relativeRoutesDir}/${file}`))
|
|
281
284
|
.find((file) => options.files[file] !== undefined);
|
|
282
285
|
if (middlewareFile !== undefined) {
|
|
283
|
-
const packages = collectRuntimePackagesForFile({
|
|
286
|
+
const packages = await collectRuntimePackagesForFile({
|
|
284
287
|
file: middlewareFile,
|
|
285
288
|
files: options.files,
|
|
289
|
+
packageJsonLookupCache,
|
|
286
290
|
projectRoot: options.projectRoot,
|
|
287
291
|
seen: new Set(),
|
|
288
292
|
});
|
|
@@ -299,7 +303,7 @@ function buildGeneratedImportPolicy(options) {
|
|
|
299
303
|
version: 1,
|
|
300
304
|
};
|
|
301
305
|
}
|
|
302
|
-
function collectRuntimePackagesForFile(options) {
|
|
306
|
+
async function collectRuntimePackagesForFile(options) {
|
|
303
307
|
if (options.seen.has(options.file)) {
|
|
304
308
|
return [];
|
|
305
309
|
}
|
|
@@ -319,14 +323,22 @@ function collectRuntimePackagesForFile(options) {
|
|
|
319
323
|
filename: join(options.projectRoot, options.file),
|
|
320
324
|
})) {
|
|
321
325
|
if (isRuntimePackageSpecifier(reference.source)) {
|
|
322
|
-
|
|
326
|
+
const packageName = runtimePackageNameForSpecifier(reference.source);
|
|
327
|
+
packages.add(packageName);
|
|
328
|
+
for (const optionalPackageName of await collectRuntimeOptionalPackages({
|
|
329
|
+
packageJsonLookupCache: options.packageJsonLookupCache,
|
|
330
|
+
packageName,
|
|
331
|
+
projectRoot: options.projectRoot,
|
|
332
|
+
})) {
|
|
333
|
+
packages.add(optionalPackageName);
|
|
334
|
+
}
|
|
323
335
|
continue;
|
|
324
336
|
}
|
|
325
337
|
const localFile = resolveBuildLocalSourceImport(options.files, options.file, reference.source);
|
|
326
338
|
if (localFile === undefined) {
|
|
327
339
|
continue;
|
|
328
340
|
}
|
|
329
|
-
for (const packageName of collectRuntimePackagesForFile({
|
|
341
|
+
for (const packageName of await collectRuntimePackagesForFile({
|
|
330
342
|
...options,
|
|
331
343
|
file: localFile,
|
|
332
344
|
})) {
|
|
@@ -336,12 +348,99 @@ function collectRuntimePackagesForFile(options) {
|
|
|
336
348
|
options.seen.delete(options.file);
|
|
337
349
|
return [...packages].sort();
|
|
338
350
|
}
|
|
351
|
+
async function collectRuntimeOptionalPackages(options) {
|
|
352
|
+
const optionalPackages = new Set();
|
|
353
|
+
const seenPackageJson = new Set();
|
|
354
|
+
const queue = [
|
|
355
|
+
{ packageName: options.packageName, optional: false, startDir: options.projectRoot },
|
|
356
|
+
];
|
|
357
|
+
for (let index = 0; index < queue.length && seenPackageJson.size < maxRuntimePackageManifestReads; index += 1) {
|
|
358
|
+
const item = queue[index];
|
|
359
|
+
if (item === undefined || !isValidRuntimePackageName(item.packageName)) {
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
const manifest = await readRuntimePackageManifest({
|
|
363
|
+
cache: options.packageJsonLookupCache,
|
|
364
|
+
packageName: item.packageName,
|
|
365
|
+
startDir: item.startDir,
|
|
366
|
+
});
|
|
367
|
+
if (manifest === undefined || seenPackageJson.has(manifest.packageJsonPath)) {
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
seenPackageJson.add(manifest.packageJsonPath);
|
|
371
|
+
if (item.optional && isRuntimePackageName(item.packageName)) {
|
|
372
|
+
optionalPackages.add(item.packageName);
|
|
373
|
+
}
|
|
374
|
+
const packageDir = dirname(manifest.packageJsonPath);
|
|
375
|
+
for (const dependencyName of Object.keys(manifest.dependencies ?? {})) {
|
|
376
|
+
if (isValidRuntimePackageName(dependencyName)) {
|
|
377
|
+
queue.push({ packageName: dependencyName, optional: false, startDir: packageDir });
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
for (const optionalDependencyName of Object.keys(manifest.optionalDependencies ?? {})) {
|
|
381
|
+
if (isValidRuntimePackageName(optionalDependencyName)) {
|
|
382
|
+
queue.push({ packageName: optionalDependencyName, optional: true, startDir: packageDir });
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return [...optionalPackages].sort();
|
|
387
|
+
}
|
|
388
|
+
async function readRuntimePackageManifest(options) {
|
|
389
|
+
const key = `${options.startDir}\0${options.packageName}`;
|
|
390
|
+
const cached = options.cache.get(key);
|
|
391
|
+
const manifest = cached ??
|
|
392
|
+
findRuntimePackageJson(options.packageName, options.startDir).then(async (packageJsonPath) => {
|
|
393
|
+
if (packageJsonPath === undefined) {
|
|
394
|
+
return undefined;
|
|
395
|
+
}
|
|
396
|
+
try {
|
|
397
|
+
const json = JSON.parse(await readFile(packageJsonPath, "utf8"));
|
|
398
|
+
return { ...json, packageJsonPath };
|
|
399
|
+
}
|
|
400
|
+
catch {
|
|
401
|
+
return undefined;
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
if (cached === undefined) {
|
|
405
|
+
options.cache.set(key, manifest);
|
|
406
|
+
}
|
|
407
|
+
return (await manifest);
|
|
408
|
+
}
|
|
409
|
+
async function findRuntimePackageJson(packageName, startDir) {
|
|
410
|
+
if (!isValidRuntimePackageName(packageName)) {
|
|
411
|
+
return undefined;
|
|
412
|
+
}
|
|
413
|
+
let current = startDir;
|
|
414
|
+
while (true) {
|
|
415
|
+
const candidate = join(current, "node_modules", ...packageName.split("/"), "package.json");
|
|
416
|
+
try {
|
|
417
|
+
if ((await stat(candidate)).isFile()) {
|
|
418
|
+
return candidate;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
// Keep walking toward the filesystem root.
|
|
423
|
+
}
|
|
424
|
+
const parent = dirname(current);
|
|
425
|
+
if (parent === current) {
|
|
426
|
+
return undefined;
|
|
427
|
+
}
|
|
428
|
+
current = parent;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
339
431
|
function isRuntimePackageSpecifier(specifier) {
|
|
340
432
|
if (specifier.startsWith(".") || specifier.startsWith("/") || /^[A-Za-z][A-Za-z0-9+.-]*:/.test(specifier)) {
|
|
341
433
|
return false;
|
|
342
434
|
}
|
|
343
435
|
const packageName = runtimePackageNameForSpecifier(specifier);
|
|
344
|
-
return !nodeBuiltinPackages.has(specifier) &&
|
|
436
|
+
return !nodeBuiltinPackages.has(specifier) && isRuntimePackageName(packageName);
|
|
437
|
+
}
|
|
438
|
+
function isRuntimePackageName(packageName) {
|
|
439
|
+
return !frameworkRuntimePackages.has(packageName);
|
|
440
|
+
}
|
|
441
|
+
function isValidRuntimePackageName(packageName) {
|
|
442
|
+
return (/^(?:@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*$/u.test(packageName) &&
|
|
443
|
+
!packageName.includes(".."));
|
|
345
444
|
}
|
|
346
445
|
function runtimePackageNameForSpecifier(specifier) {
|
|
347
446
|
if (!specifier.startsWith("@")) {
|
|
@@ -1680,6 +1779,10 @@ function cloudflareWorkspaceRuntimePlugin() {
|
|
|
1680
1779
|
["@reckona/mreact-compat/scheduler", packageFile("react-compat", "@reckona/mreact-compat", "scheduler")],
|
|
1681
1780
|
["@reckona/mreact-query", packageFile("query", "@reckona/mreact-query", "index")],
|
|
1682
1781
|
["@reckona/mreact-reactive-core", packageFile("reactive-core", "@reckona/mreact-reactive-core", "index")],
|
|
1782
|
+
[
|
|
1783
|
+
"@reckona/mreact-router/adapters/cloudflare",
|
|
1784
|
+
packageFile("router", "@reckona/mreact-router", "adapters/cloudflare"),
|
|
1785
|
+
],
|
|
1683
1786
|
["@reckona/mreact-router/link", routerLinkPath],
|
|
1684
1787
|
["@reckona/mreact-router/runtime-state", routerRuntimeStatePath],
|
|
1685
1788
|
["@reckona/mreact-router/session", packageFile("router", "@reckona/mreact-router", "session")],
|
|
@@ -1997,7 +2100,7 @@ export async function packageAwsLambdaArtifact(options) {
|
|
|
1997
2100
|
outDir: options.outDir,
|
|
1998
2101
|
});
|
|
1999
2102
|
await writeFile(join(options.outDir, "mreact-handler.mjs"), awsLambdaHandlerSource(".mreact"));
|
|
2000
|
-
const files = await
|
|
2103
|
+
const files = await collectArtifactFiles(options.outDir, "");
|
|
2001
2104
|
const manifest = {
|
|
2002
2105
|
files,
|
|
2003
2106
|
handler: "mreact-handler.handler",
|
|
@@ -2008,6 +2111,48 @@ export async function packageAwsLambdaArtifact(options) {
|
|
|
2008
2111
|
await writeFile(join(options.outDir, "mreact-lambda-artifact.json"), JSON.stringify(manifest, null, 2));
|
|
2009
2112
|
return manifest;
|
|
2010
2113
|
}
|
|
2114
|
+
export async function packageCloudflarePagesArtifact(options) {
|
|
2115
|
+
const clientManifestPath = join(options.fromDir, "client", "manifest.json");
|
|
2116
|
+
const workerPath = join(options.fromDir, "cloudflare", "worker.mjs");
|
|
2117
|
+
await assertRequiredBuildFile(clientManifestPath);
|
|
2118
|
+
await assertRequiredBuildFile(workerPath);
|
|
2119
|
+
await assertRequiredBuildFile(join(options.fromDir, "cloudflare", "route-modules.mjs"));
|
|
2120
|
+
assertPackageOutputDoesNotReplaceBuildOutput(options);
|
|
2121
|
+
const clientManifest = JSON.parse(await readFile(clientManifestPath, "utf8"));
|
|
2122
|
+
await rm(options.outDir, { force: true, recursive: true });
|
|
2123
|
+
await mkdir(options.outDir, { recursive: true });
|
|
2124
|
+
await cp(join(options.fromDir, "client"), join(options.outDir, "_mreact", "client"), {
|
|
2125
|
+
force: true,
|
|
2126
|
+
recursive: true,
|
|
2127
|
+
});
|
|
2128
|
+
await copyCloudflarePagesPublicAssets({
|
|
2129
|
+
clientDir: join(options.fromDir, "client"),
|
|
2130
|
+
outDir: options.outDir,
|
|
2131
|
+
publicAssets: clientManifest.publicAssets ?? [],
|
|
2132
|
+
});
|
|
2133
|
+
const bundledWorker = await bundleRouterModule({
|
|
2134
|
+
code: await readFile(workerPath, "utf8"),
|
|
2135
|
+
filename: workerPath,
|
|
2136
|
+
minify: true,
|
|
2137
|
+
modulePreload: false,
|
|
2138
|
+
outfile: "_worker.js",
|
|
2139
|
+
platform: "browser",
|
|
2140
|
+
plugins: [cloudflareWorkspaceRuntimePlugin()],
|
|
2141
|
+
preserveExports: true,
|
|
2142
|
+
target: "es2022",
|
|
2143
|
+
});
|
|
2144
|
+
await writeFile(join(options.outDir, "_worker.js"), bundledWorker.code);
|
|
2145
|
+
const files = await collectArtifactFiles(options.outDir, "");
|
|
2146
|
+
const manifest = {
|
|
2147
|
+
files,
|
|
2148
|
+
runtime: "cloudflare-pages",
|
|
2149
|
+
totalBytes: files.reduce((total, file) => total + file.bytes, 0),
|
|
2150
|
+
version: 1,
|
|
2151
|
+
worker: "_worker.js",
|
|
2152
|
+
};
|
|
2153
|
+
await writeFile(join(options.outDir, "mreact-cloudflare-pages-artifact.json"), JSON.stringify(manifest, null, 2));
|
|
2154
|
+
return manifest;
|
|
2155
|
+
}
|
|
2011
2156
|
async function assertRequiredBuildFile(path) {
|
|
2012
2157
|
try {
|
|
2013
2158
|
const info = await stat(path);
|
|
@@ -2020,6 +2165,21 @@ async function assertRequiredBuildFile(path) {
|
|
|
2020
2165
|
}
|
|
2021
2166
|
throw new Error(`Missing required mreact build artifact: ${path}`);
|
|
2022
2167
|
}
|
|
2168
|
+
function assertPackageOutputDoesNotReplaceBuildOutput(options) {
|
|
2169
|
+
if (resolve(options.fromDir) === resolve(options.outDir)) {
|
|
2170
|
+
throw new Error("Package output directory must be different from the mreact build output directory.");
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
async function copyCloudflarePagesPublicAssets(options) {
|
|
2174
|
+
for (const asset of options.publicAssets) {
|
|
2175
|
+
if (!asset.startsWith("/") || asset.startsWith("//") || asset.includes("..")) {
|
|
2176
|
+
continue;
|
|
2177
|
+
}
|
|
2178
|
+
const relativeAsset = asset.slice(1);
|
|
2179
|
+
await mkdir(dirname(join(options.outDir, relativeAsset)), { recursive: true });
|
|
2180
|
+
await copyFile(join(options.clientDir, relativeAsset), join(options.outDir, relativeAsset));
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2023
2183
|
async function copyAwsLambdaProjectMetadata(options) {
|
|
2024
2184
|
for (const file of [
|
|
2025
2185
|
"package.json",
|
|
@@ -2039,14 +2199,14 @@ async function copyAwsLambdaProjectMetadata(options) {
|
|
|
2039
2199
|
}
|
|
2040
2200
|
}
|
|
2041
2201
|
}
|
|
2042
|
-
async function
|
|
2202
|
+
async function collectArtifactFiles(rootDir, relativeDir) {
|
|
2043
2203
|
const entries = await readdir(join(rootDir, relativeDir), { withFileTypes: true });
|
|
2044
2204
|
const files = [];
|
|
2045
2205
|
for (const entry of entries) {
|
|
2046
2206
|
const relativePath = relativeDir === "" ? entry.name : `${relativeDir}/${entry.name}`;
|
|
2047
2207
|
const absolutePath = join(rootDir, relativePath);
|
|
2048
2208
|
if (entry.isDirectory()) {
|
|
2049
|
-
files.push(...(await
|
|
2209
|
+
files.push(...(await collectArtifactFiles(rootDir, relativePath)));
|
|
2050
2210
|
continue;
|
|
2051
2211
|
}
|
|
2052
2212
|
if (entry.isFile()) {
|