vike 0.4.179-commit-9384166 → 0.4.179-commit-fd426a3
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/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.js +3 -0
- package/dist/cjs/node/runtime/html/injectAssets/getHtmlTags.js +46 -15
- package/dist/cjs/node/runtime/html/injectAssets/injectHtmlTags.js +2 -1
- package/dist/cjs/node/runtime/html/injectAssets.js +8 -2
- package/dist/cjs/node/runtime/html/renderHtml.js +10 -1
- package/dist/cjs/node/runtime/html/stream.js +11 -5
- package/dist/cjs/shared/page-configs/serialize/serializeConfigValues.js +4 -0
- package/dist/cjs/utils/projectInfo.js +1 -1
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.js +3 -0
- package/dist/esm/node/runtime/html/injectAssets/getHtmlTags.d.ts +3 -2
- package/dist/esm/node/runtime/html/injectAssets/getHtmlTags.js +46 -15
- package/dist/esm/node/runtime/html/injectAssets/injectHtmlTags.d.ts +2 -0
- package/dist/esm/node/runtime/html/injectAssets/injectHtmlTags.js +1 -0
- package/dist/esm/node/runtime/html/injectAssets.d.ts +1 -0
- package/dist/esm/node/runtime/html/injectAssets.js +9 -3
- package/dist/esm/node/runtime/html/renderHtml.js +10 -1
- package/dist/esm/node/runtime/html/stream.d.ts +2 -1
- package/dist/esm/node/runtime/html/stream.js +11 -5
- package/dist/esm/shared/page-configs/Config.d.ts +5 -0
- package/dist/esm/shared/page-configs/serialize/serializeConfigValues.js +4 -0
- package/dist/esm/utils/projectInfo.d.ts +2 -2
- package/dist/esm/utils/projectInfo.js +1 -1
- package/package.json +1 -1
|
@@ -9,13 +9,17 @@ const serializePageContextClientSide_js_1 = require("../serializePageContextClie
|
|
|
9
9
|
const sanitizeJson_js_1 = require("./sanitizeJson.js");
|
|
10
10
|
const inferHtmlTags_js_1 = require("./inferHtmlTags.js");
|
|
11
11
|
const mergeScriptTags_js_1 = require("./mergeScriptTags.js");
|
|
12
|
+
const helpers_js_1 = require("../../../../shared/page-configs/helpers.js");
|
|
13
|
+
const getConfigValue_js_1 = require("../../../../shared/page-configs/getConfigValue.js");
|
|
12
14
|
const globalContext_js_1 = require("../../globalContext.js");
|
|
13
15
|
const picocolors_1 = __importDefault(require("@brillout/picocolors"));
|
|
16
|
+
const getConfigDefinedAt_js_1 = require("../../../../shared/page-configs/getConfigDefinedAt.js");
|
|
14
17
|
const stamp = '__injectFilterEntry';
|
|
15
|
-
function getHtmlTags(pageContext, streamFromReactStreamingPackage, injectFilter, pageAssets, viteDevScript) {
|
|
18
|
+
function getHtmlTags(pageContext, streamFromReactStreamingPackage, injectFilter, pageAssets, viteDevScript, isStream) {
|
|
16
19
|
(0, utils_js_1.assert)([true, false].includes(pageContext._isHtmlOnly));
|
|
17
20
|
const isHtmlOnly = pageContext._isHtmlOnly;
|
|
18
21
|
const { isProduction } = (0, globalContext_js_1.getGlobalContext)();
|
|
22
|
+
const injectScriptsAt = getInjectScriptsAt(pageContext._pageId, pageContext._pageConfigs);
|
|
19
23
|
const injectFilterEntries = pageAssets
|
|
20
24
|
.filter((asset) => {
|
|
21
25
|
if (asset.isEntry && asset.assetType === 'script') {
|
|
@@ -78,28 +82,39 @@ function getHtmlTags(pageContext, streamFromReactStreamingPackage, injectFilter,
|
|
|
78
82
|
// ==========
|
|
79
83
|
// JavaScript
|
|
80
84
|
// ==========
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
// See https://github.com/vikejs/vike/pull/1271
|
|
85
|
+
// - The pageContext JSON should be fully sent before Vike's client runtime starts executing.
|
|
86
|
+
// - Otherwise, race condition "SyntaxError: Unterminated string in JSON": https://github.com/vikejs/vike/issues/567
|
|
87
|
+
// - <script id="vike_pageContext" type="application/json"> must appear before the entry <script> (which loads Vike's client runtime).
|
|
88
|
+
// - <script id="vike_pageContext" type="application/json"> can't be async nor defer.
|
|
89
|
+
// - The entry <script> can't be defer, otherwise progressive hydration while SSR streaming won't work.
|
|
90
|
+
// - The entry <script> should be towards the end of the HTML as performance-wise it's more interesting to parse <div id="page-view"> before running the entry <script> which initiates the hydration.
|
|
91
|
+
// - But with HTML streaming, in order to support [Progressive Rendering](https://vike.dev/streaming#progressive-rendering), the entry <script> should be injected early instead.
|
|
89
92
|
const positionJavaScriptEntry = (() => {
|
|
93
|
+
if (injectScriptsAt !== null) {
|
|
94
|
+
if (pageContext._pageContextPromise) {
|
|
95
|
+
(0, utils_js_1.assertWarning)(injectScriptsAt === 'HTML_END' || !isStream, `You're setting injectScriptsAt to ${picocolors_1.default.code(JSON.stringify(injectScriptsAt))} while using HTML streaming with a pageContext promise (https://vike.dev/streaming#initial-data-after-stream-end) which is contradictory: the pageContext promise is skipped.`, { onlyOnce: true });
|
|
96
|
+
}
|
|
97
|
+
return injectScriptsAt;
|
|
98
|
+
}
|
|
90
99
|
if (pageContext._pageContextPromise) {
|
|
91
|
-
|
|
92
|
-
//
|
|
100
|
+
// - If there is a pageContext._pageContextPromise then <script id="vike_pageContext" type="application/json"> needs to await for it.
|
|
101
|
+
// - pageContext._pageContextPromise is typically resolved only after the page's components are rendered and the stream ended.
|
|
102
|
+
// - https://vike.dev/streaming#initial-data-after-stream-end
|
|
93
103
|
return 'HTML_END';
|
|
94
104
|
}
|
|
95
105
|
if (streamFromReactStreamingPackage && !streamFromReactStreamingPackage.hasStreamEnded()) {
|
|
96
|
-
// If there is a stream then, in order to support progressive hydration, inject the JavaScript during the stream after React(/Vue/Solid/...) resolved the first suspense boundary
|
|
106
|
+
// If there is a stream then, in order to support progressive hydration, inject the JavaScript during the stream after React(/Vue/Solid/...) resolved the first suspense boundary.
|
|
97
107
|
return 'STREAM';
|
|
98
108
|
}
|
|
99
|
-
|
|
100
|
-
return 'HTML_END';
|
|
101
|
-
}
|
|
109
|
+
return 'HTML_END';
|
|
102
110
|
})();
|
|
111
|
+
if (pageContext._pageContextPromise && streamFromReactStreamingPackage) {
|
|
112
|
+
// - Should we show this warning for Solid as well? Solid seems to also support progressive rendering.
|
|
113
|
+
// - https://github.com/vikejs/vike-solid/issues/95
|
|
114
|
+
// - Vue doesn't seem to support progressive rendering yet.
|
|
115
|
+
// - https://github.com/vikejs/vike-vue/issues/85
|
|
116
|
+
(0, utils_js_1.assertWarning)(false, "We recommend against using HTML streaming and a pageContext promise (https://vike.dev/streaming#initial-data-after-stream-end) at the same time, because progressive hydration (https://vike.dev/streaming#progressive-rendering) won't work.", { onlyOnce: true });
|
|
117
|
+
}
|
|
103
118
|
// <script id="vike_pageContext" type="application/json">
|
|
104
119
|
if (!isHtmlOnly) {
|
|
105
120
|
htmlTags.push({
|
|
@@ -195,3 +210,19 @@ function checkForWarnings(injectFilterEntries) {
|
|
|
195
210
|
}
|
|
196
211
|
});
|
|
197
212
|
}
|
|
213
|
+
function getInjectScriptsAt(pageId, pageConfigs) {
|
|
214
|
+
if (pageConfigs.length === 0)
|
|
215
|
+
return null; // only support V1 design
|
|
216
|
+
const pageConfig = (0, helpers_js_1.getPageConfig)(pageId, pageConfigs);
|
|
217
|
+
const configValue = (0, getConfigValue_js_1.getConfigValueRuntime)(pageConfig, 'injectScriptsAt');
|
|
218
|
+
if (!configValue)
|
|
219
|
+
return null;
|
|
220
|
+
const injectScriptsAt = configValue.value;
|
|
221
|
+
(0, utils_js_1.assert)(configValue.definedAtData);
|
|
222
|
+
const configDefinedAt = (0, getConfigDefinedAt_js_1.getConfigDefinedAt)('Config', 'injectScriptsAt', configValue.definedAtData);
|
|
223
|
+
(0, utils_js_1.assertUsage)(injectScriptsAt === null ||
|
|
224
|
+
injectScriptsAt === 'HTML_BEGIN' ||
|
|
225
|
+
injectScriptsAt === 'HTML_END' ||
|
|
226
|
+
injectScriptsAt === 'STREAM', `${configDefinedAt} has an invalid value`);
|
|
227
|
+
return injectScriptsAt;
|
|
228
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// Unit tests at ./injectHtmlTags.spec.ts
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.injectAtClosingTag = exports.injectAtOpeningTag = exports.createHtmlHeadIfMissing = exports.injectHtmlTagsUsingStream = exports.injectHtmlTags = void 0;
|
|
4
|
+
exports.injectAtClosingTag = exports.injectAtOpeningTag = exports.joinHtmlTags = exports.createHtmlHeadIfMissing = exports.injectHtmlTagsUsingStream = exports.injectHtmlTags = void 0;
|
|
5
5
|
const utils_js_1 = require("../../utils.js");
|
|
6
6
|
function injectHtmlTags(htmlString, htmlTags, position) {
|
|
7
7
|
const htmlFragment = joinHtmlTags(htmlTags.filter((h) => h.position === position));
|
|
@@ -24,6 +24,7 @@ function joinHtmlTags(htmlTags) {
|
|
|
24
24
|
const htmlFragment = htmlTags.map((h) => resolveHtmlTag(h.htmlTag)).join('');
|
|
25
25
|
return htmlFragment;
|
|
26
26
|
}
|
|
27
|
+
exports.joinHtmlTags = joinHtmlTags;
|
|
27
28
|
function injectHtmlFragment(position, htmlFragment, htmlString) {
|
|
28
29
|
if (position === 'HTML_BEGIN') {
|
|
29
30
|
{
|
|
@@ -9,7 +9,7 @@ const getViteDevScript_js_1 = require("./injectAssets/getViteDevScript.js");
|
|
|
9
9
|
async function injectHtmlTagsToString(htmlParts, pageContext, injectFilter) {
|
|
10
10
|
const pageAssets = await pageContext.__getPageAssets();
|
|
11
11
|
const viteDevScript = await (0, getViteDevScript_js_1.getViteDevScript)();
|
|
12
|
-
const htmlTags = (0, getHtmlTags_js_1.getHtmlTags)(pageContext, null, injectFilter, pageAssets, viteDevScript);
|
|
12
|
+
const htmlTags = (0, getHtmlTags_js_1.getHtmlTags)(pageContext, null, injectFilter, pageAssets, viteDevScript, false);
|
|
13
13
|
let htmlString = htmlPartsToString(htmlParts, pageAssets);
|
|
14
14
|
htmlString = injectToHtmlBegin(htmlString, htmlTags);
|
|
15
15
|
htmlString = injectToHtmlEnd(htmlString, htmlTags);
|
|
@@ -21,17 +21,23 @@ function injectHtmlTagsToStream(pageContext, streamFromReactStreamingPackage, in
|
|
|
21
21
|
let htmlTags;
|
|
22
22
|
return {
|
|
23
23
|
injectAtStreamBegin,
|
|
24
|
+
injectAtStreamAfterFirstChunk,
|
|
24
25
|
injectAtStreamEnd
|
|
25
26
|
};
|
|
26
27
|
async function injectAtStreamBegin(htmlPartsBegin) {
|
|
27
28
|
const pageAssets = await pageContext.__getPageAssets();
|
|
28
29
|
const viteDevScript = await (0, getViteDevScript_js_1.getViteDevScript)();
|
|
29
|
-
htmlTags = (0, getHtmlTags_js_1.getHtmlTags)(pageContext, streamFromReactStreamingPackage, injectFilter, pageAssets, viteDevScript);
|
|
30
|
+
htmlTags = (0, getHtmlTags_js_1.getHtmlTags)(pageContext, streamFromReactStreamingPackage, injectFilter, pageAssets, viteDevScript, true);
|
|
30
31
|
let htmlBegin = htmlPartsToString(htmlPartsBegin, pageAssets);
|
|
31
32
|
htmlBegin = injectToHtmlBegin(htmlBegin, htmlTags);
|
|
32
33
|
(0, injectHtmlTags_js_1.injectHtmlTagsUsingStream)(htmlTags, streamFromReactStreamingPackage);
|
|
33
34
|
return htmlBegin;
|
|
34
35
|
}
|
|
36
|
+
function injectAtStreamAfterFirstChunk() {
|
|
37
|
+
(0, utils_js_1.assert)(htmlTags);
|
|
38
|
+
const htmlFragment = (0, injectHtmlTags_js_1.joinHtmlTags)(htmlTags.filter((h) => h.position === 'STREAM'));
|
|
39
|
+
return htmlFragment;
|
|
40
|
+
}
|
|
35
41
|
async function injectAtStreamEnd(htmlPartsEnd) {
|
|
36
42
|
(0, utils_js_1.assert)(htmlTags);
|
|
37
43
|
await resolvePageContextPromise(pageContext);
|
|
@@ -64,7 +64,7 @@ async function renderHtmlStream(streamOriginal, injectString, pageContext, onErr
|
|
|
64
64
|
if ((0, react_streaming_js_1.isStreamFromReactStreamingPackage)(streamOriginal) && !streamOriginal.disabled) {
|
|
65
65
|
streamFromReactStreamingPackage = streamOriginal;
|
|
66
66
|
}
|
|
67
|
-
const { injectAtStreamBegin, injectAtStreamEnd } = (0, injectAssets_js_1.injectHtmlTagsToStream)(pageContext, streamFromReactStreamingPackage, injectFilter);
|
|
67
|
+
const { injectAtStreamBegin, injectAtStreamAfterFirstChunk, injectAtStreamEnd } = (0, injectAssets_js_1.injectHtmlTagsToStream)(pageContext, streamFromReactStreamingPackage, injectFilter);
|
|
68
68
|
(0, utils_js_1.objectAssign)(opts, {
|
|
69
69
|
injectStringAtBegin: async () => {
|
|
70
70
|
return await injectAtStreamBegin(injectString.htmlPartsBegin);
|
|
@@ -73,6 +73,15 @@ async function renderHtmlStream(streamOriginal, injectString, pageContext, onErr
|
|
|
73
73
|
return await injectAtStreamEnd(injectString.htmlPartsEnd);
|
|
74
74
|
}
|
|
75
75
|
});
|
|
76
|
+
if (
|
|
77
|
+
// React needs its own chunk stream injection mechanism
|
|
78
|
+
!react_streaming_js_1.isStreamFromReactStreamingPackage) {
|
|
79
|
+
(0, utils_js_1.objectAssign)(opts, {
|
|
80
|
+
injectStringAfterFirstChunk: () => {
|
|
81
|
+
return injectAtStreamAfterFirstChunk();
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
76
85
|
}
|
|
77
86
|
const streamWrapper = await (0, stream_js_1.processStream)(streamOriginal, opts);
|
|
78
87
|
return streamWrapper;
|
|
@@ -198,7 +198,7 @@ function pipeToStreamWritableNode(htmlRender, writable) {
|
|
|
198
198
|
(0, utils_js_1.assert)(false);
|
|
199
199
|
}
|
|
200
200
|
exports.pipeToStreamWritableNode = pipeToStreamWritableNode;
|
|
201
|
-
async function processStream(streamOriginal, { injectStringAtBegin, injectStringAtEnd, onErrorWhileStreaming, enableEagerStreaming }) {
|
|
201
|
+
async function processStream(streamOriginal, { injectStringAtBegin, injectStringAfterFirstChunk, injectStringAtEnd, onErrorWhileStreaming, enableEagerStreaming }) {
|
|
202
202
|
const buffer = [];
|
|
203
203
|
let streamOriginalHasStartedEmitting = false;
|
|
204
204
|
let streamOriginalEnded = false;
|
|
@@ -210,6 +210,7 @@ async function processStream(streamOriginal, { injectStringAtBegin, injectString
|
|
|
210
210
|
let resolve;
|
|
211
211
|
let reject;
|
|
212
212
|
let promiseHasResolved = false;
|
|
213
|
+
let injectStringAfterFirstChunk_done = false;
|
|
213
214
|
const streamWrapperPromise = new Promise((resolve_, reject_) => {
|
|
214
215
|
resolve = (streamWrapper) => {
|
|
215
216
|
promiseHasResolved = true;
|
|
@@ -223,8 +224,8 @@ async function processStream(streamOriginal, { injectStringAtBegin, injectString
|
|
|
223
224
|
let resolveReadyToWrite;
|
|
224
225
|
const promiseReadyToWrite = new Promise((r) => (resolveReadyToWrite = r));
|
|
225
226
|
if (injectStringAtBegin) {
|
|
226
|
-
const
|
|
227
|
-
writeStream(
|
|
227
|
+
const injectedChunk = await injectStringAtBegin();
|
|
228
|
+
writeStream(injectedChunk); // Adds injectedChunk to buffer
|
|
228
229
|
flushStream(); // Sets shouldFlushStream to true
|
|
229
230
|
}
|
|
230
231
|
// We call onStreamEvent() also when the stream ends in order to properly handle the situation when the stream didn't emit any data
|
|
@@ -254,6 +255,11 @@ async function processStream(streamOriginal, { injectStringAtBegin, injectString
|
|
|
254
255
|
onData(chunk) {
|
|
255
256
|
onStreamDataOrEnd(() => {
|
|
256
257
|
writeStream(chunk);
|
|
258
|
+
if (injectStringAfterFirstChunk && !injectStringAfterFirstChunk_done) {
|
|
259
|
+
const injectedChunk = injectStringAfterFirstChunk();
|
|
260
|
+
writeStream(injectedChunk);
|
|
261
|
+
injectStringAfterFirstChunk_done = true;
|
|
262
|
+
}
|
|
257
263
|
});
|
|
258
264
|
},
|
|
259
265
|
async onEnd(
|
|
@@ -268,8 +274,8 @@ async function processStream(streamOriginal, { injectStringAtBegin, injectString
|
|
|
268
274
|
streamOriginalEnded = true;
|
|
269
275
|
});
|
|
270
276
|
if (injectStringAtEnd) {
|
|
271
|
-
const
|
|
272
|
-
writeStream(
|
|
277
|
+
const injectedChunk = await injectStringAtEnd();
|
|
278
|
+
writeStream(injectedChunk);
|
|
273
279
|
}
|
|
274
280
|
await promiseReadyToWrite; // E.g. if the user calls the pipe wrapper after the original writable has ended
|
|
275
281
|
(0, utils_js_1.assert)(isReady());
|
|
@@ -157,6 +157,10 @@ function valueToJson(value, configName, definedAtData, importStatements) {
|
|
|
157
157
|
configValueSerialized = (0, stringify_1.stringify)(value, {
|
|
158
158
|
valueName,
|
|
159
159
|
forbidReactElements: true,
|
|
160
|
+
// Replace import strings with import variables.
|
|
161
|
+
// - We don't need this anymore and could remove it.
|
|
162
|
+
// - We temporarily needed it for nested document configs (`config.document.{title,description,favicon}`), but we finally decided to go for flat document configs instead (`config.{title,description,favicon}`).
|
|
163
|
+
// - https://github.com/vikejs/vike-react/pull/113
|
|
160
164
|
replacer(_, value) {
|
|
161
165
|
if (typeof value === 'string') {
|
|
162
166
|
const importData = (0, transformFileImports_js_1.parsePointerImportData)(value);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PROJECT_VERSION = exports.projectInfo = void 0;
|
|
4
|
-
const PROJECT_VERSION = '0.4.179-commit-
|
|
4
|
+
const PROJECT_VERSION = '0.4.179-commit-fd426a3';
|
|
5
5
|
exports.PROJECT_VERSION = PROJECT_VERSION;
|
|
6
6
|
const projectInfo = {
|
|
7
7
|
projectName: 'Vike',
|
|
@@ -22,10 +22,11 @@ type InjectFilterEntry = {
|
|
|
22
22
|
isEntry: boolean;
|
|
23
23
|
inject: PreloadFilterInject;
|
|
24
24
|
};
|
|
25
|
+
type Position = 'HTML_BEGIN' | 'HTML_END' | 'STREAM';
|
|
25
26
|
type HtmlTag = {
|
|
26
27
|
htmlTag: string | (() => string);
|
|
27
|
-
position:
|
|
28
|
+
position: Position;
|
|
28
29
|
};
|
|
29
30
|
declare function getHtmlTags(pageContext: {
|
|
30
31
|
_isStream: boolean;
|
|
31
|
-
} & PageContextInjectAssets, streamFromReactStreamingPackage: null | StreamFromReactStreamingPackage, injectFilter: PreloadFilter, pageAssets: PageAsset[], viteDevScript: string): HtmlTag[];
|
|
32
|
+
} & PageContextInjectAssets, streamFromReactStreamingPackage: null | StreamFromReactStreamingPackage, injectFilter: PreloadFilter, pageAssets: PageAsset[], viteDevScript: string, isStream: boolean): HtmlTag[];
|
|
@@ -4,13 +4,17 @@ import { serializePageContextClientSide } from '../serializePageContextClientSid
|
|
|
4
4
|
import { sanitizeJson } from './sanitizeJson.js';
|
|
5
5
|
import { inferAssetTag, inferPreloadTag } from './inferHtmlTags.js';
|
|
6
6
|
import { mergeScriptTags } from './mergeScriptTags.js';
|
|
7
|
+
import { getPageConfig } from '../../../../shared/page-configs/helpers.js';
|
|
8
|
+
import { getConfigValueRuntime } from '../../../../shared/page-configs/getConfigValue.js';
|
|
7
9
|
import { getGlobalContext } from '../../globalContext.js';
|
|
8
10
|
import pc from '@brillout/picocolors';
|
|
11
|
+
import { getConfigDefinedAt } from '../../../../shared/page-configs/getConfigDefinedAt.js';
|
|
9
12
|
const stamp = '__injectFilterEntry';
|
|
10
|
-
function getHtmlTags(pageContext, streamFromReactStreamingPackage, injectFilter, pageAssets, viteDevScript) {
|
|
13
|
+
function getHtmlTags(pageContext, streamFromReactStreamingPackage, injectFilter, pageAssets, viteDevScript, isStream) {
|
|
11
14
|
assert([true, false].includes(pageContext._isHtmlOnly));
|
|
12
15
|
const isHtmlOnly = pageContext._isHtmlOnly;
|
|
13
16
|
const { isProduction } = getGlobalContext();
|
|
17
|
+
const injectScriptsAt = getInjectScriptsAt(pageContext._pageId, pageContext._pageConfigs);
|
|
14
18
|
const injectFilterEntries = pageAssets
|
|
15
19
|
.filter((asset) => {
|
|
16
20
|
if (asset.isEntry && asset.assetType === 'script') {
|
|
@@ -73,28 +77,39 @@ function getHtmlTags(pageContext, streamFromReactStreamingPackage, injectFilter,
|
|
|
73
77
|
// ==========
|
|
74
78
|
// JavaScript
|
|
75
79
|
// ==========
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
// See https://github.com/vikejs/vike/pull/1271
|
|
80
|
+
// - The pageContext JSON should be fully sent before Vike's client runtime starts executing.
|
|
81
|
+
// - Otherwise, race condition "SyntaxError: Unterminated string in JSON": https://github.com/vikejs/vike/issues/567
|
|
82
|
+
// - <script id="vike_pageContext" type="application/json"> must appear before the entry <script> (which loads Vike's client runtime).
|
|
83
|
+
// - <script id="vike_pageContext" type="application/json"> can't be async nor defer.
|
|
84
|
+
// - The entry <script> can't be defer, otherwise progressive hydration while SSR streaming won't work.
|
|
85
|
+
// - The entry <script> should be towards the end of the HTML as performance-wise it's more interesting to parse <div id="page-view"> before running the entry <script> which initiates the hydration.
|
|
86
|
+
// - But with HTML streaming, in order to support [Progressive Rendering](https://vike.dev/streaming#progressive-rendering), the entry <script> should be injected early instead.
|
|
84
87
|
const positionJavaScriptEntry = (() => {
|
|
88
|
+
if (injectScriptsAt !== null) {
|
|
89
|
+
if (pageContext._pageContextPromise) {
|
|
90
|
+
assertWarning(injectScriptsAt === 'HTML_END' || !isStream, `You're setting injectScriptsAt to ${pc.code(JSON.stringify(injectScriptsAt))} while using HTML streaming with a pageContext promise (https://vike.dev/streaming#initial-data-after-stream-end) which is contradictory: the pageContext promise is skipped.`, { onlyOnce: true });
|
|
91
|
+
}
|
|
92
|
+
return injectScriptsAt;
|
|
93
|
+
}
|
|
85
94
|
if (pageContext._pageContextPromise) {
|
|
86
|
-
|
|
87
|
-
//
|
|
95
|
+
// - If there is a pageContext._pageContextPromise then <script id="vike_pageContext" type="application/json"> needs to await for it.
|
|
96
|
+
// - pageContext._pageContextPromise is typically resolved only after the page's components are rendered and the stream ended.
|
|
97
|
+
// - https://vike.dev/streaming#initial-data-after-stream-end
|
|
88
98
|
return 'HTML_END';
|
|
89
99
|
}
|
|
90
100
|
if (streamFromReactStreamingPackage && !streamFromReactStreamingPackage.hasStreamEnded()) {
|
|
91
|
-
// If there is a stream then, in order to support progressive hydration, inject the JavaScript during the stream after React(/Vue/Solid/...) resolved the first suspense boundary
|
|
101
|
+
// If there is a stream then, in order to support progressive hydration, inject the JavaScript during the stream after React(/Vue/Solid/...) resolved the first suspense boundary.
|
|
92
102
|
return 'STREAM';
|
|
93
103
|
}
|
|
94
|
-
|
|
95
|
-
return 'HTML_END';
|
|
96
|
-
}
|
|
104
|
+
return 'HTML_END';
|
|
97
105
|
})();
|
|
106
|
+
if (pageContext._pageContextPromise && streamFromReactStreamingPackage) {
|
|
107
|
+
// - Should we show this warning for Solid as well? Solid seems to also support progressive rendering.
|
|
108
|
+
// - https://github.com/vikejs/vike-solid/issues/95
|
|
109
|
+
// - Vue doesn't seem to support progressive rendering yet.
|
|
110
|
+
// - https://github.com/vikejs/vike-vue/issues/85
|
|
111
|
+
assertWarning(false, "We recommend against using HTML streaming and a pageContext promise (https://vike.dev/streaming#initial-data-after-stream-end) at the same time, because progressive hydration (https://vike.dev/streaming#progressive-rendering) won't work.", { onlyOnce: true });
|
|
112
|
+
}
|
|
98
113
|
// <script id="vike_pageContext" type="application/json">
|
|
99
114
|
if (!isHtmlOnly) {
|
|
100
115
|
htmlTags.push({
|
|
@@ -189,3 +204,19 @@ function checkForWarnings(injectFilterEntries) {
|
|
|
189
204
|
}
|
|
190
205
|
});
|
|
191
206
|
}
|
|
207
|
+
function getInjectScriptsAt(pageId, pageConfigs) {
|
|
208
|
+
if (pageConfigs.length === 0)
|
|
209
|
+
return null; // only support V1 design
|
|
210
|
+
const pageConfig = getPageConfig(pageId, pageConfigs);
|
|
211
|
+
const configValue = getConfigValueRuntime(pageConfig, 'injectScriptsAt');
|
|
212
|
+
if (!configValue)
|
|
213
|
+
return null;
|
|
214
|
+
const injectScriptsAt = configValue.value;
|
|
215
|
+
assert(configValue.definedAtData);
|
|
216
|
+
const configDefinedAt = getConfigDefinedAt('Config', 'injectScriptsAt', configValue.definedAtData);
|
|
217
|
+
assertUsage(injectScriptsAt === null ||
|
|
218
|
+
injectScriptsAt === 'HTML_BEGIN' ||
|
|
219
|
+
injectScriptsAt === 'HTML_END' ||
|
|
220
|
+
injectScriptsAt === 'STREAM', `${configDefinedAt} has an invalid value`);
|
|
221
|
+
return injectScriptsAt;
|
|
222
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { injectHtmlTags };
|
|
2
2
|
export { injectHtmlTagsUsingStream };
|
|
3
3
|
export { createHtmlHeadIfMissing };
|
|
4
|
+
export { joinHtmlTags };
|
|
4
5
|
export { injectAtOpeningTag };
|
|
5
6
|
export { injectAtClosingTag };
|
|
6
7
|
import type { StreamFromReactStreamingPackage } from '../stream/react-streaming.js';
|
|
@@ -8,6 +9,7 @@ import type { HtmlTag } from './getHtmlTags.js';
|
|
|
8
9
|
type Position = 'HTML_BEGIN' | 'HTML_END';
|
|
9
10
|
declare function injectHtmlTags(htmlString: string, htmlTags: HtmlTag[], position: Position): string;
|
|
10
11
|
declare function injectHtmlTagsUsingStream(htmlTags: HtmlTag[], streamFromReactStreamingPackage: null | StreamFromReactStreamingPackage): void;
|
|
12
|
+
declare function joinHtmlTags(htmlTags: HtmlTag[]): string;
|
|
11
13
|
declare function injectAtOpeningTag(tag: 'head' | 'html' | '!doctype', htmlString: string, htmlFragment: string): string;
|
|
12
14
|
declare function injectAtClosingTag(tag: 'body' | 'html', htmlString: string, htmlFragment: string): string;
|
|
13
15
|
declare function createHtmlHeadIfMissing(htmlString: string): string;
|
|
@@ -29,6 +29,7 @@ declare function injectHtmlTagsToStream(pageContext: PageContextInjectAssets & {
|
|
|
29
29
|
_isStream: true;
|
|
30
30
|
}, streamFromReactStreamingPackage: null | StreamFromReactStreamingPackage, injectFilter: PreloadFilter): {
|
|
31
31
|
injectAtStreamBegin: (htmlPartsBegin: HtmlPart[]) => Promise<string>;
|
|
32
|
+
injectAtStreamAfterFirstChunk: () => string;
|
|
32
33
|
injectAtStreamEnd: (htmlPartsEnd: HtmlPart[]) => Promise<string>;
|
|
33
34
|
};
|
|
34
35
|
type PageContextPromise = null | Promise<unknown> | (() => void | Promise<unknown>);
|
|
@@ -2,13 +2,13 @@ export { injectHtmlTagsToString };
|
|
|
2
2
|
export { injectHtmlTagsToStream };
|
|
3
3
|
import { assert, isCallable, isPromise } from '../utils.js';
|
|
4
4
|
import { assertPageContextProvidedByUser } from '../../../shared/assertPageContextProvidedByUser.js';
|
|
5
|
-
import { injectHtmlTags, createHtmlHeadIfMissing, injectHtmlTagsUsingStream } from './injectAssets/injectHtmlTags.js';
|
|
5
|
+
import { joinHtmlTags, injectHtmlTags, createHtmlHeadIfMissing, injectHtmlTagsUsingStream } from './injectAssets/injectHtmlTags.js';
|
|
6
6
|
import { getHtmlTags } from './injectAssets/getHtmlTags.js';
|
|
7
7
|
import { getViteDevScript } from './injectAssets/getViteDevScript.js';
|
|
8
8
|
async function injectHtmlTagsToString(htmlParts, pageContext, injectFilter) {
|
|
9
9
|
const pageAssets = await pageContext.__getPageAssets();
|
|
10
10
|
const viteDevScript = await getViteDevScript();
|
|
11
|
-
const htmlTags = getHtmlTags(pageContext, null, injectFilter, pageAssets, viteDevScript);
|
|
11
|
+
const htmlTags = getHtmlTags(pageContext, null, injectFilter, pageAssets, viteDevScript, false);
|
|
12
12
|
let htmlString = htmlPartsToString(htmlParts, pageAssets);
|
|
13
13
|
htmlString = injectToHtmlBegin(htmlString, htmlTags);
|
|
14
14
|
htmlString = injectToHtmlEnd(htmlString, htmlTags);
|
|
@@ -19,17 +19,23 @@ function injectHtmlTagsToStream(pageContext, streamFromReactStreamingPackage, in
|
|
|
19
19
|
let htmlTags;
|
|
20
20
|
return {
|
|
21
21
|
injectAtStreamBegin,
|
|
22
|
+
injectAtStreamAfterFirstChunk,
|
|
22
23
|
injectAtStreamEnd
|
|
23
24
|
};
|
|
24
25
|
async function injectAtStreamBegin(htmlPartsBegin) {
|
|
25
26
|
const pageAssets = await pageContext.__getPageAssets();
|
|
26
27
|
const viteDevScript = await getViteDevScript();
|
|
27
|
-
htmlTags = getHtmlTags(pageContext, streamFromReactStreamingPackage, injectFilter, pageAssets, viteDevScript);
|
|
28
|
+
htmlTags = getHtmlTags(pageContext, streamFromReactStreamingPackage, injectFilter, pageAssets, viteDevScript, true);
|
|
28
29
|
let htmlBegin = htmlPartsToString(htmlPartsBegin, pageAssets);
|
|
29
30
|
htmlBegin = injectToHtmlBegin(htmlBegin, htmlTags);
|
|
30
31
|
injectHtmlTagsUsingStream(htmlTags, streamFromReactStreamingPackage);
|
|
31
32
|
return htmlBegin;
|
|
32
33
|
}
|
|
34
|
+
function injectAtStreamAfterFirstChunk() {
|
|
35
|
+
assert(htmlTags);
|
|
36
|
+
const htmlFragment = joinHtmlTags(htmlTags.filter((h) => h.position === 'STREAM'));
|
|
37
|
+
return htmlFragment;
|
|
38
|
+
}
|
|
33
39
|
async function injectAtStreamEnd(htmlPartsEnd) {
|
|
34
40
|
assert(htmlTags);
|
|
35
41
|
await resolvePageContextPromise(pageContext);
|
|
@@ -61,7 +61,7 @@ async function renderHtmlStream(streamOriginal, injectString, pageContext, onErr
|
|
|
61
61
|
if (isStreamFromReactStreamingPackage(streamOriginal) && !streamOriginal.disabled) {
|
|
62
62
|
streamFromReactStreamingPackage = streamOriginal;
|
|
63
63
|
}
|
|
64
|
-
const { injectAtStreamBegin, injectAtStreamEnd } = injectHtmlTagsToStream(pageContext, streamFromReactStreamingPackage, injectFilter);
|
|
64
|
+
const { injectAtStreamBegin, injectAtStreamAfterFirstChunk, injectAtStreamEnd } = injectHtmlTagsToStream(pageContext, streamFromReactStreamingPackage, injectFilter);
|
|
65
65
|
objectAssign(opts, {
|
|
66
66
|
injectStringAtBegin: async () => {
|
|
67
67
|
return await injectAtStreamBegin(injectString.htmlPartsBegin);
|
|
@@ -70,6 +70,15 @@ async function renderHtmlStream(streamOriginal, injectString, pageContext, onErr
|
|
|
70
70
|
return await injectAtStreamEnd(injectString.htmlPartsEnd);
|
|
71
71
|
}
|
|
72
72
|
});
|
|
73
|
+
if (
|
|
74
|
+
// React needs its own chunk stream injection mechanism
|
|
75
|
+
!isStreamFromReactStreamingPackage) {
|
|
76
|
+
objectAssign(opts, {
|
|
77
|
+
injectStringAfterFirstChunk: () => {
|
|
78
|
+
return injectAtStreamAfterFirstChunk();
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
73
82
|
}
|
|
74
83
|
const streamWrapper = await processStream(streamOriginal, opts);
|
|
75
84
|
return streamWrapper;
|
|
@@ -51,8 +51,9 @@ declare function getStreamReadableNode(htmlRender: HtmlRender): Promise<null | S
|
|
|
51
51
|
declare function getStreamReadableWeb(htmlRender: HtmlRender): null | StreamReadableWeb;
|
|
52
52
|
declare function pipeToStreamWritableWeb(htmlRender: HtmlRender, writable: StreamWritableWeb): boolean;
|
|
53
53
|
declare function pipeToStreamWritableNode(htmlRender: HtmlRender, writable: StreamWritableNode): boolean;
|
|
54
|
-
declare function processStream(streamOriginal: StreamProviderAny, { injectStringAtBegin, injectStringAtEnd, onErrorWhileStreaming, enableEagerStreaming }: {
|
|
54
|
+
declare function processStream(streamOriginal: StreamProviderAny, { injectStringAtBegin, injectStringAfterFirstChunk, injectStringAtEnd, onErrorWhileStreaming, enableEagerStreaming }: {
|
|
55
55
|
injectStringAtBegin?: () => Promise<string>;
|
|
56
|
+
injectStringAfterFirstChunk?: () => string;
|
|
56
57
|
injectStringAtEnd?: () => Promise<string>;
|
|
57
58
|
onErrorWhileStreaming: (err: unknown) => void;
|
|
58
59
|
enableEagerStreaming?: boolean;
|
|
@@ -203,7 +203,7 @@ function pipeToStreamWritableNode(htmlRender, writable) {
|
|
|
203
203
|
checkType(htmlRender);
|
|
204
204
|
assert(false);
|
|
205
205
|
}
|
|
206
|
-
async function processStream(streamOriginal, { injectStringAtBegin, injectStringAtEnd, onErrorWhileStreaming, enableEagerStreaming }) {
|
|
206
|
+
async function processStream(streamOriginal, { injectStringAtBegin, injectStringAfterFirstChunk, injectStringAtEnd, onErrorWhileStreaming, enableEagerStreaming }) {
|
|
207
207
|
const buffer = [];
|
|
208
208
|
let streamOriginalHasStartedEmitting = false;
|
|
209
209
|
let streamOriginalEnded = false;
|
|
@@ -215,6 +215,7 @@ async function processStream(streamOriginal, { injectStringAtBegin, injectString
|
|
|
215
215
|
let resolve;
|
|
216
216
|
let reject;
|
|
217
217
|
let promiseHasResolved = false;
|
|
218
|
+
let injectStringAfterFirstChunk_done = false;
|
|
218
219
|
const streamWrapperPromise = new Promise((resolve_, reject_) => {
|
|
219
220
|
resolve = (streamWrapper) => {
|
|
220
221
|
promiseHasResolved = true;
|
|
@@ -228,8 +229,8 @@ async function processStream(streamOriginal, { injectStringAtBegin, injectString
|
|
|
228
229
|
let resolveReadyToWrite;
|
|
229
230
|
const promiseReadyToWrite = new Promise((r) => (resolveReadyToWrite = r));
|
|
230
231
|
if (injectStringAtBegin) {
|
|
231
|
-
const
|
|
232
|
-
writeStream(
|
|
232
|
+
const injectedChunk = await injectStringAtBegin();
|
|
233
|
+
writeStream(injectedChunk); // Adds injectedChunk to buffer
|
|
233
234
|
flushStream(); // Sets shouldFlushStream to true
|
|
234
235
|
}
|
|
235
236
|
// We call onStreamEvent() also when the stream ends in order to properly handle the situation when the stream didn't emit any data
|
|
@@ -259,6 +260,11 @@ async function processStream(streamOriginal, { injectStringAtBegin, injectString
|
|
|
259
260
|
onData(chunk) {
|
|
260
261
|
onStreamDataOrEnd(() => {
|
|
261
262
|
writeStream(chunk);
|
|
263
|
+
if (injectStringAfterFirstChunk && !injectStringAfterFirstChunk_done) {
|
|
264
|
+
const injectedChunk = injectStringAfterFirstChunk();
|
|
265
|
+
writeStream(injectedChunk);
|
|
266
|
+
injectStringAfterFirstChunk_done = true;
|
|
267
|
+
}
|
|
262
268
|
});
|
|
263
269
|
},
|
|
264
270
|
async onEnd(
|
|
@@ -273,8 +279,8 @@ async function processStream(streamOriginal, { injectStringAtBegin, injectString
|
|
|
273
279
|
streamOriginalEnded = true;
|
|
274
280
|
});
|
|
275
281
|
if (injectStringAtEnd) {
|
|
276
|
-
const
|
|
277
|
-
writeStream(
|
|
282
|
+
const injectedChunk = await injectStringAtEnd();
|
|
283
|
+
writeStream(injectedChunk);
|
|
278
284
|
}
|
|
279
285
|
await promiseReadyToWrite; // E.g. if the user calls the pipe wrapper after the original writable has ended
|
|
280
286
|
assert(isReady());
|
|
@@ -354,6 +354,11 @@ type ConfigBuiltIn = {
|
|
|
354
354
|
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
|
|
355
355
|
*/
|
|
356
356
|
cacheControl?: string;
|
|
357
|
+
/** Where scripts are injected in the HTML.
|
|
358
|
+
*
|
|
359
|
+
* https://vike.dev/injectScriptsAt
|
|
360
|
+
*/
|
|
361
|
+
injectScriptsAt?: 'HTML_BEGIN' | 'HTML_END' | 'STREAM' | null;
|
|
357
362
|
/** Used by Vike extensions to set their name.
|
|
358
363
|
*
|
|
359
364
|
* https://vike.dev/extends
|
|
@@ -151,6 +151,10 @@ function valueToJson(value, configName, definedAtData, importStatements) {
|
|
|
151
151
|
configValueSerialized = stringify(value, {
|
|
152
152
|
valueName,
|
|
153
153
|
forbidReactElements: true,
|
|
154
|
+
// Replace import strings with import variables.
|
|
155
|
+
// - We don't need this anymore and could remove it.
|
|
156
|
+
// - We temporarily needed it for nested document configs (`config.document.{title,description,favicon}`), but we finally decided to go for flat document configs instead (`config.{title,description,favicon}`).
|
|
157
|
+
// - https://github.com/vikejs/vike-react/pull/113
|
|
154
158
|
replacer(_, value) {
|
|
155
159
|
if (typeof value === 'string') {
|
|
156
160
|
const importData = parsePointerImportData(value);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { projectInfo };
|
|
2
2
|
export { PROJECT_VERSION };
|
|
3
|
-
declare const PROJECT_VERSION: "0.4.179-commit-
|
|
3
|
+
declare const PROJECT_VERSION: "0.4.179-commit-fd426a3";
|
|
4
4
|
declare const projectInfo: {
|
|
5
5
|
projectName: "Vike";
|
|
6
|
-
projectVersion: "0.4.179-commit-
|
|
6
|
+
projectVersion: "0.4.179-commit-fd426a3";
|
|
7
7
|
};
|