@pracht/core 0.2.2 → 0.2.4
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/dist/index.d.mts +9 -1
- package/dist/index.mjs +123 -45
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -157,6 +157,9 @@ type LoaderData<TLoader extends LoaderLike> = TLoader extends ((...args: any[])
|
|
|
157
157
|
interface HeadArgs<TLoader extends LoaderLike = undefined, TContext = any> extends BaseRouteArgs<TContext> {
|
|
158
158
|
data: LoaderData<TLoader>;
|
|
159
159
|
}
|
|
160
|
+
interface HeadersArgs<TLoader extends LoaderLike = undefined, TContext = any> extends BaseRouteArgs<TContext> {
|
|
161
|
+
data: LoaderData<TLoader>;
|
|
162
|
+
}
|
|
160
163
|
interface RouteComponentProps<TLoader extends LoaderLike = undefined> {
|
|
161
164
|
data: LoaderData<TLoader>;
|
|
162
165
|
params: RouteParams;
|
|
@@ -171,6 +174,7 @@ type LoaderFn<TContext = any, TData = unknown> = (args: LoaderArgs<TContext>) =>
|
|
|
171
174
|
interface RouteModule<TContext = any, TLoader extends LoaderLike = undefined> {
|
|
172
175
|
loader?: LoaderFn<TContext>;
|
|
173
176
|
head?: (args: HeadArgs<TLoader, TContext>) => MaybePromise<HeadMetadata>;
|
|
177
|
+
headers?: (args: HeadersArgs<TLoader, TContext>) => MaybePromise<HeadersInit>;
|
|
174
178
|
Component?: FunctionComponent<RouteComponentProps<TLoader>>;
|
|
175
179
|
default?: FunctionComponent<RouteComponentProps<TLoader>>;
|
|
176
180
|
ErrorBoundary?: FunctionComponent<ErrorBoundaryProps>;
|
|
@@ -180,6 +184,7 @@ interface ShellModule<TContext = any> {
|
|
|
180
184
|
Shell: FunctionComponent<ShellProps>;
|
|
181
185
|
Loading?: FunctionComponent;
|
|
182
186
|
head?: (args: BaseRouteArgs<TContext>) => MaybePromise<HeadMetadata>;
|
|
187
|
+
headers?: (args: BaseRouteArgs<TContext>) => MaybePromise<HeadersInit>;
|
|
183
188
|
}
|
|
184
189
|
type MiddlewareResult<TContext = any> = void | Response | {
|
|
185
190
|
redirect: string;
|
|
@@ -351,6 +356,7 @@ declare function applyDefaultSecurityHeaders(headers: Headers): Headers;
|
|
|
351
356
|
interface PrerenderResult {
|
|
352
357
|
path: string;
|
|
353
358
|
html: string;
|
|
359
|
+
headers?: Record<string, string>;
|
|
354
360
|
}
|
|
355
361
|
interface ISGManifestEntry {
|
|
356
362
|
revalidate: RouteRevalidate;
|
|
@@ -365,6 +371,8 @@ interface PrerenderAppOptions {
|
|
|
365
371
|
clientEntryUrl?: string;
|
|
366
372
|
/** Per-source-file CSS map produced by the vite plugin (preferred over cssUrls). */
|
|
367
373
|
cssManifest?: Record<string, string[]>;
|
|
374
|
+
/** Per-source-file JS map produced by the vite plugin for modulepreload hints. */
|
|
375
|
+
jsManifest?: Record<string, string[]>;
|
|
368
376
|
/** @deprecated Pass cssManifest instead for per-page CSS resolution. */
|
|
369
377
|
cssUrls?: string[];
|
|
370
378
|
}
|
|
@@ -395,4 +403,4 @@ interface InitClientRouterOptions {
|
|
|
395
403
|
}
|
|
396
404
|
declare function initClientRouter(options: InitClientRouterOptions): Promise<void>;
|
|
397
405
|
//#endregion
|
|
398
|
-
export { type ApiConfig, type ApiRouteArgs, type ApiRouteHandler, type ApiRouteMatch, type ApiRouteModule, type BaseRouteArgs, type DataModule, type ErrorBoundaryProps, Form, type FormProps, type GroupDefinition, type GroupMeta, type HandlePrachtRequestOptions, type HeadArgs, type HeadMetadata, type HttpMethod, type ISGManifestEntry, type InitClientRouterOptions, type LoaderArgs, type LoaderData, type LoaderFn, type Location, type MiddlewareArgs, type MiddlewareFn, type MiddlewareModule, type MiddlewareResult, type ModuleImporter, type ModuleRef, type ModuleRegistry, type NavigateFn, type PrachtApp, type PrachtAppConfig, PrachtHttpError, type PrachtHydrationState, type PrachtRuntimeDiagnosticPhase, type PrachtRuntimeDiagnostics, PrachtRuntimeProvider, type PrefetchStrategy, type PrerenderAppOptions, type PrerenderAppResult, type PrerenderResult, type Register, type RenderMode, type ResolvedApiRoute, type ResolvedPrachtApp, type ResolvedRoute, type RouteComponentProps, type RouteConfig, type RouteDefinition, type RouteMatch, type RouteMeta, type RouteModule, type RouteParams, type RouteRevalidate, type RouteStateResult, type RouteTreeNode, type SerializedRouteError, type ShellModule, type ShellProps, type StartAppOptions, Suspense, type TimeRevalidatePolicy, applyDefaultSecurityHeaders, buildPathFromSegments, defineApp, forwardRef, group, handlePrachtRequest, initClientRouter, lazy, matchApiRoute, matchAppRoute, prerenderApp, readHydrationState, resolveApiRoutes, resolveApp, route, startApp, timeRevalidate, useIsHydrated, useLocation, useNavigate, useParams, useRevalidate, useRevalidateRoute, useRouteData };
|
|
406
|
+
export { type ApiConfig, type ApiRouteArgs, type ApiRouteHandler, type ApiRouteMatch, type ApiRouteModule, type BaseRouteArgs, type DataModule, type ErrorBoundaryProps, Form, type FormProps, type GroupDefinition, type GroupMeta, type HandlePrachtRequestOptions, type HeadArgs, type HeadMetadata, type HeadersArgs, type HttpMethod, type ISGManifestEntry, type InitClientRouterOptions, type LoaderArgs, type LoaderData, type LoaderFn, type Location, type MiddlewareArgs, type MiddlewareFn, type MiddlewareModule, type MiddlewareResult, type ModuleImporter, type ModuleRef, type ModuleRegistry, type NavigateFn, type PrachtApp, type PrachtAppConfig, PrachtHttpError, type PrachtHydrationState, type PrachtRuntimeDiagnosticPhase, type PrachtRuntimeDiagnostics, PrachtRuntimeProvider, type PrefetchStrategy, type PrerenderAppOptions, type PrerenderAppResult, type PrerenderResult, type Register, type RenderMode, type ResolvedApiRoute, type ResolvedPrachtApp, type ResolvedRoute, type RouteComponentProps, type RouteConfig, type RouteDefinition, type RouteMatch, type RouteMeta, type RouteModule, type RouteParams, type RouteRevalidate, type RouteStateResult, type RouteTreeNode, type SerializedRouteError, type ShellModule, type ShellProps, type StartAppOptions, Suspense, type TimeRevalidatePolicy, applyDefaultSecurityHeaders, buildPathFromSegments, defineApp, forwardRef, group, handlePrachtRequest, initClientRouter, lazy, matchApiRoute, matchAppRoute, prerenderApp, readHydrationState, resolveApiRoutes, resolveApp, route, startApp, timeRevalidate, useIsHydrated, useLocation, useNavigate, useParams, useRevalidate, useRevalidateRoute, useRouteData };
|
package/dist/index.mjs
CHANGED
|
@@ -80,6 +80,12 @@ const SAFE_METHODS = new Set(["GET", "HEAD"]);
|
|
|
80
80
|
const HYDRATION_STATE_ELEMENT_ID = "pracht-state";
|
|
81
81
|
const ROUTE_STATE_REQUEST_HEADER = "x-pracht-route-state-request";
|
|
82
82
|
const ROUTE_STATE_CACHE_CONTROL = "no-store";
|
|
83
|
+
let _renderToStringAsync;
|
|
84
|
+
async function getRenderToStringAsync() {
|
|
85
|
+
if (_renderToStringAsync) return _renderToStringAsync;
|
|
86
|
+
_renderToStringAsync = (await import("preact-render-to-string")).renderToStringAsync;
|
|
87
|
+
return _renderToStringAsync;
|
|
88
|
+
}
|
|
83
89
|
const RouteDataContext = createContext(void 0);
|
|
84
90
|
function PrachtRuntimeProvider({ children, data, params = {}, routeId, url }) {
|
|
85
91
|
const [routeData, setRouteData] = useState(data);
|
|
@@ -327,16 +333,16 @@ async function handlePrachtRequest(options) {
|
|
|
327
333
|
currentPhase = "render";
|
|
328
334
|
shellModule = match.route.shellFile ? await resolveRegistryModule(registry.shellModules, match.route.shellFile) : void 0;
|
|
329
335
|
const head = await mergeHeadMetadata(shellModule, routeModule, routeArgs, data);
|
|
336
|
+
const documentHeaders = await mergeDocumentHeaders(shellModule, routeModule, routeArgs, data);
|
|
330
337
|
const cssUrls = resolvePageCssUrls(options, match.route.shellFile, match.route.file);
|
|
331
338
|
const modulePreloadUrls = resolvePageJsUrls(options, match.route.shellFile, match.route.file);
|
|
332
339
|
if (match.route.render === "spa") {
|
|
333
340
|
let body = "";
|
|
334
341
|
if (shellModule?.Shell || shellModule?.Loading) {
|
|
335
|
-
const { renderToStringAsync } = await import("preact-render-to-string");
|
|
336
342
|
const Shell = shellModule?.Shell;
|
|
337
343
|
const Loading = shellModule?.Loading;
|
|
338
344
|
const loadingTree = Shell != null ? h(Shell, null, Loading ? h(Loading, null) : null) : Loading ? h(Loading, null) : null;
|
|
339
|
-
if (loadingTree) body = await
|
|
345
|
+
if (loadingTree) body = await (await getRenderToStringAsync())(loadingTree);
|
|
340
346
|
}
|
|
341
347
|
return htmlResponse(buildHtmlDocument({
|
|
342
348
|
head,
|
|
@@ -351,26 +357,26 @@ async function handlePrachtRequest(options) {
|
|
|
351
357
|
clientEntryUrl: options.clientEntryUrl,
|
|
352
358
|
cssUrls,
|
|
353
359
|
modulePreloadUrls
|
|
354
|
-
}));
|
|
360
|
+
}), 200, documentHeaders);
|
|
355
361
|
}
|
|
356
362
|
const DefaultComponent = typeof routeModule.default === "function" ? routeModule.default : void 0;
|
|
357
363
|
const Component = routeModule.Component ?? DefaultComponent;
|
|
358
364
|
if (!Component) throw new Error("Route has no Component or default export");
|
|
359
|
-
const { renderToStringAsync } = await import("preact-render-to-string");
|
|
360
365
|
const Shell = shellModule?.Shell;
|
|
361
366
|
const componentProps = {
|
|
362
367
|
data,
|
|
363
368
|
params: match.params
|
|
364
369
|
};
|
|
365
370
|
const componentTree = Shell ? h(Shell, null, h(Component, componentProps)) : h(Component, componentProps);
|
|
371
|
+
const tree = h(PrachtRuntimeProvider, {
|
|
372
|
+
data,
|
|
373
|
+
params: match.params,
|
|
374
|
+
routeId: match.route.id ?? "",
|
|
375
|
+
url: url.pathname
|
|
376
|
+
}, componentTree);
|
|
366
377
|
return htmlResponse(buildHtmlDocument({
|
|
367
378
|
head,
|
|
368
|
-
body: await
|
|
369
|
-
data,
|
|
370
|
-
params: match.params,
|
|
371
|
-
routeId: match.route.id ?? "",
|
|
372
|
-
url: url.pathname
|
|
373
|
-
}, componentTree)),
|
|
379
|
+
body: await (await getRenderToStringAsync())(tree),
|
|
374
380
|
hydrationState: {
|
|
375
381
|
url: url.pathname,
|
|
376
382
|
routeId: match.route.id ?? "",
|
|
@@ -380,7 +386,7 @@ async function handlePrachtRequest(options) {
|
|
|
380
386
|
clientEntryUrl: options.clientEntryUrl,
|
|
381
387
|
cssUrls,
|
|
382
388
|
modulePreloadUrls
|
|
383
|
-
}));
|
|
389
|
+
}), 200, documentHeaders);
|
|
384
390
|
} catch (error) {
|
|
385
391
|
return renderRouteErrorResponse({
|
|
386
392
|
error,
|
|
@@ -397,15 +403,42 @@ async function handlePrachtRequest(options) {
|
|
|
397
403
|
});
|
|
398
404
|
}
|
|
399
405
|
}
|
|
406
|
+
/** Strip leading `./` and `/` so all module paths share one canonical form. */
|
|
407
|
+
function normalizeModulePath(path) {
|
|
408
|
+
return path.replace(/^\.?\//, "");
|
|
409
|
+
}
|
|
410
|
+
function buildSuffixIndex(manifest) {
|
|
411
|
+
const index = /* @__PURE__ */ new Map();
|
|
412
|
+
for (const key of Object.keys(manifest)) {
|
|
413
|
+
const normalized = normalizeModulePath(key);
|
|
414
|
+
if (!normalized) continue;
|
|
415
|
+
if (!index.has(normalized)) index.set(normalized, key);
|
|
416
|
+
for (let i = normalized.indexOf("/"); i !== -1; i = normalized.indexOf("/", i + 1)) {
|
|
417
|
+
const suffix = normalized.slice(i + 1);
|
|
418
|
+
if (suffix && !index.has(suffix)) index.set(suffix, key);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return index;
|
|
422
|
+
}
|
|
423
|
+
const suffixIndexCache = /* @__PURE__ */ new WeakMap();
|
|
424
|
+
function getSuffixIndex(manifest) {
|
|
425
|
+
let index = suffixIndexCache.get(manifest);
|
|
426
|
+
if (index) return index;
|
|
427
|
+
index = buildSuffixIndex(manifest);
|
|
428
|
+
suffixIndexCache.set(manifest, index);
|
|
429
|
+
return index;
|
|
430
|
+
}
|
|
431
|
+
function resolveManifestEntries(manifest, file) {
|
|
432
|
+
if (file in manifest) return manifest[file];
|
|
433
|
+
const resolved = getSuffixIndex(manifest).get(normalizeModulePath(file));
|
|
434
|
+
if (resolved) return manifest[resolved];
|
|
435
|
+
}
|
|
400
436
|
function resolvePageCssUrls(options, shellFile, routeFile) {
|
|
401
437
|
if (!options.cssManifest) return options.cssUrls ?? [];
|
|
402
438
|
const css = /* @__PURE__ */ new Set();
|
|
403
439
|
function addFromManifest(file) {
|
|
404
|
-
const
|
|
405
|
-
|
|
406
|
-
for (const c of cssFiles) css.add(c);
|
|
407
|
-
break;
|
|
408
|
-
}
|
|
440
|
+
const entries = resolveManifestEntries(options.cssManifest, file);
|
|
441
|
+
if (entries) for (const c of entries) css.add(c);
|
|
409
442
|
}
|
|
410
443
|
if (shellFile) addFromManifest(shellFile);
|
|
411
444
|
addFromManifest(routeFile);
|
|
@@ -415,11 +448,8 @@ function resolvePageJsUrls(options, shellFile, routeFile) {
|
|
|
415
448
|
if (!options.jsManifest) return [];
|
|
416
449
|
const js = /* @__PURE__ */ new Set();
|
|
417
450
|
function addFromManifest(file) {
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
for (const j of jsFiles) js.add(j);
|
|
421
|
-
break;
|
|
422
|
-
}
|
|
451
|
+
const entries = resolveManifestEntries(options.jsManifest, file);
|
|
452
|
+
if (entries) for (const j of entries) js.add(j);
|
|
423
453
|
}
|
|
424
454
|
if (shellFile) addFromManifest(shellFile);
|
|
425
455
|
addFromManifest(routeFile);
|
|
@@ -517,11 +547,11 @@ function deserializeRouteError$1(error) {
|
|
|
517
547
|
return result;
|
|
518
548
|
}
|
|
519
549
|
function jsonErrorResponse(routeError, options) {
|
|
520
|
-
const
|
|
550
|
+
const headers = applySecurityAndRouteHeaders(new Headers({ "content-type": "application/json; charset=utf-8" }), options.isRouteStateRequest ? { isRouteStateRequest: true } : void 0);
|
|
551
|
+
return new Response(JSON.stringify({ error: routeError }), {
|
|
521
552
|
status: routeError.status,
|
|
522
|
-
headers
|
|
553
|
+
headers
|
|
523
554
|
});
|
|
524
|
-
return options.isRouteStateRequest ? withRouteResponseHeaders(response, { isRouteStateRequest: true }) : withDefaultSecurityHeaders(response);
|
|
525
555
|
}
|
|
526
556
|
function renderApiErrorResponse(options) {
|
|
527
557
|
const exposeDetails = shouldExposeServerErrors(options.options);
|
|
@@ -567,16 +597,17 @@ async function renderRouteErrorResponse(options) {
|
|
|
567
597
|
if (options.isRouteStateRequest) return jsonErrorResponse(routeErrorWithDiagnostics, { isRouteStateRequest: true });
|
|
568
598
|
const shellModule = options.shellModule ?? (options.shellFile ? await resolveRegistryModule(options.options.registry?.shellModules, options.shellFile) : void 0);
|
|
569
599
|
const head = shellModule?.head ? await shellModule.head(options.routeArgs) : {};
|
|
600
|
+
const documentHeaders = await mergeDocumentHeaders(shellModule, void 0, options.routeArgs, void 0);
|
|
570
601
|
const cssUrls = resolvePageCssUrls(options.options, options.shellFile, options.routeArgs.route.file);
|
|
571
602
|
const modulePreloadUrls = resolvePageJsUrls(options.options, options.shellFile, options.routeArgs.route.file);
|
|
572
|
-
const
|
|
603
|
+
const renderToString = await getRenderToStringAsync();
|
|
573
604
|
const ErrorBoundary = options.routeModule.ErrorBoundary;
|
|
574
605
|
const Shell = shellModule?.Shell;
|
|
575
606
|
const errorValue = deserializeRouteError$1(routeErrorWithDiagnostics);
|
|
576
607
|
const componentTree = Shell ? h(Shell, null, h(ErrorBoundary, { error: errorValue })) : h(ErrorBoundary, { error: errorValue });
|
|
577
608
|
return htmlResponse(buildHtmlDocument({
|
|
578
609
|
head,
|
|
579
|
-
body: await
|
|
610
|
+
body: await renderToString(h(PrachtRuntimeProvider, {
|
|
580
611
|
data: null,
|
|
581
612
|
routeId: options.routeId,
|
|
582
613
|
url: options.urlPathname
|
|
@@ -590,7 +621,7 @@ async function renderRouteErrorResponse(options) {
|
|
|
590
621
|
clientEntryUrl: options.options.clientEntryUrl,
|
|
591
622
|
cssUrls,
|
|
592
623
|
modulePreloadUrls
|
|
593
|
-
}), routeErrorWithDiagnostics.status);
|
|
624
|
+
}), routeErrorWithDiagnostics.status, documentHeaders);
|
|
594
625
|
}
|
|
595
626
|
async function runMiddlewareChain(options) {
|
|
596
627
|
let context = options.context;
|
|
@@ -636,8 +667,8 @@ async function resolveDataFunctions(route, routeModule, registry) {
|
|
|
636
667
|
async function resolveRegistryModule(modules, file) {
|
|
637
668
|
if (!modules) return void 0;
|
|
638
669
|
if (file in modules) return modules[file]();
|
|
639
|
-
const
|
|
640
|
-
|
|
670
|
+
const resolved = getSuffixIndex(modules).get(normalizeModulePath(file));
|
|
671
|
+
if (resolved) return modules[resolved]();
|
|
641
672
|
}
|
|
642
673
|
async function mergeHeadMetadata(shellModule, routeModule, routeArgs, data) {
|
|
643
674
|
const shellHead = shellModule?.head ? await shellModule.head(routeArgs) : {};
|
|
@@ -652,6 +683,22 @@ async function mergeHeadMetadata(shellModule, routeModule, routeArgs, data) {
|
|
|
652
683
|
link: [...shellHead.link ?? [], ...routeHead.link ?? []]
|
|
653
684
|
};
|
|
654
685
|
}
|
|
686
|
+
async function mergeDocumentHeaders(shellModule, routeModule, routeArgs, data) {
|
|
687
|
+
const headers = new Headers();
|
|
688
|
+
const shellHeaders = shellModule?.headers ? await shellModule.headers(routeArgs) : void 0;
|
|
689
|
+
if (shellHeaders) applyHeaders(headers, shellHeaders);
|
|
690
|
+
const routeHeaders = routeModule?.headers ? await routeModule.headers({
|
|
691
|
+
...routeArgs,
|
|
692
|
+
data
|
|
693
|
+
}) : void 0;
|
|
694
|
+
if (routeHeaders) applyHeaders(headers, routeHeaders);
|
|
695
|
+
return headers;
|
|
696
|
+
}
|
|
697
|
+
function applyHeaders(headers, init) {
|
|
698
|
+
new Headers(init).forEach((value, key) => {
|
|
699
|
+
headers.set(key, value);
|
|
700
|
+
});
|
|
701
|
+
}
|
|
655
702
|
function buildHtmlDocument(options) {
|
|
656
703
|
const { head, body, hydrationState, clientEntryUrl, cssUrls = [], modulePreloadUrls = [] } = options;
|
|
657
704
|
const titleTag = head.title ? `<title>${escapeHtml(head.title)}</title>` : "";
|
|
@@ -678,11 +725,14 @@ function buildHtmlDocument(options) {
|
|
|
678
725
|
</body>
|
|
679
726
|
</html>`;
|
|
680
727
|
}
|
|
681
|
-
function htmlResponse(html, status = 200) {
|
|
682
|
-
|
|
728
|
+
function htmlResponse(html, status = 200, initHeaders) {
|
|
729
|
+
const headers = new Headers({ "content-type": "text/html; charset=utf-8" });
|
|
730
|
+
if (initHeaders) applyHeaders(headers, initHeaders);
|
|
731
|
+
applySecurityAndRouteHeaders(headers, { isRouteStateRequest: false });
|
|
732
|
+
return new Response(html, {
|
|
683
733
|
status,
|
|
684
|
-
headers
|
|
685
|
-
})
|
|
734
|
+
headers
|
|
735
|
+
});
|
|
686
736
|
}
|
|
687
737
|
function applyDefaultSecurityHeaders(headers) {
|
|
688
738
|
if (!headers.has("permissions-policy")) headers.set("permissions-policy", "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()");
|
|
@@ -691,8 +741,17 @@ function applyDefaultSecurityHeaders(headers) {
|
|
|
691
741
|
if (!headers.has("x-frame-options")) headers.set("x-frame-options", "SAMEORIGIN");
|
|
692
742
|
return headers;
|
|
693
743
|
}
|
|
744
|
+
function applySecurityAndRouteHeaders(headers, options) {
|
|
745
|
+
applyDefaultSecurityHeaders(headers);
|
|
746
|
+
if (options) {
|
|
747
|
+
appendVaryHeader(headers, ROUTE_STATE_REQUEST_HEADER);
|
|
748
|
+
if (options.isRouteStateRequest && !headers.has("cache-control")) headers.set("cache-control", ROUTE_STATE_CACHE_CONTROL);
|
|
749
|
+
}
|
|
750
|
+
return headers;
|
|
751
|
+
}
|
|
694
752
|
function withDefaultSecurityHeaders(response) {
|
|
695
|
-
const headers =
|
|
753
|
+
const headers = new Headers(response.headers);
|
|
754
|
+
applySecurityAndRouteHeaders(headers);
|
|
696
755
|
return new Response(response.body, {
|
|
697
756
|
status: response.status,
|
|
698
757
|
statusText: response.statusText,
|
|
@@ -700,9 +759,8 @@ function withDefaultSecurityHeaders(response) {
|
|
|
700
759
|
});
|
|
701
760
|
}
|
|
702
761
|
function withRouteResponseHeaders(response, options) {
|
|
703
|
-
const headers =
|
|
704
|
-
|
|
705
|
-
if (options.isRouteStateRequest && !headers.has("cache-control")) headers.set("cache-control", ROUTE_STATE_CACHE_CONTROL);
|
|
762
|
+
const headers = new Headers(response.headers);
|
|
763
|
+
applySecurityAndRouteHeaders(headers, options);
|
|
706
764
|
return new Response(response.body, {
|
|
707
765
|
status: response.status,
|
|
708
766
|
statusText: response.statusText,
|
|
@@ -730,29 +788,49 @@ async function prerenderApp(options) {
|
|
|
730
788
|
const resolved = resolveApp(options.app);
|
|
731
789
|
const results = [];
|
|
732
790
|
const isgManifest = {};
|
|
791
|
+
const work = [];
|
|
733
792
|
for (const route of resolved.routes) {
|
|
734
793
|
if (route.render !== "ssg" && route.render !== "isg") continue;
|
|
735
794
|
const paths = await collectSSGPaths(route, options.registry);
|
|
736
|
-
for (const pathname of paths) {
|
|
737
|
-
|
|
795
|
+
for (const pathname of paths) work.push({
|
|
796
|
+
pathname,
|
|
797
|
+
render: route.render,
|
|
798
|
+
revalidate: route.revalidate
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
const CONCURRENCY = 10;
|
|
802
|
+
for (let i = 0; i < work.length; i += CONCURRENCY) {
|
|
803
|
+
const batch = work.slice(i, i + CONCURRENCY);
|
|
804
|
+
const batchResults = await Promise.all(batch.map(async (item) => {
|
|
805
|
+
const url = new URL(item.pathname, "http://localhost");
|
|
738
806
|
const request = new Request(url, { method: "GET" });
|
|
739
807
|
const response = await handlePrachtRequest({
|
|
740
808
|
app: options.app,
|
|
741
809
|
request,
|
|
742
810
|
registry: options.registry,
|
|
743
811
|
clientEntryUrl: options.clientEntryUrl,
|
|
744
|
-
cssManifest: options.cssManifest
|
|
812
|
+
cssManifest: options.cssManifest,
|
|
813
|
+
jsManifest: options.jsManifest
|
|
745
814
|
});
|
|
746
815
|
if (response.status !== 200) {
|
|
747
|
-
console.warn(` Warning: ${
|
|
748
|
-
|
|
816
|
+
console.warn(` Warning: ${item.render.toUpperCase()} route "${item.pathname}" returned status ${response.status}, skipping.`);
|
|
817
|
+
return null;
|
|
749
818
|
}
|
|
750
819
|
const html = await response.text();
|
|
820
|
+
return {
|
|
821
|
+
headers: Object.fromEntries(response.headers),
|
|
822
|
+
html,
|
|
823
|
+
item
|
|
824
|
+
};
|
|
825
|
+
}));
|
|
826
|
+
for (const result of batchResults) {
|
|
827
|
+
if (!result) continue;
|
|
751
828
|
results.push({
|
|
752
|
-
path: pathname,
|
|
753
|
-
html
|
|
829
|
+
path: result.item.pathname,
|
|
830
|
+
html: result.html,
|
|
831
|
+
headers: result.headers
|
|
754
832
|
});
|
|
755
|
-
if (
|
|
833
|
+
if (result.item.render === "isg" && result.item.revalidate) isgManifest[result.item.pathname] = { revalidate: result.item.revalidate };
|
|
756
834
|
}
|
|
757
835
|
}
|
|
758
836
|
if (options.withISGManifest) return {
|