vike 0.4.237-commit-33e34e7 → 0.4.238-commit-5762291
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/cjs/node/runtime/csp.js +46 -0
- package/dist/cjs/node/runtime/html/injectAssets/getHtmlTags.js +10 -7
- package/dist/cjs/node/runtime/html/injectAssets/inferHtmlTags.js +5 -2
- package/dist/cjs/node/runtime/html/injectAssets/mergeScriptTags.js +4 -2
- package/dist/cjs/node/runtime/renderPage/loadPageConfigsLazyServerSide.js +4 -0
- package/dist/cjs/node/vite/shared/resolveVikeConfigInternal/configDefinitionsBuiltIn.js +3 -0
- package/dist/cjs/shared/page-configs/resolveVikeConfigPublic.js +2 -1
- package/dist/cjs/utils/PROJECT_VERSION.js +1 -1
- package/dist/esm/node/prerender/runPrerender.d.ts +2 -0
- package/dist/esm/node/runtime/csp.d.ts +12 -0
- package/dist/esm/node/runtime/csp.js +44 -0
- package/dist/esm/node/runtime/html/injectAssets/getHtmlTags.js +10 -7
- package/dist/esm/node/runtime/html/injectAssets/inferHtmlTags.d.ts +2 -1
- package/dist/esm/node/runtime/html/injectAssets/inferHtmlTags.js +5 -2
- package/dist/esm/node/runtime/html/injectAssets/mergeScriptTags.d.ts +2 -1
- package/dist/esm/node/runtime/html/injectAssets/mergeScriptTags.js +4 -2
- package/dist/esm/node/runtime/html/serializeContext.d.ts +2 -1
- package/dist/esm/node/runtime/renderPage/loadPageConfigsLazyServerSide.d.ts +2 -0
- package/dist/esm/node/runtime/renderPage/loadPageConfigsLazyServerSide.js +4 -0
- package/dist/esm/node/runtime/renderPage/renderPageAfterRoute.d.ts +4 -0
- package/dist/esm/node/vite/shared/resolveVikeConfigInternal/configDefinitionsBuiltIn.js +3 -0
- package/dist/esm/shared/page-configs/resolveVikeConfigPublic.js +2 -1
- package/dist/esm/types/Config.d.ts +8 -0
- package/dist/esm/types/PageContext.d.ts +6 -0
- package/dist/esm/utils/PROJECT_VERSION.d.ts +1 -1
- package/dist/esm/utils/PROJECT_VERSION.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolvePageContextCspNone = resolvePageContextCspNone;
|
|
4
|
+
exports.inferNonceAttr = inferNonceAttr;
|
|
5
|
+
exports.addCspHeader = addCspHeader;
|
|
6
|
+
const import_1 = require("@brillout/import");
|
|
7
|
+
async function resolvePageContextCspNone(pageContext) {
|
|
8
|
+
if (pageContext.cspNonce)
|
|
9
|
+
return; // already set by user e.g. `renderPage({ cspNonce: '123456789' })`
|
|
10
|
+
const { csp } = pageContext.config;
|
|
11
|
+
if (!csp?.nonce)
|
|
12
|
+
return;
|
|
13
|
+
let cspNonce;
|
|
14
|
+
if (csp.nonce === true) {
|
|
15
|
+
cspNonce = await generateNonce();
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
cspNonce = await csp.nonce(pageContext);
|
|
19
|
+
}
|
|
20
|
+
const pageContextAddendum = { cspNonce };
|
|
21
|
+
return pageContextAddendum;
|
|
22
|
+
}
|
|
23
|
+
// Generate a cryptographically secure nonce for Content Security Policy (CSP).
|
|
24
|
+
// Returns a base64url-encoded nonce string (URL-safe, no padding).
|
|
25
|
+
// https://github.com/vikejs/vike/issues/1554#issuecomment-3181128304
|
|
26
|
+
async function generateNonce() {
|
|
27
|
+
let cryptoModule;
|
|
28
|
+
try {
|
|
29
|
+
cryptoModule = (await (0, import_1.import_)('crypto')).default;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return Math.random().toString(36).substring(2, 18);
|
|
33
|
+
}
|
|
34
|
+
return cryptoModule.randomBytes(16).toString('base64url');
|
|
35
|
+
}
|
|
36
|
+
function inferNonceAttr(pageContext) {
|
|
37
|
+
const nonceAttr = pageContext.cspNonce ? ` nonce="${pageContext.cspNonce}"` : '';
|
|
38
|
+
return nonceAttr;
|
|
39
|
+
}
|
|
40
|
+
function addCspHeader(pageContext, headersResponse) {
|
|
41
|
+
if (!pageContext.cspNonce)
|
|
42
|
+
return;
|
|
43
|
+
if (headersResponse.get('Content-Security-Policy'))
|
|
44
|
+
return;
|
|
45
|
+
headersResponse.set('Content-Security-Policy', `script-src 'self' 'nonce-${pageContext.cspNonce}'`);
|
|
46
|
+
}
|
|
@@ -15,6 +15,7 @@ const picocolors_1 = __importDefault(require("@brillout/picocolors"));
|
|
|
15
15
|
const getConfigDefinedAt_js_1 = require("../../../../shared/page-configs/getConfigDefinedAt.js");
|
|
16
16
|
const htmlElementIds_js_1 = require("../../../../shared/htmlElementIds.js");
|
|
17
17
|
const isFontFallback_js_1 = require("../../renderPage/isFontFallback.js");
|
|
18
|
+
const csp_js_1 = require("../../csp.js");
|
|
18
19
|
const stamp = '__injectFilterEntry';
|
|
19
20
|
async function getHtmlTags(pageContext, streamFromReactStreamingPackage, injectFilter, pageAssets, viteDevScript, isStream) {
|
|
20
21
|
(0, utils_js_1.assert)([true, false].includes(pageContext._isHtmlOnly));
|
|
@@ -81,7 +82,7 @@ async function getHtmlTags(pageContext, streamFromReactStreamingPackage, injectF
|
|
|
81
82
|
.forEach((asset) => {
|
|
82
83
|
if (!asset.inject)
|
|
83
84
|
return;
|
|
84
|
-
const htmlTag = asset.isEntry ? (0, inferHtmlTags_js_1.inferAssetTag)(asset) : (0, inferHtmlTags_js_1.inferPreloadTag)(asset);
|
|
85
|
+
const htmlTag = asset.isEntry ? (0, inferHtmlTags_js_1.inferAssetTag)(asset, pageContext) : (0, inferHtmlTags_js_1.inferPreloadTag)(asset);
|
|
85
86
|
htmlTags.push({ htmlTag, position: asset.inject });
|
|
86
87
|
});
|
|
87
88
|
// ==========
|
|
@@ -142,7 +143,7 @@ async function getHtmlTags(pageContext, streamFromReactStreamingPackage, injectF
|
|
|
142
143
|
});
|
|
143
144
|
}
|
|
144
145
|
// The JavaScript entry <script> tag
|
|
145
|
-
const scriptEntry = mergeScriptEntries(pageAssets, viteDevScript);
|
|
146
|
+
const scriptEntry = mergeScriptEntries(pageAssets, viteDevScript, pageContext);
|
|
146
147
|
if (scriptEntry) {
|
|
147
148
|
htmlTags.push({
|
|
148
149
|
htmlTag: scriptEntry,
|
|
@@ -163,9 +164,9 @@ async function getHtmlTags(pageContext, streamFromReactStreamingPackage, injectF
|
|
|
163
164
|
});
|
|
164
165
|
return htmlTags;
|
|
165
166
|
}
|
|
166
|
-
function mergeScriptEntries(pageAssets, viteDevScript) {
|
|
167
|
+
function mergeScriptEntries(pageAssets, viteDevScript, pageContext) {
|
|
167
168
|
const scriptEntries = pageAssets.filter((pageAsset) => pageAsset.isEntry && pageAsset.assetType === 'script');
|
|
168
|
-
let scriptEntry = `${viteDevScript}${scriptEntries.map((asset) => (0, inferHtmlTags_js_1.inferAssetTag)(asset)).join('')}`;
|
|
169
|
+
let scriptEntry = `${viteDevScript}${scriptEntries.map((asset) => (0, inferHtmlTags_js_1.inferAssetTag)(asset, pageContext)).join('')}`;
|
|
169
170
|
// We merge scripts to avoid the infamous HMR preamble error.
|
|
170
171
|
// - Infamous HMR preamble error:
|
|
171
172
|
// ```browser-console
|
|
@@ -183,12 +184,13 @@ function mergeScriptEntries(pageAssets, viteDevScript) {
|
|
|
183
184
|
// ```
|
|
184
185
|
// - Maybe an alternative would be to make Vike's client runtime entry <script> tag non-async. Would that work? Would it be a performance issue?
|
|
185
186
|
// - The entry <script> shouldn't be `<script defer>` upon HTML streaming, otherwise progressive hydration while SSR streaming won't work.
|
|
186
|
-
scriptEntry = (0, mergeScriptTags_js_1.mergeScriptTags)(scriptEntry);
|
|
187
|
+
scriptEntry = (0, mergeScriptTags_js_1.mergeScriptTags)(scriptEntry, pageContext);
|
|
187
188
|
return scriptEntry;
|
|
188
189
|
}
|
|
189
190
|
function getPageContextJsonScriptTag(pageContext) {
|
|
190
191
|
const pageContextClientSerialized = (0, sanitizeJson_js_1.sanitizeJson)((0, serializeContext_js_1.getPageContextClientSerialized)(pageContext, true));
|
|
191
|
-
const
|
|
192
|
+
const nonceAttr = (0, csp_js_1.inferNonceAttr)(pageContext);
|
|
193
|
+
const htmlTag = `<script id="${htmlElementIds_js_1.htmlElementId_pageContext}" type="application/json"${nonceAttr}>${pageContextClientSerialized}</script>`;
|
|
192
194
|
// Used by contra.com https://github.com/gajus
|
|
193
195
|
// @ts-expect-error
|
|
194
196
|
pageContext._pageContextHtmlTag = htmlTag;
|
|
@@ -196,7 +198,8 @@ function getPageContextJsonScriptTag(pageContext) {
|
|
|
196
198
|
}
|
|
197
199
|
function getGlobalContextJsonScriptTag(pageContext) {
|
|
198
200
|
const globalContextClientSerialized = (0, sanitizeJson_js_1.sanitizeJson)((0, serializeContext_js_1.getGlobalContextClientSerialized)(pageContext, true));
|
|
199
|
-
const
|
|
201
|
+
const nonceAttr = (0, csp_js_1.inferNonceAttr)(pageContext);
|
|
202
|
+
const htmlTag = `<script id="${htmlElementIds_js_1.htmlElementId_globalContext}" type="application/json"${nonceAttr}>${globalContextClientSerialized}</script>`;
|
|
200
203
|
return htmlTag;
|
|
201
204
|
}
|
|
202
205
|
function assertInjectFilterEntries(injectFilterEntries) {
|
|
@@ -5,6 +5,8 @@ exports.inferAssetTag = inferAssetTag;
|
|
|
5
5
|
exports.inferPreloadTag = inferPreloadTag;
|
|
6
6
|
exports.inferEarlyHintLink = inferEarlyHintLink;
|
|
7
7
|
const utils_js_1 = require("../../utils.js");
|
|
8
|
+
const csp_js_1 = require("../../csp.js");
|
|
9
|
+
// TODO/now rename scriptAttrs scriptCommonAttrs
|
|
8
10
|
// We can't use `defer` here. With `defer`, the entry script won't start before `</body>` has been parsed, preventing progressive hydration during SSR streaming, see https://github.com/vikejs/vike/pull/1271
|
|
9
11
|
const scriptAttrs = 'type="module" async';
|
|
10
12
|
exports.scriptAttrs = scriptAttrs;
|
|
@@ -23,11 +25,12 @@ function inferPreloadTag(pageAsset) {
|
|
|
23
25
|
.join(' ');
|
|
24
26
|
return `<link ${attributes}>`;
|
|
25
27
|
}
|
|
26
|
-
function inferAssetTag(pageAsset) {
|
|
28
|
+
function inferAssetTag(pageAsset, pageContext) {
|
|
27
29
|
const { src, assetType, mediaType } = pageAsset;
|
|
28
30
|
if (assetType === 'script') {
|
|
29
31
|
(0, utils_js_1.assert)(mediaType === 'text/javascript');
|
|
30
|
-
|
|
32
|
+
const nonceAttr = (0, csp_js_1.inferNonceAttr)(pageContext);
|
|
33
|
+
return `<script src="${src}" ${scriptAttrs}${nonceAttr}></script>`;
|
|
31
34
|
}
|
|
32
35
|
if (assetType === 'style') {
|
|
33
36
|
// WARNING: if changing following line, then also update https://github.com/vikejs/vike/blob/fae90a15d88e5e87ca9fcbb54cf2dc8773d2f229/vike/client/shared/removeFoucBuster.ts#L29
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.mergeScriptTags = mergeScriptTags;
|
|
4
|
+
const csp_js_1 = require("../../csp.js");
|
|
4
5
|
const utils_js_1 = require("../../utils.js");
|
|
5
6
|
const inferHtmlTags_js_1 = require("./inferHtmlTags.js");
|
|
6
7
|
const scriptRE = /(<script\b(?:\s[^>]*>|>))(.*?)<\/script>/gims;
|
|
7
8
|
const srcRE = /\bsrc\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s'">]+))/im;
|
|
8
9
|
const typeRE = /\btype\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s'">]+))/im;
|
|
9
|
-
function mergeScriptTags(scriptTagsHtml) {
|
|
10
|
+
function mergeScriptTags(scriptTagsHtml, pageContext) {
|
|
10
11
|
let scriptTag = '';
|
|
11
12
|
const scripts = parseScripts(scriptTagsHtml);
|
|
12
13
|
// We need to merge module scripts to ensure execution order
|
|
@@ -35,7 +36,8 @@ function mergeScriptTags(scriptTagsHtml) {
|
|
|
35
36
|
}
|
|
36
37
|
});
|
|
37
38
|
if (contents.length > 0) {
|
|
38
|
-
|
|
39
|
+
const nonceAttr = (0, csp_js_1.inferNonceAttr)(pageContext);
|
|
40
|
+
scriptTag += `<script ${inferHtmlTags_js_1.scriptAttrs}${nonceAttr}>\n${contents.join('\n')}\n</script>`;
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
43
|
}
|
|
@@ -11,6 +11,7 @@ const analyzePage_js_1 = require("./analyzePage.js");
|
|
|
11
11
|
const loadAndParseVirtualFilePageEntry_js_1 = require("../../../shared/page-configs/loadAndParseVirtualFilePageEntry.js");
|
|
12
12
|
const execHookServer_js_1 = require("./execHookServer.js");
|
|
13
13
|
const getCacheControl_js_1 = require("./getCacheControl.js");
|
|
14
|
+
const csp_js_1 = require("../csp.js");
|
|
14
15
|
async function loadPageConfigsLazyServerSide(pageContext) {
|
|
15
16
|
(0, utils_js_1.objectAssign)(pageContext, {
|
|
16
17
|
_pageConfig: (0, findPageConfig_js_1.findPageConfig)(pageContext._globalContext._pageConfigs, pageContext.pageId),
|
|
@@ -55,6 +56,7 @@ async function resolvePageContext(pageContext) {
|
|
|
55
56
|
passToClient.push(...valS);
|
|
56
57
|
});
|
|
57
58
|
}
|
|
59
|
+
(0, utils_js_1.objectAssign)(pageContext, await (0, csp_js_1.resolvePageContextCspNone)(pageContext));
|
|
58
60
|
(0, utils_js_1.objectAssign)(pageContext, {
|
|
59
61
|
Page: pageContext.exports.Page,
|
|
60
62
|
_isHtmlOnly: isHtmlOnly,
|
|
@@ -125,6 +127,7 @@ async function loadPageUserFiles_v1Design(pageContext) {
|
|
|
125
127
|
pageFilesLoaded: pageFilesServerSide,
|
|
126
128
|
};
|
|
127
129
|
}
|
|
130
|
+
// TODO/now: move all response headers code to headersResponse.ts
|
|
128
131
|
function resolveHeadersResponse(pageContext) {
|
|
129
132
|
const headersResponse = mergeHeaders(pageContext.config.headersResponse);
|
|
130
133
|
if (!headersResponse.get('Cache-Control')) {
|
|
@@ -132,6 +135,7 @@ function resolveHeadersResponse(pageContext) {
|
|
|
132
135
|
if (cacheControl)
|
|
133
136
|
headersResponse.set('Cache-Control', cacheControl);
|
|
134
137
|
}
|
|
138
|
+
(0, csp_js_1.addCspHeader)(pageContext, headersResponse);
|
|
135
139
|
return headersResponse;
|
|
136
140
|
}
|
|
137
141
|
function mergeHeaders(headersList = []) {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
//
|
|
2
|
+
// TODO/now: rename PageConfig names
|
|
3
3
|
// - Use `Internal` suffix, i.e. {Page,Global}ConfigInternal
|
|
4
4
|
// - While keeping {Page,Global}ConfigPublic or remove Public suffix and rename it to {Page,Global}Config ?
|
|
5
5
|
// - rename EagerLoaded EagerlyLoaded
|
|
6
6
|
// - remove `LazyLoaded` suffix
|
|
7
|
+
// TODO/now: rename VikeConfigPublicPageLazyLoaded PageContextSomething (for `pageContext: PageContextSomething` usage)
|
|
7
8
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
9
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
10
|
};
|
|
@@ -174,6 +174,8 @@ declare function createPageContextPrerendering(urlOriginal: string, prerenderCon
|
|
|
174
174
|
_pageConfig: null | import("../../types/PageConfig.js").PageConfigRuntime;
|
|
175
175
|
} & import("../../shared/getPageFiles.js").VikeConfigPublicPageLazyLoaded & {
|
|
176
176
|
_pageConfig: null | import("../../types/PageConfig.js").PageConfigRuntime;
|
|
177
|
+
} & {
|
|
178
|
+
cspNonce: string;
|
|
177
179
|
} & {
|
|
178
180
|
Page: unknown;
|
|
179
181
|
_isHtmlOnly: boolean;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { resolvePageContextCspNone };
|
|
2
|
+
export { inferNonceAttr };
|
|
3
|
+
export { addCspHeader };
|
|
4
|
+
export type { PageContextCspNonce };
|
|
5
|
+
import type { VikeConfigPublicPageLazyLoaded } from '../../shared/getPageFiles.js';
|
|
6
|
+
import type { PageContextServer } from '../../types/PageContext.js';
|
|
7
|
+
declare function resolvePageContextCspNone(pageContext: VikeConfigPublicPageLazyLoaded & PageContextCspNonce): Promise<{
|
|
8
|
+
cspNonce: string;
|
|
9
|
+
} | undefined>;
|
|
10
|
+
type PageContextCspNonce = Pick<PageContextServer, 'cspNonce'>;
|
|
11
|
+
declare function inferNonceAttr(pageContext: PageContextCspNonce): string;
|
|
12
|
+
declare function addCspHeader(pageContext: PageContextCspNonce, headersResponse: Headers): void;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export { resolvePageContextCspNone };
|
|
2
|
+
export { inferNonceAttr };
|
|
3
|
+
export { addCspHeader };
|
|
4
|
+
import { import_ } from '@brillout/import';
|
|
5
|
+
async function resolvePageContextCspNone(pageContext) {
|
|
6
|
+
if (pageContext.cspNonce)
|
|
7
|
+
return; // already set by user e.g. `renderPage({ cspNonce: '123456789' })`
|
|
8
|
+
const { csp } = pageContext.config;
|
|
9
|
+
if (!csp?.nonce)
|
|
10
|
+
return;
|
|
11
|
+
let cspNonce;
|
|
12
|
+
if (csp.nonce === true) {
|
|
13
|
+
cspNonce = await generateNonce();
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
cspNonce = await csp.nonce(pageContext);
|
|
17
|
+
}
|
|
18
|
+
const pageContextAddendum = { cspNonce };
|
|
19
|
+
return pageContextAddendum;
|
|
20
|
+
}
|
|
21
|
+
// Generate a cryptographically secure nonce for Content Security Policy (CSP).
|
|
22
|
+
// Returns a base64url-encoded nonce string (URL-safe, no padding).
|
|
23
|
+
// https://github.com/vikejs/vike/issues/1554#issuecomment-3181128304
|
|
24
|
+
async function generateNonce() {
|
|
25
|
+
let cryptoModule;
|
|
26
|
+
try {
|
|
27
|
+
cryptoModule = (await import_('crypto')).default;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return Math.random().toString(36).substring(2, 18);
|
|
31
|
+
}
|
|
32
|
+
return cryptoModule.randomBytes(16).toString('base64url');
|
|
33
|
+
}
|
|
34
|
+
function inferNonceAttr(pageContext) {
|
|
35
|
+
const nonceAttr = pageContext.cspNonce ? ` nonce="${pageContext.cspNonce}"` : '';
|
|
36
|
+
return nonceAttr;
|
|
37
|
+
}
|
|
38
|
+
function addCspHeader(pageContext, headersResponse) {
|
|
39
|
+
if (!pageContext.cspNonce)
|
|
40
|
+
return;
|
|
41
|
+
if (headersResponse.get('Content-Security-Policy'))
|
|
42
|
+
return;
|
|
43
|
+
headersResponse.set('Content-Security-Policy', `script-src 'self' 'nonce-${pageContext.cspNonce}'`);
|
|
44
|
+
}
|
|
@@ -10,6 +10,7 @@ import pc from '@brillout/picocolors';
|
|
|
10
10
|
import { getConfigDefinedAt } from '../../../../shared/page-configs/getConfigDefinedAt.js';
|
|
11
11
|
import { htmlElementId_globalContext, htmlElementId_pageContext } from '../../../../shared/htmlElementIds.js';
|
|
12
12
|
import { isFontFallback } from '../../renderPage/isFontFallback.js';
|
|
13
|
+
import { inferNonceAttr } from '../../csp.js';
|
|
13
14
|
const stamp = '__injectFilterEntry';
|
|
14
15
|
async function getHtmlTags(pageContext, streamFromReactStreamingPackage, injectFilter, pageAssets, viteDevScript, isStream) {
|
|
15
16
|
assert([true, false].includes(pageContext._isHtmlOnly));
|
|
@@ -76,7 +77,7 @@ async function getHtmlTags(pageContext, streamFromReactStreamingPackage, injectF
|
|
|
76
77
|
.forEach((asset) => {
|
|
77
78
|
if (!asset.inject)
|
|
78
79
|
return;
|
|
79
|
-
const htmlTag = asset.isEntry ? inferAssetTag(asset) : inferPreloadTag(asset);
|
|
80
|
+
const htmlTag = asset.isEntry ? inferAssetTag(asset, pageContext) : inferPreloadTag(asset);
|
|
80
81
|
htmlTags.push({ htmlTag, position: asset.inject });
|
|
81
82
|
});
|
|
82
83
|
// ==========
|
|
@@ -137,7 +138,7 @@ async function getHtmlTags(pageContext, streamFromReactStreamingPackage, injectF
|
|
|
137
138
|
});
|
|
138
139
|
}
|
|
139
140
|
// The JavaScript entry <script> tag
|
|
140
|
-
const scriptEntry = mergeScriptEntries(pageAssets, viteDevScript);
|
|
141
|
+
const scriptEntry = mergeScriptEntries(pageAssets, viteDevScript, pageContext);
|
|
141
142
|
if (scriptEntry) {
|
|
142
143
|
htmlTags.push({
|
|
143
144
|
htmlTag: scriptEntry,
|
|
@@ -158,9 +159,9 @@ async function getHtmlTags(pageContext, streamFromReactStreamingPackage, injectF
|
|
|
158
159
|
});
|
|
159
160
|
return htmlTags;
|
|
160
161
|
}
|
|
161
|
-
function mergeScriptEntries(pageAssets, viteDevScript) {
|
|
162
|
+
function mergeScriptEntries(pageAssets, viteDevScript, pageContext) {
|
|
162
163
|
const scriptEntries = pageAssets.filter((pageAsset) => pageAsset.isEntry && pageAsset.assetType === 'script');
|
|
163
|
-
let scriptEntry = `${viteDevScript}${scriptEntries.map((asset) => inferAssetTag(asset)).join('')}`;
|
|
164
|
+
let scriptEntry = `${viteDevScript}${scriptEntries.map((asset) => inferAssetTag(asset, pageContext)).join('')}`;
|
|
164
165
|
// We merge scripts to avoid the infamous HMR preamble error.
|
|
165
166
|
// - Infamous HMR preamble error:
|
|
166
167
|
// ```browser-console
|
|
@@ -178,12 +179,13 @@ function mergeScriptEntries(pageAssets, viteDevScript) {
|
|
|
178
179
|
// ```
|
|
179
180
|
// - Maybe an alternative would be to make Vike's client runtime entry <script> tag non-async. Would that work? Would it be a performance issue?
|
|
180
181
|
// - The entry <script> shouldn't be `<script defer>` upon HTML streaming, otherwise progressive hydration while SSR streaming won't work.
|
|
181
|
-
scriptEntry = mergeScriptTags(scriptEntry);
|
|
182
|
+
scriptEntry = mergeScriptTags(scriptEntry, pageContext);
|
|
182
183
|
return scriptEntry;
|
|
183
184
|
}
|
|
184
185
|
function getPageContextJsonScriptTag(pageContext) {
|
|
185
186
|
const pageContextClientSerialized = sanitizeJson(getPageContextClientSerialized(pageContext, true));
|
|
186
|
-
const
|
|
187
|
+
const nonceAttr = inferNonceAttr(pageContext);
|
|
188
|
+
const htmlTag = `<script id="${htmlElementId_pageContext}" type="application/json"${nonceAttr}>${pageContextClientSerialized}</script>`;
|
|
187
189
|
// Used by contra.com https://github.com/gajus
|
|
188
190
|
// @ts-expect-error
|
|
189
191
|
pageContext._pageContextHtmlTag = htmlTag;
|
|
@@ -191,7 +193,8 @@ function getPageContextJsonScriptTag(pageContext) {
|
|
|
191
193
|
}
|
|
192
194
|
function getGlobalContextJsonScriptTag(pageContext) {
|
|
193
195
|
const globalContextClientSerialized = sanitizeJson(getGlobalContextClientSerialized(pageContext, true));
|
|
194
|
-
const
|
|
196
|
+
const nonceAttr = inferNonceAttr(pageContext);
|
|
197
|
+
const htmlTag = `<script id="${htmlElementId_globalContext}" type="application/json"${nonceAttr}>${globalContextClientSerialized}</script>`;
|
|
195
198
|
return htmlTag;
|
|
196
199
|
}
|
|
197
200
|
function assertInjectFilterEntries(injectFilterEntries) {
|
|
@@ -3,7 +3,8 @@ export { inferPreloadTag };
|
|
|
3
3
|
export { inferEarlyHintLink };
|
|
4
4
|
export { scriptAttrs };
|
|
5
5
|
import type { PageAsset } from '../../renderPage/getPageAssets.js';
|
|
6
|
+
import { type PageContextCspNonce } from '../../csp.js';
|
|
6
7
|
declare const scriptAttrs = "type=\"module\" async";
|
|
7
8
|
declare function inferPreloadTag(pageAsset: PageAsset): string;
|
|
8
|
-
declare function inferAssetTag(pageAsset: PageAsset): string;
|
|
9
|
+
declare function inferAssetTag(pageAsset: PageAsset, pageContext: PageContextCspNonce): string;
|
|
9
10
|
declare function inferEarlyHintLink(pageAsset: PageAsset): string;
|
|
@@ -3,6 +3,8 @@ export { inferPreloadTag };
|
|
|
3
3
|
export { inferEarlyHintLink };
|
|
4
4
|
export { scriptAttrs };
|
|
5
5
|
import { assert } from '../../utils.js';
|
|
6
|
+
import { inferNonceAttr } from '../../csp.js';
|
|
7
|
+
// TODO/now rename scriptAttrs scriptCommonAttrs
|
|
6
8
|
// We can't use `defer` here. With `defer`, the entry script won't start before `</body>` has been parsed, preventing progressive hydration during SSR streaming, see https://github.com/vikejs/vike/pull/1271
|
|
7
9
|
const scriptAttrs = 'type="module" async';
|
|
8
10
|
function inferPreloadTag(pageAsset) {
|
|
@@ -20,11 +22,12 @@ function inferPreloadTag(pageAsset) {
|
|
|
20
22
|
.join(' ');
|
|
21
23
|
return `<link ${attributes}>`;
|
|
22
24
|
}
|
|
23
|
-
function inferAssetTag(pageAsset) {
|
|
25
|
+
function inferAssetTag(pageAsset, pageContext) {
|
|
24
26
|
const { src, assetType, mediaType } = pageAsset;
|
|
25
27
|
if (assetType === 'script') {
|
|
26
28
|
assert(mediaType === 'text/javascript');
|
|
27
|
-
|
|
29
|
+
const nonceAttr = inferNonceAttr(pageContext);
|
|
30
|
+
return `<script src="${src}" ${scriptAttrs}${nonceAttr}></script>`;
|
|
28
31
|
}
|
|
29
32
|
if (assetType === 'style') {
|
|
30
33
|
// WARNING: if changing following line, then also update https://github.com/vikejs/vike/blob/fae90a15d88e5e87ca9fcbb54cf2dc8773d2f229/vike/client/shared/removeFoucBuster.ts#L29
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
export { mergeScriptTags };
|
|
2
|
+
import { inferNonceAttr } from '../../csp.js';
|
|
2
3
|
import { assert } from '../../utils.js';
|
|
3
4
|
import { scriptAttrs } from './inferHtmlTags.js';
|
|
4
5
|
const scriptRE = /(<script\b(?:\s[^>]*>|>))(.*?)<\/script>/gims;
|
|
5
6
|
const srcRE = /\bsrc\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s'">]+))/im;
|
|
6
7
|
const typeRE = /\btype\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s'">]+))/im;
|
|
7
|
-
function mergeScriptTags(scriptTagsHtml) {
|
|
8
|
+
function mergeScriptTags(scriptTagsHtml, pageContext) {
|
|
8
9
|
let scriptTag = '';
|
|
9
10
|
const scripts = parseScripts(scriptTagsHtml);
|
|
10
11
|
// We need to merge module scripts to ensure execution order
|
|
@@ -33,7 +34,8 @@ function mergeScriptTags(scriptTagsHtml) {
|
|
|
33
34
|
}
|
|
34
35
|
});
|
|
35
36
|
if (contents.length > 0) {
|
|
36
|
-
|
|
37
|
+
const nonceAttr = inferNonceAttr(pageContext);
|
|
38
|
+
scriptTag += `<script ${scriptAttrs}${nonceAttr}>\n${contents.join('\n')}\n</script>`;
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
}
|
|
@@ -8,6 +8,7 @@ import type { UrlRedirect } from '../../../shared/route/abort.js';
|
|
|
8
8
|
import type { GlobalContextServerInternal } from '../globalContext.js';
|
|
9
9
|
import type { PageContextCreated } from '../renderPage/createPageContextServerSide.js';
|
|
10
10
|
import type { PageContextBegin } from '../renderPage.js';
|
|
11
|
+
import type { PageContextCspNonce } from '../csp.js';
|
|
11
12
|
type PageContextSerialization = PageContextCreated & {
|
|
12
13
|
pageId: string;
|
|
13
14
|
routeParams: Record<string, string>;
|
|
@@ -17,7 +18,7 @@ type PageContextSerialization = PageContextCreated & {
|
|
|
17
18
|
_pageContextInit: Record<string, unknown>;
|
|
18
19
|
_globalContext: GlobalContextServerInternal;
|
|
19
20
|
_isPageContextJsonRequest: null | PageContextBegin['_isPageContextJsonRequest'];
|
|
20
|
-
};
|
|
21
|
+
} & PageContextCspNonce;
|
|
21
22
|
declare function getPageContextClientSerialized(pageContext: PageContextSerialization, isHtmlJsonScript: boolean): string;
|
|
22
23
|
declare function getGlobalContextClientSerialized(pageContext: PageContextSerialization, isHtmlJsonScript: boolean): string;
|
|
23
24
|
type PassToClient = string[];
|
|
@@ -111,6 +111,8 @@ declare function loadPageConfigsLazyServerSide(pageContext: PageContext_loadPage
|
|
|
111
111
|
_pageConfig: null | PageConfigRuntime;
|
|
112
112
|
} & VikeConfigPublicPageLazyLoaded & {
|
|
113
113
|
_pageConfig: null | PageConfigRuntime;
|
|
114
|
+
} & {
|
|
115
|
+
cspNonce: string;
|
|
114
116
|
} & {
|
|
115
117
|
Page: unknown;
|
|
116
118
|
_isHtmlOnly: boolean;
|
|
@@ -9,6 +9,7 @@ import { analyzePage } from './analyzePage.js';
|
|
|
9
9
|
import { loadAndParseVirtualFilePageEntry } from '../../../shared/page-configs/loadAndParseVirtualFilePageEntry.js';
|
|
10
10
|
import { execHookServer } from './execHookServer.js';
|
|
11
11
|
import { getCacheControl } from './getCacheControl.js';
|
|
12
|
+
import { addCspHeader, resolvePageContextCspNone } from '../csp.js';
|
|
12
13
|
async function loadPageConfigsLazyServerSide(pageContext) {
|
|
13
14
|
objectAssign(pageContext, {
|
|
14
15
|
_pageConfig: findPageConfig(pageContext._globalContext._pageConfigs, pageContext.pageId),
|
|
@@ -53,6 +54,7 @@ async function resolvePageContext(pageContext) {
|
|
|
53
54
|
passToClient.push(...valS);
|
|
54
55
|
});
|
|
55
56
|
}
|
|
57
|
+
objectAssign(pageContext, await resolvePageContextCspNone(pageContext));
|
|
56
58
|
objectAssign(pageContext, {
|
|
57
59
|
Page: pageContext.exports.Page,
|
|
58
60
|
_isHtmlOnly: isHtmlOnly,
|
|
@@ -123,6 +125,7 @@ async function loadPageUserFiles_v1Design(pageContext) {
|
|
|
123
125
|
pageFilesLoaded: pageFilesServerSide,
|
|
124
126
|
};
|
|
125
127
|
}
|
|
128
|
+
// TODO/now: move all response headers code to headersResponse.ts
|
|
126
129
|
function resolveHeadersResponse(pageContext) {
|
|
127
130
|
const headersResponse = mergeHeaders(pageContext.config.headersResponse);
|
|
128
131
|
if (!headersResponse.get('Cache-Control')) {
|
|
@@ -130,6 +133,7 @@ function resolveHeadersResponse(pageContext) {
|
|
|
130
133
|
if (cacheControl)
|
|
131
134
|
headersResponse.set('Cache-Control', cacheControl);
|
|
132
135
|
}
|
|
136
|
+
addCspHeader(pageContext, headersResponse);
|
|
133
137
|
return headersResponse;
|
|
134
138
|
}
|
|
135
139
|
function mergeHeaders(headersList = []) {
|
|
@@ -127,6 +127,8 @@ declare function prerenderPage(pageContext: PageContextCreated & PageConfigsLazy
|
|
|
127
127
|
_pageConfig: null | import("../../../types/PageConfig.js").PageConfigRuntime;
|
|
128
128
|
} & import("../../../shared/getPageFiles.js").VikeConfigPublicPageLazyLoaded & {
|
|
129
129
|
_pageConfig: null | import("../../../types/PageConfig.js").PageConfigRuntime;
|
|
130
|
+
} & {
|
|
131
|
+
cspNonce: string;
|
|
130
132
|
} & {
|
|
131
133
|
Page: unknown;
|
|
132
134
|
_isHtmlOnly: boolean;
|
|
@@ -246,6 +248,8 @@ declare function prerenderPage(pageContext: PageContextCreated & PageConfigsLazy
|
|
|
246
248
|
_pageConfig: null | import("../../../types/PageConfig.js").PageConfigRuntime;
|
|
247
249
|
} & import("../../../shared/getPageFiles.js").VikeConfigPublicPageLazyLoaded & {
|
|
248
250
|
_pageConfig: null | import("../../../types/PageConfig.js").PageConfigRuntime;
|
|
251
|
+
} & {
|
|
252
|
+
cspNonce: string;
|
|
249
253
|
} & {
|
|
250
254
|
Page: unknown;
|
|
251
255
|
_isHtmlOnly: boolean;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
//
|
|
1
|
+
// TODO/now: rename PageConfig names
|
|
2
2
|
// - Use `Internal` suffix, i.e. {Page,Global}ConfigInternal
|
|
3
3
|
// - While keeping {Page,Global}ConfigPublic or remove Public suffix and rename it to {Page,Global}Config ?
|
|
4
4
|
// - rename EagerLoaded EagerlyLoaded
|
|
5
5
|
// - remove `LazyLoaded` suffix
|
|
6
|
+
// TODO/now: rename VikeConfigPublicPageLazyLoaded PageContextSomething (for `pageContext: PageContextSomething` usage)
|
|
6
7
|
// TO-DO/soon/same-api: use public API internally?
|
|
7
8
|
// TO-DO/soon/flat-pageContext: rename definedAt => definedBy
|
|
8
9
|
export { resolveVikeConfigPublicGlobal };
|
|
@@ -468,6 +468,14 @@ type ConfigBuiltIn = {
|
|
|
468
468
|
* https://vike.dev/mode
|
|
469
469
|
*/
|
|
470
470
|
mode?: string;
|
|
471
|
+
/**
|
|
472
|
+
* Content Security Policy (CSP).
|
|
473
|
+
*
|
|
474
|
+
* https://vike.dev/csp
|
|
475
|
+
*/
|
|
476
|
+
csp?: {
|
|
477
|
+
nonce: boolean | ((pageContext: PageContextServer) => string | Promise<string>);
|
|
478
|
+
};
|
|
471
479
|
/** Where scripts are injected in the HTML.
|
|
472
480
|
*
|
|
473
481
|
* https://vike.dev/injectScriptsAt
|
|
@@ -183,6 +183,12 @@ type PageContextBuiltInServer<Data> = PageContextBuiltInCommon<Data> & PageConte
|
|
|
183
183
|
* https://vike.dev/pageContext#globalContext
|
|
184
184
|
*/
|
|
185
185
|
globalContext: GlobalContextServer;
|
|
186
|
+
/**
|
|
187
|
+
* The CSP nonce.
|
|
188
|
+
*
|
|
189
|
+
* https://vike.dev/csp
|
|
190
|
+
*/
|
|
191
|
+
cspNonce?: string;
|
|
186
192
|
isHydration?: undefined;
|
|
187
193
|
isBackwardNavigation?: undefined;
|
|
188
194
|
previousPageContext?: undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PROJECT_VERSION: "0.4.
|
|
1
|
+
export declare const PROJECT_VERSION: "0.4.238-commit-5762291";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Automatically updated by @brillout/release-me
|
|
2
|
-
export const PROJECT_VERSION = '0.4.
|
|
2
|
+
export const PROJECT_VERSION = '0.4.238-commit-5762291';
|