@reckona/mreact-router 0.0.96 → 0.0.98
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 +4 -0
- package/dist/actions.d.ts +7 -0
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +26 -2
- package/dist/actions.js.map +1 -1
- package/dist/adapters/aws-lambda.d.ts.map +1 -1
- package/dist/adapters/aws-lambda.js +10 -7
- package/dist/adapters/aws-lambda.js.map +1 -1
- package/dist/adapters/static.d.ts.map +1 -1
- package/dist/adapters/static.js +13 -0
- package/dist/adapters/static.js.map +1 -1
- package/dist/build.d.ts +38 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +1093 -307
- package/dist/build.js.map +1 -1
- package/dist/bundle-pipeline.d.ts +7 -0
- package/dist/bundle-pipeline.d.ts.map +1 -1
- package/dist/bundle-pipeline.js +121 -69
- package/dist/bundle-pipeline.js.map +1 -1
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +10 -8
- package/dist/cache.js.map +1 -1
- package/dist/cli-options.d.ts +1 -0
- package/dist/cli-options.d.ts.map +1 -1
- package/dist/cli-options.js +16 -0
- package/dist/cli-options.js.map +1 -1
- package/dist/cli.js +1 -0
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +6 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +120 -9
- package/dist/client.js.map +1 -1
- package/dist/csrf.d.ts.map +1 -1
- package/dist/csrf.js +7 -3
- package/dist/csrf.js.map +1 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +18 -1
- package/dist/http.js.map +1 -1
- package/dist/import-policy.d.ts.map +1 -1
- package/dist/import-policy.js +7 -3
- package/dist/import-policy.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/link.d.ts +5 -3
- package/dist/link.d.ts.map +1 -1
- package/dist/link.js +92 -3
- package/dist/link.js.map +1 -1
- package/dist/logger.d.ts +7 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js.map +1 -1
- package/dist/module-runner.d.ts +2 -0
- package/dist/module-runner.d.ts.map +1 -1
- package/dist/module-runner.js +1 -0
- package/dist/module-runner.js.map +1 -1
- package/dist/native-route-matcher.d.ts +2 -1
- package/dist/native-route-matcher.d.ts.map +1 -1
- package/dist/native-route-matcher.js +13 -2
- package/dist/native-route-matcher.js.map +1 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +121 -6
- package/dist/render.js.map +1 -1
- package/dist/route-source.d.ts +11 -11
- package/dist/route-source.d.ts.map +1 -1
- package/dist/route-source.js +32 -25
- package/dist/route-source.js.map +1 -1
- package/dist/route-styles.d.ts +6 -0
- package/dist/route-styles.d.ts.map +1 -1
- package/dist/route-styles.js +10 -1
- package/dist/route-styles.js.map +1 -1
- package/dist/serve.d.ts +5 -0
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +7 -4
- package/dist/serve.js.map +1 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +32 -5
- package/dist/vite.js.map +1 -1
- package/package.json +11 -11
- package/src/actions.ts +42 -2
- package/src/adapters/aws-lambda.ts +33 -16
- package/src/adapters/static.ts +25 -0
- package/src/build.ts +1605 -421
- package/src/bundle-pipeline.ts +136 -70
- package/src/cache.ts +13 -8
- package/src/cli-options.ts +20 -0
- package/src/cli.ts +1 -0
- package/src/client.ts +152 -15
- package/src/csrf.ts +8 -3
- package/src/http.ts +21 -1
- package/src/import-policy.ts +8 -3
- package/src/index.ts +5 -0
- package/src/link.ts +135 -8
- package/src/logger.ts +9 -1
- package/src/module-runner.ts +4 -0
- package/src/native-route-matcher.ts +13 -2
- package/src/render.ts +181 -6
- package/src/route-source.ts +33 -20
- package/src/route-styles.ts +17 -1
- package/src/serve.ts +11 -6
- package/src/vite.ts +41 -5
package/dist/render.js
CHANGED
|
@@ -683,6 +683,13 @@ async function renderAppRequestInternal(options) {
|
|
|
683
683
|
vitePlugins: options.vitePlugins,
|
|
684
684
|
});
|
|
685
685
|
html = injectHeadMetadata(html, metadata);
|
|
686
|
+
warnIfCspNonceWouldBlockInlineTags({
|
|
687
|
+
html,
|
|
688
|
+
logger: options.logger,
|
|
689
|
+
metadata,
|
|
690
|
+
request: options.request,
|
|
691
|
+
serverModuleCacheVersion: options.serverModuleCacheVersion,
|
|
692
|
+
});
|
|
686
693
|
html = injectAuthSessionClaims(html, originalAnalysis.authIncludesClaims ? currentAuthClaims() : undefined);
|
|
687
694
|
html = injectQueryState(html, dehydrate(queryClient));
|
|
688
695
|
const response = withOptionalActionCookie(htmlResponse(`<!DOCTYPE html>${clientNavigationHeadTags({
|
|
@@ -897,6 +904,13 @@ async function renderAppRequestInternal(options) {
|
|
|
897
904
|
finishRenderTimingPhase(timing, phaseStartedAt, "metadataMs");
|
|
898
905
|
phaseStartedAt = renderTimingPhaseStartedAt(timing);
|
|
899
906
|
html = injectHeadMetadata(html, metadata);
|
|
907
|
+
warnIfCspNonceWouldBlockInlineTags({
|
|
908
|
+
html,
|
|
909
|
+
logger: options.logger,
|
|
910
|
+
metadata,
|
|
911
|
+
request: options.request,
|
|
912
|
+
serverModuleCacheVersion: options.serverModuleCacheVersion,
|
|
913
|
+
});
|
|
900
914
|
html = injectAuthSessionClaims(html, originalAnalysis.authIncludesClaims ? currentAuthClaims() : undefined);
|
|
901
915
|
html = injectQueryState(html, dehydrate(queryClient));
|
|
902
916
|
const response = withOptionalActionCookie(htmlResponse(`<!DOCTYPE html>${clientNavigationHeadTags({
|
|
@@ -1513,6 +1527,7 @@ export async function bundleMiddlewareModuleCode(options) {
|
|
|
1513
1527
|
code: options.code,
|
|
1514
1528
|
filename: options.file,
|
|
1515
1529
|
platform: "node",
|
|
1530
|
+
root: options.importPolicy?.projectRoot,
|
|
1516
1531
|
plugins: [
|
|
1517
1532
|
fileImportMetaUrlPlugin(),
|
|
1518
1533
|
createAppRouterImportPolicyPlugin({
|
|
@@ -1639,7 +1654,7 @@ function routeSourceAnalysisFromArtifact(artifact) {
|
|
|
1639
1654
|
};
|
|
1640
1655
|
}
|
|
1641
1656
|
async function analyzeRouteSourceUncached(options) {
|
|
1642
|
-
const routeCode = stripRouteModuleExports(options.code);
|
|
1657
|
+
const routeCode = stripRouteModuleExports(options.code, options.filename);
|
|
1643
1658
|
const clientInference = await inferClientRouteModule({
|
|
1644
1659
|
appDir: options.appDir,
|
|
1645
1660
|
cache: options.clientRouteInferenceCache,
|
|
@@ -2126,7 +2141,7 @@ async function renderShellPrefixSuffix(appDir, shell, props, slotContext, server
|
|
|
2126
2141
|
? { client: false, clientBoundaryImports: [], diagnostics: [] }
|
|
2127
2142
|
: await inferClientRouteModule({
|
|
2128
2143
|
cache: clientRouteInferenceCache,
|
|
2129
|
-
code: stripRouteClientOnlyExports(code),
|
|
2144
|
+
code: stripRouteClientOnlyExports(code, shell.file),
|
|
2130
2145
|
filename: shell.file,
|
|
2131
2146
|
vitePlugins,
|
|
2132
2147
|
});
|
|
@@ -2442,7 +2457,7 @@ async function loadRouteLoaderModule(options) {
|
|
|
2442
2457
|
}
|
|
2443
2458
|
export async function bundleRouteLoaderModuleCode(options) {
|
|
2444
2459
|
const output = await bundleRouterModule({
|
|
2445
|
-
code: stripRouteLoaderOnlyExports(options.code),
|
|
2460
|
+
code: stripRouteLoaderOnlyExports(options.code, options.filename),
|
|
2446
2461
|
externalizeAppSourceModuleDirs: options.externalizeAppSourceModuleDirs,
|
|
2447
2462
|
filename: options.filename,
|
|
2448
2463
|
platform: "node",
|
|
@@ -2538,7 +2553,7 @@ function isRouteMetadata(value) {
|
|
|
2538
2553
|
}
|
|
2539
2554
|
async function bundleRouteMetadataModuleCode(options) {
|
|
2540
2555
|
const output = await bundleRouterModule({
|
|
2541
|
-
code: stripRouteMetadataOnlyExports(options.code),
|
|
2556
|
+
code: stripRouteMetadataOnlyExports(options.code, options.filename),
|
|
2542
2557
|
filename: options.filename,
|
|
2543
2558
|
platform: "node",
|
|
2544
2559
|
vitePlugins: options.vitePlugins,
|
|
@@ -2633,13 +2648,113 @@ function hasGenerateMetadataExport(code) {
|
|
|
2633
2648
|
function usesRuntimeCacheControl(code) {
|
|
2634
2649
|
return /\bcacheControl\s*\(/.test(code);
|
|
2635
2650
|
}
|
|
2651
|
+
function warnIfCspNonceWouldBlockInlineTags(options) {
|
|
2652
|
+
if (options.serverModuleCacheVersion !== undefined ||
|
|
2653
|
+
process.env.NODE_ENV === "production" ||
|
|
2654
|
+
options.metadata?.csp?.disable === true ||
|
|
2655
|
+
options.metadata?.csp?.directives === undefined ||
|
|
2656
|
+
!/<(?:script|style)\b/i.test(options.html)) {
|
|
2657
|
+
return;
|
|
2658
|
+
}
|
|
2659
|
+
const scriptNonces = cspDirectiveNonces(options.metadata.csp, "script-src");
|
|
2660
|
+
const styleNonces = cspDirectiveNonces(options.metadata.csp, "style-src");
|
|
2661
|
+
if (scriptNonces.size === 0 && styleNonces.size === 0) {
|
|
2662
|
+
return;
|
|
2663
|
+
}
|
|
2664
|
+
for (const tag of inlineCspTags(options.html)) {
|
|
2665
|
+
if (tag.name === "script") {
|
|
2666
|
+
if (scriptNonces.size > 0 &&
|
|
2667
|
+
!scriptHasExternalSourceOrInertType(tag.attributes) &&
|
|
2668
|
+
tag.content.trim() !== "" &&
|
|
2669
|
+
!tagHasMatchingNonce(tag.attributes, scriptNonces)) {
|
|
2670
|
+
warnCspInlineNonceMismatch(options, "script-src", "script");
|
|
2671
|
+
}
|
|
2672
|
+
continue;
|
|
2673
|
+
}
|
|
2674
|
+
if (styleNonces.size > 0 &&
|
|
2675
|
+
tag.content.trim() !== "" &&
|
|
2676
|
+
!tagHasMatchingNonce(tag.attributes, styleNonces)) {
|
|
2677
|
+
warnCspInlineNonceMismatch(options, "style-src", "style");
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
function warnCspInlineNonceMismatch(options, directive, tag) {
|
|
2682
|
+
const message = tag === "script"
|
|
2683
|
+
? "mreact router: CSP script-src uses a nonce, but an inline <script> without a matching nonce will be blocked. Add the script through metadata.head with nonce: true, move it to an external script, or remove script-src for this route."
|
|
2684
|
+
: "mreact router: CSP style-src uses a nonce, but an inline <style> without a matching nonce will be blocked. Add the style through metadata.head with nonce: true, move it to an external stylesheet, or remove style-src for this route.";
|
|
2685
|
+
if (options.logger === undefined) {
|
|
2686
|
+
console.warn(message);
|
|
2687
|
+
return;
|
|
2688
|
+
}
|
|
2689
|
+
emitRouterLog(options.logger, "warn", {
|
|
2690
|
+
directive,
|
|
2691
|
+
path: new URL(options.request.url).pathname,
|
|
2692
|
+
tag,
|
|
2693
|
+
type: "router:csp:inline-nonce-warning",
|
|
2694
|
+
});
|
|
2695
|
+
}
|
|
2696
|
+
function cspDirectiveNonces(csp, directive) {
|
|
2697
|
+
const values = csp.directives?.[directive];
|
|
2698
|
+
const nonces = new Set();
|
|
2699
|
+
if (values !== undefined && csp.nonce !== undefined) {
|
|
2700
|
+
nonces.add(csp.nonce);
|
|
2701
|
+
}
|
|
2702
|
+
for (const value of Array.isArray(values) ? values : values === undefined ? [] : [values]) {
|
|
2703
|
+
const match = /^'nonce-([^']+)'$/.exec(value);
|
|
2704
|
+
if (match?.[1] !== undefined) {
|
|
2705
|
+
nonces.add(match[1]);
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
return nonces;
|
|
2709
|
+
}
|
|
2710
|
+
function inlineCspTags(html) {
|
|
2711
|
+
const tags = [];
|
|
2712
|
+
const pattern = /<(script|style)\b([^>]*)>([\s\S]*?)<\/\1>/gi;
|
|
2713
|
+
let match;
|
|
2714
|
+
while ((match = pattern.exec(html)) !== null) {
|
|
2715
|
+
const name = match[1]?.toLowerCase();
|
|
2716
|
+
if (name !== "script" && name !== "style") {
|
|
2717
|
+
continue;
|
|
2718
|
+
}
|
|
2719
|
+
tags.push({
|
|
2720
|
+
attributes: parseTagAttributes(match[2] ?? ""),
|
|
2721
|
+
content: match[3] ?? "",
|
|
2722
|
+
name,
|
|
2723
|
+
});
|
|
2724
|
+
}
|
|
2725
|
+
return tags;
|
|
2726
|
+
}
|
|
2727
|
+
function parseTagAttributes(source) {
|
|
2728
|
+
const attributes = new Map();
|
|
2729
|
+
const pattern = /([^\s=/>]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
|
|
2730
|
+
let match;
|
|
2731
|
+
while ((match = pattern.exec(source)) !== null) {
|
|
2732
|
+
const name = match[1]?.toLowerCase();
|
|
2733
|
+
if (name === undefined) {
|
|
2734
|
+
continue;
|
|
2735
|
+
}
|
|
2736
|
+
attributes.set(name, match[2] ?? match[3] ?? match[4] ?? "");
|
|
2737
|
+
}
|
|
2738
|
+
return attributes;
|
|
2739
|
+
}
|
|
2740
|
+
function scriptHasExternalSourceOrInertType(attributes) {
|
|
2741
|
+
if (attributes.has("src")) {
|
|
2742
|
+
return true;
|
|
2743
|
+
}
|
|
2744
|
+
const type = attributes.get("type")?.trim().toLowerCase();
|
|
2745
|
+
return type === "application/json" || type === "application/ld+json";
|
|
2746
|
+
}
|
|
2747
|
+
function tagHasMatchingNonce(attributes, expectedNonces) {
|
|
2748
|
+
const nonce = attributes.get("nonce");
|
|
2749
|
+
return nonce !== undefined && expectedNonces.has(nonce);
|
|
2750
|
+
}
|
|
2636
2751
|
function injectQueryState(html, state) {
|
|
2637
2752
|
if (state.queries.length === 0) {
|
|
2638
2753
|
return html;
|
|
2639
2754
|
}
|
|
2640
2755
|
const script = `<script type="application/json" id="${__MREACT_QUERY_STATE_SCRIPT_ID}">${escapeJsonForHtml(JSON.stringify(state))}</script>`;
|
|
2641
2756
|
return /<\/body>/i.test(html)
|
|
2642
|
-
? html.replace(/<\/body>/i, `${script}</body>`)
|
|
2757
|
+
? html.replace(/<\/body>/i, () => `${script}</body>`)
|
|
2643
2758
|
: `${html}${script}`;
|
|
2644
2759
|
}
|
|
2645
2760
|
function injectAuthSessionClaims(html, claims) {
|
|
@@ -2648,7 +2763,7 @@ function injectAuthSessionClaims(html, claims) {
|
|
|
2648
2763
|
}
|
|
2649
2764
|
const script = `<script type="application/json" id="${authSessionScriptId}">${escapeJsonForHtml(JSON.stringify(claims))}</script>`;
|
|
2650
2765
|
return /<\/body>/i.test(html)
|
|
2651
|
-
? html.replace(/<\/body>/i, `${script}</body>`)
|
|
2766
|
+
? html.replace(/<\/body>/i, () => `${script}</body>`)
|
|
2652
2767
|
: `${html}${script}`;
|
|
2653
2768
|
}
|
|
2654
2769
|
function authIncludesClaims(code) {
|