vike 0.4.177 → 0.4.178
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/envVars.js +7 -3
- package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/crawlPlusFiles.js +131 -20
- package/dist/cjs/node/runtime/html/injectAssets/getHtmlTags.js +6 -9
- package/dist/cjs/node/runtime/html/injectAssets/{getViteDevScripts.js → getViteDevScript.js} +5 -5
- package/dist/cjs/node/runtime/html/injectAssets/injectHtmlTags.js +20 -18
- package/dist/cjs/node/runtime/html/injectAssets.js +13 -10
- package/dist/cjs/node/runtime/html/renderHtml.js +4 -4
- package/dist/cjs/node/runtime/html/stream/react-streaming.js +11 -10
- package/dist/cjs/node/runtime/html/stream.js +18 -10
- package/dist/cjs/node/runtime/utils.js +1 -0
- package/dist/cjs/utils/isVikeReactApp.js +9 -0
- package/dist/cjs/utils/parseUrl.js +17 -12
- package/dist/cjs/utils/projectInfo.js +1 -1
- package/dist/esm/client/client-routing-runtime/getPageContextFromHooks.js +1 -0
- package/dist/esm/node/plugin/plugins/envVars.js +7 -3
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/crawlPlusFiles.js +132 -21
- package/dist/esm/node/runtime/html/injectAssets/getHtmlTags.d.ts +2 -2
- package/dist/esm/node/runtime/html/injectAssets/getHtmlTags.js +6 -9
- package/dist/esm/node/runtime/html/injectAssets/getViteDevScript.d.ts +2 -0
- package/dist/esm/node/runtime/html/injectAssets/{getViteDevScripts.js → getViteDevScript.js} +4 -4
- package/dist/esm/node/runtime/html/injectAssets/injectHtmlTags.d.ts +5 -2
- package/dist/esm/node/runtime/html/injectAssets/injectHtmlTags.js +19 -17
- package/dist/esm/node/runtime/html/injectAssets.d.ts +2 -2
- package/dist/esm/node/runtime/html/injectAssets.js +14 -11
- package/dist/esm/node/runtime/html/renderHtml.js +5 -5
- package/dist/esm/node/runtime/html/stream/react-streaming.d.ts +16 -23
- package/dist/esm/node/runtime/html/stream/react-streaming.js +11 -10
- package/dist/esm/node/runtime/html/stream.d.ts +2 -2
- package/dist/esm/node/runtime/html/stream.js +19 -11
- package/dist/esm/node/runtime/utils.d.ts +1 -0
- package/dist/esm/node/runtime/utils.js +1 -0
- package/dist/esm/utils/isVikeReactApp.d.ts +1 -0
- package/dist/esm/utils/isVikeReactApp.js +5 -0
- package/dist/esm/utils/parseUrl.d.ts +1 -1
- package/dist/esm/utils/parseUrl.js +17 -12
- package/dist/esm/utils/projectInfo.d.ts +2 -2
- package/dist/esm/utils/projectInfo.js +1 -1
- package/package.json +3 -3
- package/dist/esm/node/runtime/html/injectAssets/getViteDevScripts.d.ts +0 -2
|
@@ -10,17 +10,9 @@ exports.isUriWithProtocol = exports.createUrlFromComponents = exports.assertUrlC
|
|
|
10
10
|
const slice_js_1 = require("./slice.js");
|
|
11
11
|
const assert_js_1 = require("./assert.js");
|
|
12
12
|
const picocolors_1 = __importDefault(require("@brillout/picocolors"));
|
|
13
|
-
const PROTOCOLS = [
|
|
14
|
-
'http://',
|
|
15
|
-
'https://',
|
|
16
|
-
// For [Tauri](https://tauri.app/)
|
|
17
|
-
'tauri://',
|
|
18
|
-
// For Electron: https://github.com/vikejs/vike/issues/1557
|
|
19
|
-
'file://'
|
|
20
|
-
];
|
|
21
13
|
function isParsable(url) {
|
|
22
14
|
// `parseUrl()` works with these URLs
|
|
23
|
-
return (
|
|
15
|
+
return (isUrlWithProtocol(url) ||
|
|
24
16
|
url.startsWith('/') ||
|
|
25
17
|
url.startsWith('.') ||
|
|
26
18
|
url.startsWith('?') ||
|
|
@@ -138,7 +130,8 @@ function getPathname(url, baseServer) {
|
|
|
138
130
|
}
|
|
139
131
|
}
|
|
140
132
|
function parseOrigin(url) {
|
|
141
|
-
if (!
|
|
133
|
+
if (!isUrlWithProtocol(url)) {
|
|
134
|
+
(0, assert_js_1.assert)(!isUriWithProtocol(url));
|
|
142
135
|
return { pathname: url, origin: null };
|
|
143
136
|
}
|
|
144
137
|
else {
|
|
@@ -239,8 +232,20 @@ function createUrlFromComponents(origin, pathname, searchOriginal, hashOriginal)
|
|
|
239
232
|
return urlRecreated;
|
|
240
233
|
}
|
|
241
234
|
exports.createUrlFromComponents = createUrlFromComponents;
|
|
242
|
-
function isUriWithProtocol(
|
|
235
|
+
function isUriWithProtocol(str) {
|
|
243
236
|
// https://en.wikipedia.org/wiki/List_of_URI_schemes
|
|
244
|
-
|
|
237
|
+
// https://www.rfc-editor.org/rfc/rfc7595
|
|
238
|
+
// https://github.com/vikejs/vike/commit/886a99ff21e86a8ca699a25cee7edc184aa058e4#r143308934
|
|
239
|
+
// Examples:
|
|
240
|
+
// http://
|
|
241
|
+
// https://
|
|
242
|
+
// tauri:// # [Tauri](https://tauri.app)
|
|
243
|
+
// file:// # [Electron](https://github.com/vikejs/vike/issues/1557)
|
|
244
|
+
// capacitor:// # [Capacitor](https://github.com/vikejs/vike/issues/1706)
|
|
245
|
+
return /^[a-z][a-z0-9\+\-]*:/i.test(str);
|
|
245
246
|
}
|
|
246
247
|
exports.isUriWithProtocol = isUriWithProtocol;
|
|
248
|
+
// Same as isUriWithProtocol() but with trailing :// which is needed for parseOrigin()
|
|
249
|
+
function isUrlWithProtocol(str) {
|
|
250
|
+
return /^[a-z][a-z0-9\+\-]*:\/\//i.test(str);
|
|
251
|
+
}
|
|
@@ -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.
|
|
4
|
+
const PROJECT_VERSION = '0.4.178';
|
|
5
5
|
exports.PROJECT_VERSION = PROJECT_VERSION;
|
|
6
6
|
const projectInfo = {
|
|
7
7
|
projectName: 'Vike',
|
|
@@ -19,6 +19,7 @@ import { isServerSideError } from '../../shared/misc/isServerSideError.js';
|
|
|
19
19
|
const globalObject = getGlobalObject('router/getPageContext.ts', {});
|
|
20
20
|
function getPageContextFromHooks_serialized() {
|
|
21
21
|
const pageContextSerialized = getPageContextSerializedInHtml();
|
|
22
|
+
assertUsage(!('urlOriginal' in pageContextSerialized), "Adding 'urlOriginal' to passToClient is forbidden");
|
|
22
23
|
processPageContextFromServer(pageContextSerialized);
|
|
23
24
|
objectAssign(pageContextSerialized, {
|
|
24
25
|
_hasPageContextFromServer: true
|
|
@@ -5,6 +5,11 @@ import { loadEnv } from 'vite';
|
|
|
5
5
|
import { assert, assertPosixPath, assertUsage, assertWarning, escapeRegex, isArray, lowerFirst } from '../utils.js';
|
|
6
6
|
import { sourceMapPassthrough } from '../shared/rollupSourceMap.js';
|
|
7
7
|
import { getModuleFilePath } from '../shared/getFilePath.js';
|
|
8
|
+
const PUBLIC_ENV_PREFIX = 'PUBLIC_ENV__';
|
|
9
|
+
const PUBLIC_ENV_WHITELIST = [
|
|
10
|
+
// https://github.com/vikejs/vike/issues/1724
|
|
11
|
+
'STORYBOOK'
|
|
12
|
+
];
|
|
8
13
|
function envVarsPlugin() {
|
|
9
14
|
let envsAll;
|
|
10
15
|
let config;
|
|
@@ -37,14 +42,13 @@ function envVarsPlugin() {
|
|
|
37
42
|
// Security check
|
|
38
43
|
{
|
|
39
44
|
const envStatement = getEnvStatement(envName);
|
|
40
|
-
const
|
|
41
|
-
const isPrivate = !envName.startsWith(publicPrefix);
|
|
45
|
+
const isPrivate = !envName.startsWith(PUBLIC_ENV_PREFIX) && !PUBLIC_ENV_WHITELIST.includes(envName);
|
|
42
46
|
if (isPrivate && isClientSide) {
|
|
43
47
|
if (!code.includes(envStatement))
|
|
44
48
|
return;
|
|
45
49
|
const modulePath = getModuleFilePath(id, config);
|
|
46
50
|
const errMsgAddendum = isBuild ? '' : ' (Vike will prevent your app from building for production)';
|
|
47
|
-
const keyPublic = `${
|
|
51
|
+
const keyPublic = `${PUBLIC_ENV_PREFIX}${envName}`;
|
|
48
52
|
const errMsg = `${envStatement} is used in client-side file ${modulePath} which means that the environment variable ${envName} will be included in client-side bundles and, therefore, ${envName} will be publicly exposed which can be a security leak${errMsgAddendum}. Use ${envStatement} only in server-side files, or rename ${envName} to ${keyPublic}, see https://vike.dev/env`;
|
|
49
53
|
if (isBuild) {
|
|
50
54
|
assertUsage(false, errMsg);
|
package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/crawlPlusFiles.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
export { crawlPlusFiles };
|
|
2
|
-
import { assertPosixPath, assert, assertWarning,
|
|
2
|
+
import { assertPosixPath, assert, assertWarning, scriptFileExtensions, humanizeTime, assertIsSingleModuleInstance, assertIsNotProductionRuntime, isVersionOrAbove, isScriptFile } from '../../../../utils.js';
|
|
3
3
|
import path from 'path';
|
|
4
|
+
import fs from 'fs/promises';
|
|
4
5
|
import glob from 'fast-glob';
|
|
5
6
|
import { exec } from 'child_process';
|
|
6
7
|
import { promisify } from 'util';
|
|
7
8
|
import pc from '@brillout/picocolors';
|
|
8
9
|
import { isTemporaryBuildFile } from './transpileAndExecuteFile.js';
|
|
9
10
|
const execA = promisify(exec);
|
|
11
|
+
const TOO_MANY_UNTRACKED_FILES = 5;
|
|
10
12
|
assertIsNotProductionRuntime();
|
|
11
13
|
assertIsSingleModuleInstance('crawlPlusFiles.ts');
|
|
12
14
|
let gitIsNotUsable = false;
|
|
@@ -30,15 +32,17 @@ async function crawlPlusFiles(userRootDir, outDirAbsoluteFilesystem, isDev, craw
|
|
|
30
32
|
let files = [];
|
|
31
33
|
const res = crawlWithGit !== false && (await gitLsFiles(userRootDir, outDirRelativeFromUserRootDir));
|
|
32
34
|
if (res &&
|
|
33
|
-
// Fallback to fast-glob for users that dynamically generate plus files. (Assuming
|
|
34
|
-
res.length > 0) {
|
|
35
|
-
files = res;
|
|
35
|
+
// Fallback to fast-glob for users that dynamically generate plus files. (Assuming that no plus file is found because of the user's .gitignore list.)
|
|
36
|
+
res.files.length > 0) {
|
|
37
|
+
files = res.files;
|
|
38
|
+
// We cannot find files inside symlink directories with `$ git ls-files` => we use fast-glob
|
|
39
|
+
files.push(...(await crawlSymlinkDirs(res.symlinkDirs, userRootDir, outDirRelativeFromUserRootDir)));
|
|
36
40
|
}
|
|
37
41
|
else {
|
|
38
42
|
files = await fastGlob(userRootDir, outDirRelativeFromUserRootDir);
|
|
39
43
|
}
|
|
40
44
|
// Filter build files
|
|
41
|
-
files = files.filter((
|
|
45
|
+
files = files.filter((filePath) => !isTemporaryBuildFile(filePath));
|
|
42
46
|
// Check performance
|
|
43
47
|
{
|
|
44
48
|
const timeAfter = new Date().getTime();
|
|
@@ -46,8 +50,8 @@ async function crawlPlusFiles(userRootDir, outDirAbsoluteFilesystem, isDev, craw
|
|
|
46
50
|
if (isDev) {
|
|
47
51
|
// We only warn in dev, because while building it's expected to take a long time as crawling is competing for resources with other tasks.
|
|
48
52
|
// Although, in dev, it's also competing for resources e.g. with Vite's `optimizeDeps`.
|
|
49
|
-
assertWarning(timeSpent < 3 * 1000, `Crawling your ${pc.cyan('+')} files took an unexpected long time (${humanizeTime(timeSpent)}). If you
|
|
50
|
-
onlyOnce: 'slow-
|
|
53
|
+
assertWarning(timeSpent < 3 * 1000, `Crawling your ${pc.cyan('+')} files took an unexpected long time (${humanizeTime(timeSpent)}). If you consistently get this warning, then consider reaching out on GitHub.`, {
|
|
54
|
+
onlyOnce: 'slow-crawling'
|
|
51
55
|
});
|
|
52
56
|
}
|
|
53
57
|
}
|
|
@@ -76,20 +80,27 @@ async function gitLsFiles(userRootDir, outDirRelativeFromUserRootDir) {
|
|
|
76
80
|
'git',
|
|
77
81
|
preserveUTF8,
|
|
78
82
|
'ls-files',
|
|
79
|
-
|
|
83
|
+
// We don't filter because:
|
|
84
|
+
// - It would skip symlink directories
|
|
85
|
+
// - Performance gain seems negligible: https://github.com/vikejs/vike/pull/1688#issuecomment-2166206648
|
|
86
|
+
// ...scriptFileExtensionList.map((ext) => `"**/+*.${ext}"`),
|
|
87
|
+
// Performance gain is non-negligible.
|
|
88
|
+
// - https://github.com/vikejs/vike/pull/1688#issuecomment-2166206648
|
|
89
|
+
// - When node_modules/ is untracked the performance gain could be significant?
|
|
80
90
|
...ignoreAsPatterns.map((pattern) => `--exclude="${pattern}"`),
|
|
81
|
-
// --others
|
|
82
|
-
// --cached
|
|
83
|
-
|
|
91
|
+
// --others --exclude-standard => list untracked files (--others) while using .gitignore (--exclude-standard)
|
|
92
|
+
// --cached => list tracked files
|
|
93
|
+
// --stage => get file modes which we use to find symlink directories
|
|
94
|
+
'--others --exclude-standard --cached --stage'
|
|
84
95
|
].join(' ');
|
|
85
|
-
let
|
|
96
|
+
let resultLines;
|
|
86
97
|
let filesDeleted;
|
|
87
98
|
try {
|
|
88
99
|
;
|
|
89
|
-
[
|
|
100
|
+
[resultLines, filesDeleted] = await Promise.all([
|
|
90
101
|
// Main command
|
|
91
102
|
runCmd1(cmd, userRootDir),
|
|
92
|
-
// Get tracked
|
|
103
|
+
// Get tracked but deleted files
|
|
93
104
|
runCmd1('git ls-files --deleted', userRootDir)
|
|
94
105
|
]);
|
|
95
106
|
}
|
|
@@ -100,11 +111,40 @@ async function gitLsFiles(userRootDir, outDirRelativeFromUserRootDir) {
|
|
|
100
111
|
}
|
|
101
112
|
throw err;
|
|
102
113
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
114
|
+
const filePaths = resultLines.map(parseGitLsResultLine);
|
|
115
|
+
// If there are too many files without mode we fallback to fast-glob
|
|
116
|
+
if (filePaths.filter((f) => !f.mode).length > TOO_MANY_UNTRACKED_FILES)
|
|
117
|
+
return null;
|
|
118
|
+
const symlinkDirs = [];
|
|
119
|
+
const files = [];
|
|
120
|
+
for (const { filePath, mode } of filePaths) {
|
|
121
|
+
// Deleted?
|
|
122
|
+
if (filesDeleted.includes(filePath))
|
|
123
|
+
continue;
|
|
124
|
+
// We have to repeat the same exclusion logic here because the option --exclude of `$ git ls-files` only applies to untracked files. (We use --exclude only to speed up the `$ git ls-files` command.)
|
|
125
|
+
if (!ignoreAsFilterFn(filePath))
|
|
126
|
+
continue;
|
|
127
|
+
// Symlink directory?
|
|
128
|
+
{
|
|
129
|
+
const isSymlinkDir = await isSymlinkDirectory(mode, filePath, userRootDir);
|
|
130
|
+
if (isSymlinkDir) {
|
|
131
|
+
symlinkDirs.push(filePath);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
// Skip deleted files and non-symlink directories
|
|
135
|
+
if (isSymlinkDir === null) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// + file?
|
|
140
|
+
if (!path.posix.basename(filePath).startsWith('+'))
|
|
141
|
+
continue;
|
|
142
|
+
// JavaScript file?
|
|
143
|
+
if (!isScriptFile(filePath))
|
|
144
|
+
continue;
|
|
145
|
+
files.push(filePath);
|
|
146
|
+
}
|
|
147
|
+
return { files, symlinkDirs };
|
|
108
148
|
}
|
|
109
149
|
// Same as gitLsFiles() but using fast-glob
|
|
110
150
|
async function fastGlob(userRootDir, outDirRelativeFromUserRootDir) {
|
|
@@ -115,7 +155,7 @@ async function fastGlob(userRootDir, outDirRelativeFromUserRootDir) {
|
|
|
115
155
|
});
|
|
116
156
|
return files;
|
|
117
157
|
}
|
|
118
|
-
// Same as
|
|
158
|
+
// Same as getIgnoreAsFilterFn() but as glob pattern
|
|
119
159
|
function getIgnoreAsPatterns(outDirRelativeFromUserRootDir) {
|
|
120
160
|
const ignoreAsPatterns = [
|
|
121
161
|
'**/node_modules/**',
|
|
@@ -132,7 +172,7 @@ function getIgnoreAsPatterns(outDirRelativeFromUserRootDir) {
|
|
|
132
172
|
}
|
|
133
173
|
return ignoreAsPatterns;
|
|
134
174
|
}
|
|
135
|
-
// Same as
|
|
175
|
+
// Same as getIgnoreAsPatterns() but for Array.filter()
|
|
136
176
|
function getIgnoreAsFilterFn(outDirRelativeFromUserRootDir) {
|
|
137
177
|
assert(outDirRelativeFromUserRootDir === null || !outDirRelativeFromUserRootDir.startsWith('/'));
|
|
138
178
|
return (file) => !file.includes('node_modules/') &&
|
|
@@ -169,6 +209,77 @@ async function isGitNotUsable(userRootDir) {
|
|
|
169
209
|
return false;
|
|
170
210
|
}
|
|
171
211
|
}
|
|
212
|
+
async function crawlSymlinkDirs(symlinkDirs, userRootDir, outDirRelativeFromUserRootDir) {
|
|
213
|
+
const filesInSymlinkDirs = (await Promise.all(symlinkDirs.map(async (symlinkDir) => (await fastGlob(path.posix.join(userRootDir, symlinkDir), outDirRelativeFromUserRootDir)).map((filePath) => path.posix.join(symlinkDir, filePath))))).flat();
|
|
214
|
+
return filesInSymlinkDirs;
|
|
215
|
+
}
|
|
216
|
+
// Parse:
|
|
217
|
+
// ```
|
|
218
|
+
// some/not/tracked/path
|
|
219
|
+
// 100644 f6928073402b241b468b199893ff6f4aed0b7195 0\tpages/index/+Page.tsx
|
|
220
|
+
// ```
|
|
221
|
+
function parseGitLsResultLine(resultLine) {
|
|
222
|
+
const [part1, part2, ...rest] = resultLine.split('\t');
|
|
223
|
+
assert(part1);
|
|
224
|
+
assert(rest.length === 0);
|
|
225
|
+
// Git doesn't provide the mode for untracked paths.
|
|
226
|
+
// `resultLine` is:
|
|
227
|
+
// ```
|
|
228
|
+
// some/not/tracked/path
|
|
229
|
+
// ```
|
|
230
|
+
if (part2 === undefined) {
|
|
231
|
+
return { filePath: part1, mode: null };
|
|
232
|
+
}
|
|
233
|
+
assert(part2);
|
|
234
|
+
// `resultLine` is:
|
|
235
|
+
// ```
|
|
236
|
+
// 100644 f6928073402b241b468b199893ff6f4aed0b7195 0\tpages/index/+Page.tsx
|
|
237
|
+
// ```
|
|
238
|
+
const [mode, _, __, ...rest2] = part1.split(' ');
|
|
239
|
+
assert(mode && _ && __ && rest2.length === 0);
|
|
240
|
+
return { filePath: part2, mode };
|
|
241
|
+
}
|
|
242
|
+
async function isSymlinkDirectory(mode, filePath, userRootDir) {
|
|
243
|
+
const filePathAbsolute = path.posix.join(userRootDir, filePath);
|
|
244
|
+
let stats = null;
|
|
245
|
+
let isSymlink = false;
|
|
246
|
+
if (mode === '120000') {
|
|
247
|
+
isSymlink = true;
|
|
248
|
+
}
|
|
249
|
+
else if (mode === null) {
|
|
250
|
+
// `$ git ls-files` doesn't provide the mode when Git doesn't track the path
|
|
251
|
+
stats = await getFileStats(filePathAbsolute);
|
|
252
|
+
if (stats === null)
|
|
253
|
+
return null;
|
|
254
|
+
isSymlink = stats.isSymbolicLink();
|
|
255
|
+
if (!isSymlink && stats.isDirectory())
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
assert(mode);
|
|
260
|
+
}
|
|
261
|
+
if (!isSymlink)
|
|
262
|
+
return false;
|
|
263
|
+
if (!stats)
|
|
264
|
+
stats = await getFileStats(filePathAbsolute);
|
|
265
|
+
if (stats === null)
|
|
266
|
+
return null;
|
|
267
|
+
const isDirectory = stats.isDirectory();
|
|
268
|
+
return isDirectory;
|
|
269
|
+
}
|
|
270
|
+
async function getFileStats(filePathAbsolute) {
|
|
271
|
+
let stats;
|
|
272
|
+
try {
|
|
273
|
+
stats = await fs.lstat(filePathAbsolute);
|
|
274
|
+
}
|
|
275
|
+
catch (err) {
|
|
276
|
+
// File was deleted, usually a temporary file such as +config.js.build-j95xb988fpln.mjs
|
|
277
|
+
// ENOENT: no such file or directory
|
|
278
|
+
assert(err.code === 'ENOENT');
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
return stats;
|
|
282
|
+
}
|
|
172
283
|
async function runCmd1(cmd, cwd) {
|
|
173
284
|
const { stdout } = await execA(cmd, { cwd });
|
|
174
285
|
/* Not always true: https://github.com/vikejs/vike/issues/1440#issuecomment-1892831303
|
|
@@ -3,7 +3,7 @@ export type { HtmlTag };
|
|
|
3
3
|
export type { PreloadFilter };
|
|
4
4
|
export type { InjectFilterEntry };
|
|
5
5
|
import type { PageContextInjectAssets } from '../injectAssets.js';
|
|
6
|
-
import type {
|
|
6
|
+
import type { StreamFromReactStreamingPackage } from '../stream/react-streaming.js';
|
|
7
7
|
import type { PageAsset } from '../../renderPage/getPageAssets.js';
|
|
8
8
|
type PreloadFilter = null | ((assets: InjectFilterEntry[]) => InjectFilterEntry[]);
|
|
9
9
|
type PreloadFilterInject = false | 'HTML_BEGIN' | 'HTML_END';
|
|
@@ -28,4 +28,4 @@ type HtmlTag = {
|
|
|
28
28
|
};
|
|
29
29
|
declare function getHtmlTags(pageContext: {
|
|
30
30
|
_isStream: boolean;
|
|
31
|
-
} & PageContextInjectAssets,
|
|
31
|
+
} & PageContextInjectAssets, streamFromReactStreamingPackage: null | StreamFromReactStreamingPackage, injectFilter: PreloadFilter, pageAssets: PageAsset[], viteDevScript: string): HtmlTag[];
|
|
@@ -3,16 +3,14 @@ import { assert, assertWarning, assertUsage, isObject, freezePartial } from '../
|
|
|
3
3
|
import { serializePageContextClientSide } from '../serializePageContextClientSide.js';
|
|
4
4
|
import { sanitizeJson } from './sanitizeJson.js';
|
|
5
5
|
import { inferAssetTag, inferPreloadTag } from './inferHtmlTags.js';
|
|
6
|
-
import { getViteDevScripts } from './getViteDevScripts.js';
|
|
7
6
|
import { mergeScriptTags } from './mergeScriptTags.js';
|
|
8
7
|
import { getGlobalContext } from '../../globalContext.js';
|
|
9
8
|
import pc from '@brillout/picocolors';
|
|
10
9
|
const stamp = '__injectFilterEntry';
|
|
11
|
-
|
|
10
|
+
function getHtmlTags(pageContext, streamFromReactStreamingPackage, injectFilter, pageAssets, viteDevScript) {
|
|
12
11
|
assert([true, false].includes(pageContext._isHtmlOnly));
|
|
13
12
|
const isHtmlOnly = pageContext._isHtmlOnly;
|
|
14
13
|
const { isProduction } = getGlobalContext();
|
|
15
|
-
const pageAssets = await pageContext.__getPageAssets();
|
|
16
14
|
const injectFilterEntries = pageAssets
|
|
17
15
|
.filter((asset) => {
|
|
18
16
|
if (asset.isEntry && asset.assetType === 'script') {
|
|
@@ -85,11 +83,11 @@ async function getHtmlTags(pageContext, injectToStream, injectFilter) {
|
|
|
85
83
|
// See https://github.com/vikejs/vike/pull/1271
|
|
86
84
|
const positionJavaScriptEntry = (() => {
|
|
87
85
|
if (pageContext._pageContextPromise) {
|
|
88
|
-
assertWarning(!
|
|
86
|
+
assertWarning(!streamFromReactStreamingPackage, "[getHtmlTags()] We recommend against using streaming and a pageContext promise at the same time, because progressive hydration won't work.", { onlyOnce: true });
|
|
89
87
|
// If there is a pageContext._pageContextPromise (which is resolved after the stream has ended) then the pageContext JSON data needs to await for it: https://vike.dev/streaming#initial-data-after-stream-end
|
|
90
88
|
return 'HTML_END';
|
|
91
89
|
}
|
|
92
|
-
if (
|
|
90
|
+
if (streamFromReactStreamingPackage && !streamFromReactStreamingPackage.hasStreamEnded()) {
|
|
93
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
|
|
94
92
|
return 'STREAM';
|
|
95
93
|
}
|
|
@@ -107,7 +105,7 @@ async function getHtmlTags(pageContext, injectToStream, injectFilter) {
|
|
|
107
105
|
});
|
|
108
106
|
}
|
|
109
107
|
// The JavaScript entry <script> tag
|
|
110
|
-
const scriptEntry =
|
|
108
|
+
const scriptEntry = mergeScriptEntries(pageAssets, viteDevScript);
|
|
111
109
|
if (scriptEntry) {
|
|
112
110
|
htmlTags.push({
|
|
113
111
|
htmlTag: scriptEntry,
|
|
@@ -128,10 +126,9 @@ async function getHtmlTags(pageContext, injectToStream, injectFilter) {
|
|
|
128
126
|
});
|
|
129
127
|
return htmlTags;
|
|
130
128
|
}
|
|
131
|
-
|
|
129
|
+
function mergeScriptEntries(pageAssets, viteDevScript) {
|
|
132
130
|
const scriptEntries = pageAssets.filter((pageAsset) => pageAsset.isEntry && pageAsset.assetType === 'script');
|
|
133
|
-
const
|
|
134
|
-
const scriptTagsHtml = `${viteScripts}${scriptEntries.map((asset) => inferAssetTag(asset)).join('')}`;
|
|
131
|
+
const scriptTagsHtml = `${viteDevScript}${scriptEntries.map((asset) => inferAssetTag(asset)).join('')}`;
|
|
135
132
|
const scriptTag = mergeScriptTags(scriptTagsHtml);
|
|
136
133
|
return scriptTag;
|
|
137
134
|
}
|
package/dist/esm/node/runtime/html/injectAssets/{getViteDevScripts.js → getViteDevScript.js}
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { getViteDevScript };
|
|
2
2
|
import { getGlobalContext } from '../../globalContext.js';
|
|
3
3
|
import { assert, assertUsage, assertWarning } from '../../utils.js';
|
|
4
4
|
import pc from '@brillout/picocolors';
|
|
5
|
-
async function
|
|
5
|
+
async function getViteDevScript() {
|
|
6
6
|
const globalContext = getGlobalContext();
|
|
7
7
|
if (globalContext.isProduction) {
|
|
8
8
|
return '';
|
|
@@ -18,6 +18,6 @@ async function getViteDevScripts() {
|
|
|
18
18
|
const viteInjection = fakeHtml.slice(fakeHtmlBegin.length, -1 * fakeHtmlEnd.length);
|
|
19
19
|
assert(viteInjection.includes('script'));
|
|
20
20
|
assertWarning(!viteInjection.includes('import('), 'Unexpected Vite HMR code. Reach out to a Vike maintainer on GitHub.', { onlyOnce: true });
|
|
21
|
-
const
|
|
22
|
-
return
|
|
21
|
+
const viteDevScript = viteInjection;
|
|
22
|
+
return viteDevScript;
|
|
23
23
|
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
export { injectHtmlTags };
|
|
2
|
+
export { injectHtmlTagsUsingStream };
|
|
2
3
|
export { createHtmlHeadIfMissing };
|
|
3
4
|
export { injectAtOpeningTag };
|
|
4
5
|
export { injectAtClosingTag };
|
|
6
|
+
import type { StreamFromReactStreamingPackage } from '../stream/react-streaming.js';
|
|
5
7
|
import type { HtmlTag } from './getHtmlTags.js';
|
|
6
|
-
|
|
7
|
-
declare function injectHtmlTags(htmlString: string, htmlTags: HtmlTag[],
|
|
8
|
+
type Position = 'HTML_BEGIN' | 'HTML_END';
|
|
9
|
+
declare function injectHtmlTags(htmlString: string, htmlTags: HtmlTag[], position: Position): string;
|
|
10
|
+
declare function injectHtmlTagsUsingStream(htmlTags: HtmlTag[], streamFromReactStreamingPackage: null | StreamFromReactStreamingPackage): Promise<void>;
|
|
8
11
|
declare function injectAtOpeningTag(tag: 'head' | 'html' | '!doctype', htmlString: string, htmlFragment: string): string;
|
|
9
12
|
declare function injectAtClosingTag(tag: 'body' | 'html', htmlString: string, htmlFragment: string): string;
|
|
10
13
|
declare function createHtmlHeadIfMissing(htmlString: string): string;
|
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
// Unit tests at ./injectHtmlTags.spec.ts
|
|
2
2
|
export { injectHtmlTags };
|
|
3
|
+
export { injectHtmlTagsUsingStream };
|
|
3
4
|
export { createHtmlHeadIfMissing };
|
|
4
5
|
// Only needed for unit tests
|
|
5
6
|
export { injectAtOpeningTag };
|
|
6
7
|
export { injectAtClosingTag };
|
|
7
8
|
import { assert, assertUsage, slice } from '../../utils.js';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
.map((h) => resolveHtmlTag(h.htmlTag))
|
|
14
|
-
.join('');
|
|
15
|
-
if (htmlFragment) {
|
|
16
|
-
htmlString = injectHtmlFragment(position, htmlFragment, htmlString, injectToStream);
|
|
17
|
-
}
|
|
18
|
-
});
|
|
9
|
+
function injectHtmlTags(htmlString, htmlTags, position) {
|
|
10
|
+
const htmlFragment = joinHtmlTags(htmlTags.filter((h) => h.position === position));
|
|
11
|
+
if (htmlFragment) {
|
|
12
|
+
htmlString = injectHtmlFragment(position, htmlFragment, htmlString);
|
|
13
|
+
}
|
|
19
14
|
return htmlString;
|
|
20
15
|
}
|
|
21
|
-
function
|
|
16
|
+
async function injectHtmlTagsUsingStream(htmlTags, streamFromReactStreamingPackage) {
|
|
17
|
+
const htmlFragment = joinHtmlTags(htmlTags.filter((h) => h.position === 'STREAM'));
|
|
18
|
+
if (htmlFragment) {
|
|
19
|
+
assert(streamFromReactStreamingPackage);
|
|
20
|
+
assert(!streamFromReactStreamingPackage.hasStreamEnded());
|
|
21
|
+
await streamFromReactStreamingPackage.injectToStream(htmlFragment, { flush: true });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function joinHtmlTags(htmlTags) {
|
|
25
|
+
const htmlFragment = htmlTags.map((h) => resolveHtmlTag(h.htmlTag)).join('');
|
|
26
|
+
return htmlFragment;
|
|
27
|
+
}
|
|
28
|
+
function injectHtmlFragment(position, htmlFragment, htmlString) {
|
|
22
29
|
if (position === 'HTML_BEGIN') {
|
|
23
30
|
{
|
|
24
31
|
const res = injectAtPaceholder(htmlFragment, htmlString, true);
|
|
@@ -43,11 +50,6 @@ function injectHtmlFragment(position, htmlFragment, htmlString, injectToStream)
|
|
|
43
50
|
}
|
|
44
51
|
return htmlString + '\n' + htmlFragment;
|
|
45
52
|
}
|
|
46
|
-
if (position === 'STREAM') {
|
|
47
|
-
assert(injectToStream);
|
|
48
|
-
injectToStream(htmlFragment, { flush: true });
|
|
49
|
-
return htmlString;
|
|
50
|
-
}
|
|
51
53
|
assert(false);
|
|
52
54
|
}
|
|
53
55
|
function resolveHtmlTag(htmlTag) {
|
|
@@ -5,7 +5,7 @@ export type { PageContextPromise };
|
|
|
5
5
|
import type { PageAsset } from '../renderPage/getPageAssets.js';
|
|
6
6
|
import type { HtmlPart } from './renderHtml.js';
|
|
7
7
|
import { type PreloadFilter } from './injectAssets/getHtmlTags.js';
|
|
8
|
-
import type {
|
|
8
|
+
import type { StreamFromReactStreamingPackage } from './stream/react-streaming.js';
|
|
9
9
|
import type { PageConfigRuntime } from '../../../shared/page-configs/PageConfig.js';
|
|
10
10
|
import type { PageContextSerialization } from './serializePageContextClientSide.js';
|
|
11
11
|
type PageContextInjectAssets = {
|
|
@@ -27,7 +27,7 @@ declare function injectHtmlTagsToString(htmlParts: HtmlPart[], pageContext: Page
|
|
|
27
27
|
}, injectFilter: PreloadFilter): Promise<string>;
|
|
28
28
|
declare function injectHtmlTagsToStream(pageContext: PageContextInjectAssets & {
|
|
29
29
|
_isStream: true;
|
|
30
|
-
},
|
|
30
|
+
}, streamFromReactStreamingPackage: null | StreamFromReactStreamingPackage, injectFilter: PreloadFilter): {
|
|
31
31
|
injectAtStreamBegin: (htmlPartsBegin: HtmlPart[]) => Promise<string>;
|
|
32
32
|
injectAtStreamEnd: (htmlPartsEnd: HtmlPart[]) => Promise<string>;
|
|
33
33
|
};
|
|
@@ -2,27 +2,32 @@ 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 } from './injectAssets/injectHtmlTags.js';
|
|
5
|
+
import { injectHtmlTags, createHtmlHeadIfMissing, injectHtmlTagsUsingStream } from './injectAssets/injectHtmlTags.js';
|
|
6
6
|
import { getHtmlTags } from './injectAssets/getHtmlTags.js';
|
|
7
|
+
import { getViteDevScript } from './injectAssets/getViteDevScript.js';
|
|
7
8
|
async function injectHtmlTagsToString(htmlParts, pageContext, injectFilter) {
|
|
8
|
-
const htmlTags = await getHtmlTags(pageContext, null, injectFilter);
|
|
9
9
|
const pageAssets = await pageContext.__getPageAssets();
|
|
10
|
+
const viteDevScript = await getViteDevScript();
|
|
11
|
+
const htmlTags = getHtmlTags(pageContext, null, injectFilter, pageAssets, viteDevScript);
|
|
10
12
|
let htmlString = htmlPartsToString(htmlParts, pageAssets);
|
|
11
|
-
htmlString = injectToHtmlBegin(htmlString, htmlTags
|
|
13
|
+
htmlString = injectToHtmlBegin(htmlString, htmlTags);
|
|
12
14
|
htmlString = injectToHtmlEnd(htmlString, htmlTags);
|
|
15
|
+
assert(htmlTags.filter((snippet) => snippet.position === 'STREAM').length === 0);
|
|
13
16
|
return htmlString;
|
|
14
17
|
}
|
|
15
|
-
function injectHtmlTagsToStream(pageContext,
|
|
18
|
+
function injectHtmlTagsToStream(pageContext, streamFromReactStreamingPackage, injectFilter) {
|
|
16
19
|
let htmlTags;
|
|
17
20
|
return {
|
|
18
21
|
injectAtStreamBegin,
|
|
19
22
|
injectAtStreamEnd
|
|
20
23
|
};
|
|
21
24
|
async function injectAtStreamBegin(htmlPartsBegin) {
|
|
22
|
-
htmlTags = await getHtmlTags(pageContext, injectToStream, injectFilter);
|
|
23
25
|
const pageAssets = await pageContext.__getPageAssets();
|
|
26
|
+
const viteDevScript = await getViteDevScript();
|
|
27
|
+
htmlTags = getHtmlTags(pageContext, streamFromReactStreamingPackage, injectFilter, pageAssets, viteDevScript);
|
|
24
28
|
let htmlBegin = htmlPartsToString(htmlPartsBegin, pageAssets);
|
|
25
|
-
htmlBegin = injectToHtmlBegin(htmlBegin, htmlTags
|
|
29
|
+
htmlBegin = injectToHtmlBegin(htmlBegin, htmlTags);
|
|
30
|
+
await injectHtmlTagsUsingStream(htmlTags, streamFromReactStreamingPackage);
|
|
26
31
|
return htmlBegin;
|
|
27
32
|
}
|
|
28
33
|
async function injectAtStreamEnd(htmlPartsEnd) {
|
|
@@ -34,16 +39,14 @@ function injectHtmlTagsToStream(pageContext, injectToStream, injectFilter) {
|
|
|
34
39
|
return htmlEnd;
|
|
35
40
|
}
|
|
36
41
|
}
|
|
37
|
-
function injectToHtmlBegin(htmlBegin, htmlTags
|
|
38
|
-
const htmlTagsAtBegin = htmlTags.filter((snippet) => snippet.position !== 'HTML_END');
|
|
42
|
+
function injectToHtmlBegin(htmlBegin, htmlTags) {
|
|
39
43
|
// Ensure existence of `<head>`
|
|
40
44
|
htmlBegin = createHtmlHeadIfMissing(htmlBegin);
|
|
41
|
-
htmlBegin = injectHtmlTags(htmlBegin,
|
|
45
|
+
htmlBegin = injectHtmlTags(htmlBegin, htmlTags, 'HTML_BEGIN');
|
|
42
46
|
return htmlBegin;
|
|
43
47
|
}
|
|
44
48
|
function injectToHtmlEnd(htmlEnd, htmlTags) {
|
|
45
|
-
|
|
46
|
-
htmlEnd = injectHtmlTags(htmlEnd, htmlTagsAtEnd, null);
|
|
49
|
+
htmlEnd = injectHtmlTags(htmlEnd, htmlTags, 'HTML_END');
|
|
47
50
|
return htmlEnd;
|
|
48
51
|
}
|
|
49
52
|
async function resolvePageContextPromise(pageContext) {
|
|
@@ -6,7 +6,7 @@ export { getHtmlString };
|
|
|
6
6
|
import { assert, assertUsage, assertWarning, checkType, escapeHtml, hasProp, isHtml, isPromise, objectAssign } from '../utils.js';
|
|
7
7
|
import { injectHtmlTagsToString, injectHtmlTagsToStream } from './injectAssets.js';
|
|
8
8
|
import { processStream, isStream, streamToString } from './stream.js';
|
|
9
|
-
import {
|
|
9
|
+
import { isStreamFromReactStreamingPackage } from './stream/react-streaming.js';
|
|
10
10
|
import { getGlobalContext } from '../globalContext.js';
|
|
11
11
|
import pc from '@brillout/picocolors';
|
|
12
12
|
function isDocumentHtml(something) {
|
|
@@ -57,11 +57,11 @@ async function renderHtmlStream(streamOriginal, injectString, pageContext, onErr
|
|
|
57
57
|
enableEagerStreaming: pageContext.enableEagerStreaming
|
|
58
58
|
};
|
|
59
59
|
if (injectString) {
|
|
60
|
-
let
|
|
61
|
-
if (
|
|
62
|
-
|
|
60
|
+
let streamFromReactStreamingPackage = null;
|
|
61
|
+
if (isStreamFromReactStreamingPackage(streamOriginal) && !streamOriginal.disabled) {
|
|
62
|
+
streamFromReactStreamingPackage = streamOriginal;
|
|
63
63
|
}
|
|
64
|
-
const { injectAtStreamBegin, injectAtStreamEnd } = injectHtmlTagsToStream(pageContext,
|
|
64
|
+
const { injectAtStreamBegin, injectAtStreamEnd } = injectHtmlTagsToStream(pageContext, streamFromReactStreamingPackage, injectFilter);
|
|
65
65
|
objectAssign(opts, {
|
|
66
66
|
injectStringAtBegin: async () => {
|
|
67
67
|
return await injectAtStreamBegin(injectString.htmlPartsBegin);
|
|
@@ -1,25 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export type {
|
|
6
|
-
|
|
1
|
+
export { isStreamFromReactStreamingPackage };
|
|
2
|
+
export { streamFromReactStreamingPackageToString };
|
|
3
|
+
export { getStreamOfReactStreamingPackage };
|
|
4
|
+
export type { StreamFromReactStreamingPackage };
|
|
5
|
+
export type { StreamFromReactStreamingPackagePublic };
|
|
6
|
+
import type { renderToStream } from 'react-streaming/server';
|
|
7
7
|
import { StreamReadableWeb, StreamWritableNode } from '../stream.js';
|
|
8
|
-
type
|
|
9
|
-
|
|
10
|
-
}) => void;
|
|
11
|
-
type StreamReactStreaming = {
|
|
12
|
-
injectToStream: InjectToStream;
|
|
13
|
-
disabled?: boolean;
|
|
14
|
-
} & ({
|
|
15
|
-
pipe: (writable: StreamWritableNode) => void;
|
|
16
|
-
readable: null;
|
|
17
|
-
} | {
|
|
18
|
-
pipe: null;
|
|
19
|
-
readable: StreamReadableWeb;
|
|
20
|
-
});
|
|
21
|
-
declare function streamReactStreamingToString(stream: StreamReactStreaming): Promise<string>;
|
|
22
|
-
declare function isStreamReactStreaming(thing: unknown): thing is StreamReactStreaming;
|
|
23
|
-
declare function getStreamFromReactStreaming(stream: StreamReactStreaming): StreamReadableWeb | {
|
|
24
|
-
__streamPipeNode: (writable: import("stream").Writable) => void;
|
|
8
|
+
type StreamFromReactStreamingPackagePublic = {
|
|
9
|
+
injectToStream: Function;
|
|
25
10
|
};
|
|
11
|
+
type StreamFromReactStreamingPackage = Awaited<ReturnType<typeof renderToStream>>;
|
|
12
|
+
declare function streamFromReactStreamingPackageToString(stream: StreamFromReactStreamingPackage): Promise<string>;
|
|
13
|
+
declare function isStreamFromReactStreamingPackage(thing: unknown): thing is StreamFromReactStreamingPackage;
|
|
14
|
+
type Pipe = {
|
|
15
|
+
__streamPipeNode: (writable: StreamWritableNode) => void;
|
|
16
|
+
};
|
|
17
|
+
type Readable = StreamReadableWeb;
|
|
18
|
+
declare function getStreamOfReactStreamingPackage(stream: StreamFromReactStreamingPackage): Pipe | Readable;
|