@reckona/mreact-router 0.0.190 → 0.0.192
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 +1 -1
- package/dist/build.d.ts +6 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +57 -12
- package/dist/build.js.map +1 -1
- package/dist/cli-options.d.ts +3 -0
- package/dist/cli-options.d.ts.map +1 -1
- package/dist/cli-options.js +42 -0
- package/dist/cli-options.js.map +1 -1
- package/dist/cli.js +12 -0
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
- package/src/build.ts +113 -16
- package/src/cli-options.ts +64 -0
- package/src/cli.ts +12 -0
- package/src/index.ts +1 -0
package/src/build.ts
CHANGED
|
@@ -117,10 +117,18 @@ const serverArtifactFilesystemConcurrency = 2;
|
|
|
117
117
|
type ServerTransformOutput = ReturnType<typeof transform>;
|
|
118
118
|
type ServerTransformCache = Map<string, Promise<ServerTransformOutput>>;
|
|
119
119
|
|
|
120
|
+
export type AwsLambdaGeneratedHandlerPreloadMode =
|
|
121
|
+
| "all"
|
|
122
|
+
| "hot-route-requests"
|
|
123
|
+
| "middleware"
|
|
124
|
+
| "none";
|
|
125
|
+
|
|
120
126
|
/**
|
|
121
127
|
* Configures an app-router production build and its deployment targets.
|
|
122
128
|
*/
|
|
123
129
|
export interface BuildAppOptions extends AppRouterProjectOptions {
|
|
130
|
+
awsLambdaPreload?: AwsLambdaGeneratedHandlerPreloadMode | undefined;
|
|
131
|
+
awsLambdaPreloadRoutes?: readonly string[] | undefined;
|
|
124
132
|
onBuildProgress?: ((event: BuildAppProgressEvent) => void) | undefined;
|
|
125
133
|
onBuildPhaseTiming?: ((timing: BuildAppPhaseTiming) => void) | undefined;
|
|
126
134
|
outDir: string;
|
|
@@ -261,6 +269,7 @@ export interface AwsLambdaArtifactManifest {
|
|
|
261
269
|
files: Array<{ bytes: number; path: string }>;
|
|
262
270
|
handler: string;
|
|
263
271
|
runtime: "aws-lambda";
|
|
272
|
+
streamingHandler?: string | undefined;
|
|
264
273
|
totalBytes: number;
|
|
265
274
|
version: 1;
|
|
266
275
|
}
|
|
@@ -280,6 +289,8 @@ export interface CloudflarePagesArtifactManifest {
|
|
|
280
289
|
* Configures packaging of a built app-router output directory for AWS Lambda.
|
|
281
290
|
*/
|
|
282
291
|
export interface PackageAwsLambdaArtifactOptions {
|
|
292
|
+
awsLambdaPreload?: AwsLambdaGeneratedHandlerPreloadMode | undefined;
|
|
293
|
+
awsLambdaPreloadRoutes?: readonly string[] | undefined;
|
|
283
294
|
fromDir: string;
|
|
284
295
|
handlerEntry?: string | undefined;
|
|
285
296
|
outDir: string;
|
|
@@ -810,7 +821,15 @@ async function buildAppWithResolvedProject(
|
|
|
810
821
|
|
|
811
822
|
if (shouldTrackBuildPhases === false) {
|
|
812
823
|
await Promise.all([
|
|
813
|
-
...(shouldBuildAwsLambda
|
|
824
|
+
...(shouldBuildAwsLambda
|
|
825
|
+
? [
|
|
826
|
+
writeAwsLambdaHandlerArtifact(
|
|
827
|
+
options.outDir,
|
|
828
|
+
options.awsLambdaPreload,
|
|
829
|
+
options.awsLambdaPreloadRoutes,
|
|
830
|
+
),
|
|
831
|
+
]
|
|
832
|
+
: []),
|
|
814
833
|
...(shouldBuildCloudflare
|
|
815
834
|
? [
|
|
816
835
|
writeCloudflareWorkerArtifact({
|
|
@@ -825,7 +844,15 @@ async function buildAppWithResolvedProject(
|
|
|
825
844
|
} else {
|
|
826
845
|
await timeBuildPhase(timingSink, progressSink, "adapterArtifacts", async () => {
|
|
827
846
|
await Promise.all([
|
|
828
|
-
...(shouldBuildAwsLambda
|
|
847
|
+
...(shouldBuildAwsLambda
|
|
848
|
+
? [
|
|
849
|
+
writeAwsLambdaHandlerArtifact(
|
|
850
|
+
options.outDir,
|
|
851
|
+
options.awsLambdaPreload,
|
|
852
|
+
options.awsLambdaPreloadRoutes,
|
|
853
|
+
),
|
|
854
|
+
]
|
|
855
|
+
: []),
|
|
829
856
|
...(shouldBuildCloudflare
|
|
830
857
|
? [
|
|
831
858
|
writeCloudflareWorkerArtifact({
|
|
@@ -888,14 +915,18 @@ async function createIncrementalBuildCacheFingerprint(options: {
|
|
|
888
915
|
}))
|
|
889
916
|
.sort((left, right) => left.file.localeCompare(right.file));
|
|
890
917
|
const payload = {
|
|
891
|
-
appDir: normalizeBuildInputPath(
|
|
918
|
+
appDir: normalizeBuildInputPath(
|
|
919
|
+
relative(options.project.projectRoot, options.project.routesDir),
|
|
920
|
+
),
|
|
892
921
|
assetBaseUrl: options.project.assetBaseUrl ?? null,
|
|
893
922
|
clientConsolePureFunctions: options.project.clientConsolePureFunctions ?? [],
|
|
894
923
|
clientSourceMaps: options.project.clientSourceMaps,
|
|
895
924
|
define,
|
|
896
925
|
nodeEnv: process.env.NODE_ENV ?? null,
|
|
897
926
|
publicAssetBaseUrl: options.project.publicAssetBaseUrl ?? null,
|
|
898
|
-
publicDir: normalizeBuildInputPath(
|
|
927
|
+
publicDir: normalizeBuildInputPath(
|
|
928
|
+
relative(options.project.projectRoot, options.project.publicDir),
|
|
929
|
+
),
|
|
899
930
|
routes,
|
|
900
931
|
sourceDirs: options.project.allowedSourceDirs
|
|
901
932
|
.map((directory) => normalizeBuildInputPath(relative(options.project.projectRoot, directory)))
|
|
@@ -1009,10 +1040,7 @@ async function readJsonBuildOutput<T>(file: string): Promise<T | undefined> {
|
|
|
1009
1040
|
try {
|
|
1010
1041
|
return JSON.parse(await readFile(file, "utf8")) as T;
|
|
1011
1042
|
} catch (error) {
|
|
1012
|
-
if (
|
|
1013
|
-
(isNodeError(error) && error.code === "ENOENT") ||
|
|
1014
|
-
error instanceof SyntaxError
|
|
1015
|
-
) {
|
|
1043
|
+
if ((isNodeError(error) && error.code === "ENOENT") || error instanceof SyntaxError) {
|
|
1016
1044
|
return undefined;
|
|
1017
1045
|
}
|
|
1018
1046
|
|
|
@@ -6483,10 +6511,25 @@ async function writeNavigationRuntimeBundle(
|
|
|
6483
6511
|
return script;
|
|
6484
6512
|
}
|
|
6485
6513
|
|
|
6486
|
-
async function writeAwsLambdaHandlerArtifact(
|
|
6514
|
+
async function writeAwsLambdaHandlerArtifact(
|
|
6515
|
+
outDir: string,
|
|
6516
|
+
preload?: AwsLambdaGeneratedHandlerPreloadMode,
|
|
6517
|
+
routes?: readonly string[],
|
|
6518
|
+
): Promise<void> {
|
|
6487
6519
|
const awsLambdaDir = join(outDir, "aws-lambda");
|
|
6488
6520
|
await mkdir(awsLambdaDir, { recursive: true });
|
|
6489
|
-
|
|
6521
|
+
const policy = { mode: preload ?? "middleware", ...(routes === undefined ? {} : { routes }) };
|
|
6522
|
+
await Promise.all([
|
|
6523
|
+
writeFile(
|
|
6524
|
+
join(awsLambdaDir, "mreact-handler.mjs"),
|
|
6525
|
+
awsLambdaHandlerSource("buffered", "..", policy.mode, policy.routes),
|
|
6526
|
+
),
|
|
6527
|
+
writeFile(
|
|
6528
|
+
join(awsLambdaDir, "mreact-streaming-handler.mjs"),
|
|
6529
|
+
awsLambdaHandlerSource("streaming", "..", policy.mode, policy.routes),
|
|
6530
|
+
),
|
|
6531
|
+
writeFile(join(awsLambdaDir, "preload-policy.json"), JSON.stringify(policy, null, 2)),
|
|
6532
|
+
]);
|
|
6490
6533
|
}
|
|
6491
6534
|
|
|
6492
6535
|
async function writeCloudflareWorkerArtifact(options: {
|
|
@@ -6541,7 +6584,17 @@ export async function packageAwsLambdaArtifact(
|
|
|
6541
6584
|
outDir: options.outDir,
|
|
6542
6585
|
});
|
|
6543
6586
|
if (options.handlerEntry === undefined) {
|
|
6544
|
-
await
|
|
6587
|
+
const builtPolicy = await readAwsLambdaGeneratedPreloadPolicy(options.fromDir);
|
|
6588
|
+
const preload = options.awsLambdaPreload ?? builtPolicy.mode;
|
|
6589
|
+
const routes = options.awsLambdaPreloadRoutes ?? builtPolicy.routes;
|
|
6590
|
+
await writeFile(
|
|
6591
|
+
join(options.outDir, "mreact-handler.mjs"),
|
|
6592
|
+
awsLambdaHandlerSource("buffered", ".mreact", preload, routes),
|
|
6593
|
+
);
|
|
6594
|
+
await writeFile(
|
|
6595
|
+
join(options.outDir, "mreact-streaming-handler.mjs"),
|
|
6596
|
+
awsLambdaHandlerSource("streaming", ".mreact", preload, routes),
|
|
6597
|
+
);
|
|
6545
6598
|
} else {
|
|
6546
6599
|
await writeAwsLambdaCustomHandlerArtifact({
|
|
6547
6600
|
entry: options.handlerEntry,
|
|
@@ -6559,6 +6612,9 @@ export async function packageAwsLambdaArtifact(
|
|
|
6559
6612
|
files,
|
|
6560
6613
|
handler: "mreact-handler.handler",
|
|
6561
6614
|
runtime: "aws-lambda",
|
|
6615
|
+
...(options.handlerEntry === undefined
|
|
6616
|
+
? { streamingHandler: "mreact-streaming-handler.handler" }
|
|
6617
|
+
: {}),
|
|
6562
6618
|
totalBytes: files.reduce((total, file) => total + file.bytes, 0),
|
|
6563
6619
|
version: 1,
|
|
6564
6620
|
} satisfies AwsLambdaArtifactManifest;
|
|
@@ -6788,22 +6844,63 @@ async function collectArtifactFiles(
|
|
|
6788
6844
|
);
|
|
6789
6845
|
}
|
|
6790
6846
|
|
|
6791
|
-
function awsLambdaHandlerSource(
|
|
6847
|
+
function awsLambdaHandlerSource(
|
|
6848
|
+
kind: "buffered" | "streaming",
|
|
6849
|
+
outDirRelativeToHandler: string,
|
|
6850
|
+
preload: AwsLambdaGeneratedHandlerPreloadMode = "middleware",
|
|
6851
|
+
routes?: readonly string[],
|
|
6852
|
+
): string {
|
|
6853
|
+
const createHandler =
|
|
6854
|
+
kind === "streaming"
|
|
6855
|
+
? "createAwsLambdaStreamingRequestHandler"
|
|
6856
|
+
: "createAwsLambdaRequestHandler";
|
|
6792
6857
|
return `import { fileURLToPath } from "node:url";
|
|
6793
6858
|
import { dirname, resolve } from "node:path";
|
|
6794
|
-
import {
|
|
6859
|
+
import { ${createHandler}, warmAwsLambdaRuntime } from "@reckona/mreact-router/adapters/aws-lambda";
|
|
6795
6860
|
|
|
6796
6861
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
6797
|
-
|
|
6798
|
-
export const handler = await createPreloadedAwsLambdaRequestHandler({
|
|
6862
|
+
const options = {
|
|
6799
6863
|
importPolicy: "generated",
|
|
6800
6864
|
outDir: resolve(here, ${JSON.stringify(outDirRelativeToHandler)}),
|
|
6801
|
-
preload: { mode: "
|
|
6865
|
+
preload: { mode: ${JSON.stringify(preload)}${routes === undefined ? "" : `, routes: ${JSON.stringify(routes)}`} },
|
|
6802
6866
|
timings: process.env.MREACT_ROUTER_TIMINGS === "1",
|
|
6867
|
+
};
|
|
6868
|
+
|
|
6869
|
+
await warmAwsLambdaRuntime(options);
|
|
6870
|
+
export const handler = ${createHandler}({
|
|
6871
|
+
...options,
|
|
6872
|
+
preload: { mode: "none" },
|
|
6803
6873
|
});
|
|
6804
6874
|
`;
|
|
6805
6875
|
}
|
|
6806
6876
|
|
|
6877
|
+
async function readAwsLambdaGeneratedPreloadPolicy(outDir: string): Promise<{
|
|
6878
|
+
mode: AwsLambdaGeneratedHandlerPreloadMode;
|
|
6879
|
+
routes?: readonly string[] | undefined;
|
|
6880
|
+
}> {
|
|
6881
|
+
const path = join(outDir, "aws-lambda", "preload-policy.json");
|
|
6882
|
+
try {
|
|
6883
|
+
return JSON.parse(await readFile(path, "utf8")) as {
|
|
6884
|
+
mode: AwsLambdaGeneratedHandlerPreloadMode;
|
|
6885
|
+
routes?: readonly string[] | undefined;
|
|
6886
|
+
};
|
|
6887
|
+
} catch (error) {
|
|
6888
|
+
if (isNodeErrorCode(error, "ENOENT")) {
|
|
6889
|
+
return { mode: "middleware" };
|
|
6890
|
+
}
|
|
6891
|
+
throw error;
|
|
6892
|
+
}
|
|
6893
|
+
}
|
|
6894
|
+
|
|
6895
|
+
function isNodeErrorCode(error: unknown, code: string): boolean {
|
|
6896
|
+
return (
|
|
6897
|
+
typeof error === "object" &&
|
|
6898
|
+
error !== null &&
|
|
6899
|
+
"code" in error &&
|
|
6900
|
+
(error as { code?: unknown }).code === code
|
|
6901
|
+
);
|
|
6902
|
+
}
|
|
6903
|
+
|
|
6807
6904
|
async function validateProductionRoutes(options: {
|
|
6808
6905
|
clientRouteInferenceCache: ClientRouteInferenceCache;
|
|
6809
6906
|
files: Record<string, string>;
|
package/src/cli-options.ts
CHANGED
|
@@ -5,11 +5,14 @@ import {
|
|
|
5
5
|
type AppRouterClientSourceMapMode,
|
|
6
6
|
} from "./config.js";
|
|
7
7
|
import type { RequestHostPolicy } from "./serve.js";
|
|
8
|
+
import type { AwsLambdaGeneratedHandlerPreloadMode } from "./build.js";
|
|
8
9
|
|
|
9
10
|
export type CliRequestLogMode = "requests";
|
|
10
11
|
export type CliBuildTarget = AppRouterBuildTarget | "all";
|
|
11
12
|
|
|
12
13
|
export interface ParsedCliArguments {
|
|
14
|
+
awsLambdaPreload?: AwsLambdaGeneratedHandlerPreloadMode | undefined;
|
|
15
|
+
awsLambdaPreloadRoutes?: readonly string[] | undefined;
|
|
13
16
|
clientSourceMaps?: AppRouterClientSourceMapMode | undefined;
|
|
14
17
|
command: string;
|
|
15
18
|
allowedHosts?: readonly string[] | undefined;
|
|
@@ -157,6 +160,36 @@ export function parseCliArguments(argv: readonly string[]): ParsedCliArguments {
|
|
|
157
160
|
continue;
|
|
158
161
|
}
|
|
159
162
|
|
|
163
|
+
if (value === "--aws-lambda-preload") {
|
|
164
|
+
parsed.awsLambdaPreload = parseCliAwsLambdaPreloadMode(
|
|
165
|
+
readOptionValue(argv, index, "aws-lambda-preload"),
|
|
166
|
+
);
|
|
167
|
+
index += 1;
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (value.startsWith("--aws-lambda-preload=")) {
|
|
172
|
+
parsed.awsLambdaPreload = parseCliAwsLambdaPreloadMode(
|
|
173
|
+
value.slice("--aws-lambda-preload=".length),
|
|
174
|
+
);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (value === "--aws-lambda-preload-routes") {
|
|
179
|
+
parsed.awsLambdaPreloadRoutes = parseCliAwsLambdaPreloadRoutes(
|
|
180
|
+
readOptionValue(argv, index, "aws-lambda-preload-routes"),
|
|
181
|
+
);
|
|
182
|
+
index += 1;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (value.startsWith("--aws-lambda-preload-routes=")) {
|
|
187
|
+
parsed.awsLambdaPreloadRoutes = parseCliAwsLambdaPreloadRoutes(
|
|
188
|
+
value.slice("--aws-lambda-preload-routes=".length),
|
|
189
|
+
);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
|
|
160
193
|
if (value.startsWith("--client-source-maps=")) {
|
|
161
194
|
parsed.clientSourceMaps = parseCliClientSourceMapMode(
|
|
162
195
|
value.slice("--client-source-maps=".length),
|
|
@@ -195,6 +228,10 @@ export function formatCliHelp(command?: string | undefined): string {
|
|
|
195
228
|
` Select build artifacts. Defaults to ${defaultTargetLabel.toLowerCase()}. all selects ${allTargetValues}. aws-lambda writes .mreact/aws-lambda/mreact-handler.mjs and .mreact/server/import-policy.json.`,
|
|
196
229
|
" --client-source-maps=none|hidden",
|
|
197
230
|
" Control production client source map output.",
|
|
231
|
+
" --aws-lambda-preload=middleware|hot-route-requests|all|none",
|
|
232
|
+
" Select generated Lambda initialization preload. Defaults to middleware.",
|
|
233
|
+
" --aws-lambda-preload-routes=/,/login",
|
|
234
|
+
" Select routes for hot-route-requests initialization preload.",
|
|
198
235
|
" -h, --help",
|
|
199
236
|
" Show this help message.",
|
|
200
237
|
"",
|
|
@@ -223,6 +260,10 @@ export function formatCliHelp(command?: string | undefined): string {
|
|
|
223
260
|
" For aws-lambda only, skip the production node_modules check when a later deploy step installs dependencies into the package directory.",
|
|
224
261
|
" --handler <entry>",
|
|
225
262
|
" For aws-lambda only, bundle a custom handler entry into mreact-handler.mjs. App-local extensionless TypeScript imports are bundled; package imports stay external.",
|
|
263
|
+
" --aws-lambda-preload=middleware|hot-route-requests|all|none",
|
|
264
|
+
" Override generated Lambda initialization preload. Omission preserves the build policy; legacy output without policy metadata falls back to middleware.",
|
|
265
|
+
" --aws-lambda-preload-routes=/,/login",
|
|
266
|
+
" Select routes for hot-route-requests initialization preload.",
|
|
226
267
|
" -h, --help Show this help message.",
|
|
227
268
|
"",
|
|
228
269
|
"Examples:",
|
|
@@ -462,6 +503,29 @@ function parseCliClientSourceMapMode(value: string): AppRouterClientSourceMapMod
|
|
|
462
503
|
);
|
|
463
504
|
}
|
|
464
505
|
|
|
506
|
+
function parseCliAwsLambdaPreloadMode(value: string): AwsLambdaGeneratedHandlerPreloadMode {
|
|
507
|
+
if (
|
|
508
|
+
value === "all" ||
|
|
509
|
+
value === "hot-route-requests" ||
|
|
510
|
+
value === "middleware" ||
|
|
511
|
+
value === "none"
|
|
512
|
+
) {
|
|
513
|
+
return value;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
throw new Error(
|
|
517
|
+
`Unsupported aws-lambda-preload ${JSON.stringify(value)}. Expected "middleware", "hot-route-requests", "all", or "none".`,
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
function parseCliAwsLambdaPreloadRoutes(value: string): readonly string[] {
|
|
522
|
+
const routes = value.split(",").filter((route) => route.startsWith("/"));
|
|
523
|
+
if (routes.length === 0 || routes.join(",") !== value) {
|
|
524
|
+
throw new Error("aws-lambda-preload-routes must be a comma-separated list of absolute routes.");
|
|
525
|
+
}
|
|
526
|
+
return routes;
|
|
527
|
+
}
|
|
528
|
+
|
|
465
529
|
function readOptionValue(values: readonly string[], index: number, name: string): string {
|
|
466
530
|
const value = values[index + 1];
|
|
467
531
|
|
package/src/cli.ts
CHANGED
|
@@ -61,6 +61,12 @@ if (parsed !== undefined) {
|
|
|
61
61
|
try {
|
|
62
62
|
const result = await buildApp({
|
|
63
63
|
...loaded.project,
|
|
64
|
+
...(parsed.awsLambdaPreload === undefined
|
|
65
|
+
? {}
|
|
66
|
+
: { awsLambdaPreload: parsed.awsLambdaPreload }),
|
|
67
|
+
...(parsed.awsLambdaPreloadRoutes === undefined
|
|
68
|
+
? {}
|
|
69
|
+
: { awsLambdaPreloadRoutes: parsed.awsLambdaPreloadRoutes }),
|
|
64
70
|
...(parsed.clientSourceMaps === undefined
|
|
65
71
|
? {}
|
|
66
72
|
: { clientSourceMaps: parsed.clientSourceMaps }),
|
|
@@ -87,6 +93,12 @@ if (parsed !== undefined) {
|
|
|
87
93
|
} else if (command === "package") {
|
|
88
94
|
if (routeArg === "aws-lambda") {
|
|
89
95
|
const manifest = await packageAwsLambdaArtifact({
|
|
96
|
+
...(parsed.awsLambdaPreload === undefined
|
|
97
|
+
? {}
|
|
98
|
+
: { awsLambdaPreload: parsed.awsLambdaPreload }),
|
|
99
|
+
...(parsed.awsLambdaPreloadRoutes === undefined
|
|
100
|
+
? {}
|
|
101
|
+
: { awsLambdaPreloadRoutes: parsed.awsLambdaPreloadRoutes }),
|
|
90
102
|
fromDir: resolve(parsed.from ?? ".mreact"),
|
|
91
103
|
...(parsed.handler === undefined ? {} : { handlerEntry: resolve(parsed.handler) }),
|
|
92
104
|
outDir: resolve(parsed.out ?? ".lambda"),
|