vike 0.4.147 → 0.4.148-commit-7596dcd
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/getConfigValuesSerialized.js +17 -12
- package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.js +3 -0
- package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/crawlPlusFiles.js +116 -0
- package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.js +33 -45
- package/dist/cjs/node/prerender/runPrerender.js +85 -84
- package/dist/cjs/node/runtime/html/injectAssets/injectAssets__public.js +1 -1
- package/dist/cjs/node/runtime/html/renderHtml.js +1 -1
- package/dist/cjs/node/runtime/renderPage/createHttpResponseObject/assertNoInfiniteHttpRedirect.js +12 -12
- package/dist/cjs/node/runtime/renderPage/createHttpResponseObject.js +3 -3
- package/dist/cjs/node/runtime/renderPage/executeOnBeforeRenderHook.js +1 -1
- package/dist/cjs/node/runtime/renderPage/executeOnRenderHtmlHook.js +3 -3
- package/dist/cjs/node/runtime/renderPage/getHttpResponseBody.js +1 -1
- package/dist/cjs/node/runtime/renderPage/renderPageAlreadyRouted.js +21 -18
- package/dist/cjs/node/runtime/renderPage.js +73 -49
- package/dist/cjs/shared/getPageFiles/parseGlobResults.js +3 -3
- package/dist/cjs/shared/hooks/executeHook.js +18 -29
- package/dist/cjs/shared/hooks/getHook.js +104 -3
- package/dist/cjs/shared/page-configs/helpers/getConfigDefinedAtString.js +1 -1
- package/dist/cjs/shared/route/executeGuardHook.js +3 -2
- package/dist/cjs/shared/route/executeOnBeforeRouteHook.js +4 -4
- package/dist/cjs/shared/route/loadPageRoutes.js +10 -15
- package/dist/cjs/shared/route/resolveRedirects.js +8 -5
- package/dist/cjs/utils/parseUrl-extras.js +6 -1
- package/dist/cjs/utils/parseUrl.js +24 -16
- package/dist/cjs/utils/projectInfo.js +1 -1
- package/dist/esm/client/client-routing-runtime/createPageContext.d.ts +1 -1
- package/dist/esm/client/client-routing-runtime/getPageContextFromHooks.js +20 -10
- package/dist/esm/client/client-routing-runtime/onBrowserHistoryNavigation.js +2 -2
- package/dist/esm/client/client-routing-runtime/renderPageClientSide.js +18 -12
- package/dist/esm/client/shared/executeOnRenderClientHook.js +1 -1
- package/dist/esm/client/shared/getPageContextSerializedInHtml.js +1 -1
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getConfigValuesSerialized.js +17 -12
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.js +3 -0
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/crawlPlusFiles.d.ts +5 -0
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/crawlPlusFiles.js +110 -0
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.js +34 -46
- package/dist/esm/node/prerender/runPrerender.js +87 -86
- package/dist/esm/node/runtime/html/injectAssets/injectAssets__public.js +1 -1
- package/dist/esm/node/runtime/html/renderHtml.js +1 -1
- package/dist/esm/node/runtime/renderPage/createHttpResponseObject/assertNoInfiniteHttpRedirect.d.ts +1 -1
- package/dist/esm/node/runtime/renderPage/createHttpResponseObject/assertNoInfiniteHttpRedirect.js +13 -13
- package/dist/esm/node/runtime/renderPage/createHttpResponseObject.d.ts +1 -1
- package/dist/esm/node/runtime/renderPage/createHttpResponseObject.js +3 -3
- package/dist/esm/node/runtime/renderPage/executeOnBeforeRenderHook.js +1 -1
- package/dist/esm/node/runtime/renderPage/executeOnRenderHtmlHook.d.ts +2 -2
- package/dist/esm/node/runtime/renderPage/executeOnRenderHtmlHook.js +3 -3
- package/dist/esm/node/runtime/renderPage/getHttpResponseBody.js +1 -1
- package/dist/esm/node/runtime/renderPage/renderPageAlreadyRouted.d.ts +7 -7
- package/dist/esm/node/runtime/renderPage/renderPageAlreadyRouted.js +22 -19
- package/dist/esm/node/runtime/renderPage.js +74 -50
- package/dist/esm/shared/getPageFiles/parseGlobResults.js +1 -1
- package/dist/esm/shared/hooks/executeHook.d.ts +2 -2
- package/dist/esm/shared/hooks/executeHook.js +18 -29
- package/dist/esm/shared/hooks/getHook.d.ts +17 -7
- package/dist/esm/shared/hooks/getHook.js +103 -3
- package/dist/esm/shared/page-configs/Config.d.ts +21 -13
- package/dist/esm/shared/page-configs/helpers/getConfigDefinedAtString.d.ts +1 -1
- package/dist/esm/shared/page-configs/helpers/getConfigDefinedAtString.js +1 -1
- package/dist/esm/shared/route/executeGuardHook.js +4 -3
- package/dist/esm/shared/route/executeOnBeforeRouteHook.d.ts +1 -8
- package/dist/esm/shared/route/executeOnBeforeRouteHook.js +6 -6
- package/dist/esm/shared/route/index.d.ts +2 -2
- package/dist/esm/shared/route/loadPageRoutes.d.ts +2 -2
- package/dist/esm/shared/route/loadPageRoutes.js +11 -16
- package/dist/esm/shared/route/resolveRedirects.js +8 -5
- package/dist/esm/utils/parseUrl-extras.d.ts +2 -0
- package/dist/esm/utils/parseUrl-extras.js +5 -0
- package/dist/esm/utils/parseUrl.js +24 -16
- package/dist/esm/utils/projectInfo.d.ts +2 -2
- package/dist/esm/utils/projectInfo.js +1 -1
- package/package.json +3 -3
- /package/dist/cjs/shared/page-configs/serialize/{assertPageConfigs.js → assertPageConfigsSerialized.js} +0 -0
- /package/dist/esm/shared/page-configs/serialize/{assertPageConfigs.d.ts → assertPageConfigsSerialized.d.ts} +0 -0
- /package/dist/esm/shared/page-configs/serialize/{assertPageConfigs.js → assertPageConfigsSerialized.js} +0 -0
|
@@ -12,7 +12,7 @@ async function executeOnBeforeRouteHook(pageContext) {
|
|
|
12
12
|
const pageContextFromOnBeforeRouteHook = {};
|
|
13
13
|
if (!pageContext._onBeforeRouteHook)
|
|
14
14
|
return null;
|
|
15
|
-
const pageContextFromHook = await
|
|
15
|
+
const pageContextFromHook = await getPageContextFromHook(pageContext._onBeforeRouteHook, pageContext);
|
|
16
16
|
if (pageContextFromHook) {
|
|
17
17
|
(0, utils_js_1.objectAssign)(pageContextFromOnBeforeRouteHook, pageContextFromHook);
|
|
18
18
|
if ((0, utils_js_1.hasProp)(pageContextFromOnBeforeRouteHook, '_pageId', 'string') ||
|
|
@@ -37,11 +37,11 @@ async function executeOnBeforeRouteHook(pageContext) {
|
|
|
37
37
|
return pageContextFromOnBeforeRouteHook;
|
|
38
38
|
}
|
|
39
39
|
exports.executeOnBeforeRouteHook = executeOnBeforeRouteHook;
|
|
40
|
-
async function
|
|
41
|
-
let hookReturn = onBeforeRouteHook.
|
|
40
|
+
async function getPageContextFromHook(onBeforeRouteHook, pageContext) {
|
|
41
|
+
let hookReturn = onBeforeRouteHook.hookFn(pageContext);
|
|
42
42
|
(0, resolveRouteFunction_js_1.assertSyncRouting)(hookReturn, `The onBeforeRoute() hook ${onBeforeRouteHook.hookFilePath}`);
|
|
43
43
|
// TODO/v1-release: make executeOnBeforeRouteHook() and route() sync
|
|
44
|
-
hookReturn = await hookReturn;
|
|
44
|
+
hookReturn = await (0, utils_js_1.executeHook)(() => hookReturn, onBeforeRouteHook);
|
|
45
45
|
const errPrefix = `The onBeforeRoute() hook defined by ${onBeforeRouteHook.hookFilePath}`;
|
|
46
46
|
(0, utils_js_1.assertUsage)(hookReturn === null ||
|
|
47
47
|
hookReturn === undefined ||
|
|
@@ -7,6 +7,7 @@ const deduceRouteStringFromFilesystemPath_js_1 = require("./deduceRouteStringFro
|
|
|
7
7
|
const utils_js_2 = require("../utils.js");
|
|
8
8
|
const helpers_js_1 = require("../page-configs/helpers.js");
|
|
9
9
|
const resolveRouteFunction_js_1 = require("./resolveRouteFunction.js");
|
|
10
|
+
const getHook_js_1 = require("../hooks/getHook.js");
|
|
10
11
|
async function loadPageRoutes(
|
|
11
12
|
// Remove all arguments and use GlobalContext instead?
|
|
12
13
|
pageFilesAll, pageConfigs, pageConfigGlobal, allPageIds) {
|
|
@@ -139,20 +140,8 @@ function getPageRoutes(filesystemRoots, pageFilesAll, pageConfigs, allPageIds) {
|
|
|
139
140
|
function getGlobalHooks(pageFilesAll, pageConfigs, pageConfigGlobal) {
|
|
140
141
|
// V1 Design
|
|
141
142
|
if (pageConfigs.length > 0) {
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
const configValue = pageConfigGlobal.configValues[hookName];
|
|
145
|
-
const { value: hookFn } = configValue;
|
|
146
|
-
const hookFilePath = (0, helpers_js_1.getHookFilePathToShowToUser)(configValue);
|
|
147
|
-
const hookDefinedAt = (0, helpers_js_1.getConfigDefinedAtString)('Hook', hookName, configValue);
|
|
148
|
-
(0, utils_js_1.assertUsage)((0, utils_js_2.isCallable)(hookFn), `${hookDefinedAt} should be a function.`);
|
|
149
|
-
const onBeforeRouteHook = {
|
|
150
|
-
hookFilePath: hookFilePath,
|
|
151
|
-
onBeforeRoute: hookFn
|
|
152
|
-
};
|
|
153
|
-
return { onBeforeRouteHook, filesystemRoots: null };
|
|
154
|
-
}
|
|
155
|
-
return { onBeforeRouteHook: null, filesystemRoots: null };
|
|
143
|
+
const hook = (0, getHook_js_1.getHookFromPageConfigGlobal)(pageConfigGlobal, 'onBeforeRoute');
|
|
144
|
+
return { onBeforeRouteHook: hook, filesystemRoots: null };
|
|
156
145
|
}
|
|
157
146
|
// Old design
|
|
158
147
|
// TODO/v1-release: remove
|
|
@@ -165,7 +154,13 @@ function getGlobalHooks(pageFilesAll, pageConfigs, pageConfigGlobal) {
|
|
|
165
154
|
if ('onBeforeRoute' in fileExports) {
|
|
166
155
|
(0, utils_js_1.assertUsage)((0, utils_js_1.hasProp)(fileExports, 'onBeforeRoute', 'function'), `\`export { onBeforeRoute }\` of ${filePath} should be a function.`);
|
|
167
156
|
const { onBeforeRoute } = fileExports;
|
|
168
|
-
|
|
157
|
+
const hookName = 'onBeforeRoute';
|
|
158
|
+
onBeforeRouteHook = {
|
|
159
|
+
hookFilePath: filePath,
|
|
160
|
+
hookFn: onBeforeRoute,
|
|
161
|
+
hookName,
|
|
162
|
+
hookTimeout: (0, getHook_js_1.getHookTimeoutDefault)(hookName)
|
|
163
|
+
};
|
|
169
164
|
}
|
|
170
165
|
if ('filesystemRoutingRoot' in fileExports) {
|
|
171
166
|
(0, utils_js_1.assertUsage)((0, utils_js_1.hasProp)(fileExports, 'filesystemRoutingRoot', 'string'), `\`export { filesystemRoutingRoot }\` of ${filePath} should be a string.`);
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.resolveRouteStringRedirect = exports.resolveRedirects = void 0;
|
|
7
7
|
const assertIsNotBrowser_js_1 = require("../../utils/assertIsNotBrowser.js");
|
|
8
|
+
const parseUrl_extras_js_1 = require("../../utils/parseUrl-extras.js");
|
|
8
9
|
const utils_js_1 = require("../utils.js");
|
|
9
10
|
const resolveRouteString_js_1 = require("./resolveRouteString.js");
|
|
10
11
|
const picocolors_1 = __importDefault(require("@brillout/picocolors"));
|
|
@@ -23,9 +24,9 @@ exports.resolveRedirects = resolveRedirects;
|
|
|
23
24
|
function resolveRouteStringRedirect(urlSource, urlTarget, urlPathname) {
|
|
24
25
|
(0, resolveRouteString_js_1.assertRouteString)(urlSource, `${configSrc} Invalid`);
|
|
25
26
|
(0, utils_js_1.assertUsage)(urlTarget.startsWith('/') ||
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
urlTarget === '*', `${configSrc} Invalid redirection target URL ${picocolors_1.default.cyan(urlTarget)}: the target URL should start with ${picocolors_1.default.cyan('/')}, ${picocolors_1.default.cyan('http
|
|
27
|
+
// Is allowing any protocol a safety issue? https://github.com/vikejs/vike/pull/1292#issuecomment-1828043917
|
|
28
|
+
(0, parseUrl_extras_js_1.isUriWithProtocol)(urlTarget) ||
|
|
29
|
+
urlTarget === '*', `${configSrc} Invalid redirection target URL ${picocolors_1.default.cyan(urlTarget)}: the target URL should start with ${picocolors_1.default.cyan('/')}, a valid protocol (${picocolors_1.default.cyan('https:')}, ${picocolors_1.default.cyan('http:')}, ${picocolors_1.default.cyan('ipfs:')}, ${picocolors_1.default.cyan('magnet:')}, ...), or be ${picocolors_1.default.cyan('*')}`);
|
|
29
30
|
assertParams(urlSource, urlTarget);
|
|
30
31
|
const match = (0, resolveRouteString_js_1.resolveRouteString)(urlSource, urlPathname);
|
|
31
32
|
if (!match)
|
|
@@ -37,10 +38,12 @@ function resolveRouteStringRedirect(urlSource, urlTarget, urlPathname) {
|
|
|
37
38
|
}
|
|
38
39
|
urlResolved = urlResolved.replaceAll(key, val);
|
|
39
40
|
});
|
|
40
|
-
|
|
41
|
+
if (!urlResolved.startsWith('mailto:')) {
|
|
42
|
+
(0, utils_js_1.assertUsage)(!urlResolved.includes('@'), 'URL should not contain "@" unless it is a mailto link.');
|
|
43
|
+
}
|
|
41
44
|
if (urlResolved === urlPathname)
|
|
42
45
|
return null;
|
|
43
|
-
(0, utils_js_1.assert)(
|
|
46
|
+
(0, utils_js_1.assert)(urlResolved.startsWith('/') || (0, parseUrl_extras_js_1.isUriWithProtocol)(urlResolved));
|
|
44
47
|
return urlResolved;
|
|
45
48
|
}
|
|
46
49
|
exports.resolveRouteStringRedirect = resolveRouteStringRedirect;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.addUrlOrigin = exports.removeUrlOrigin = exports.modifyUrlPathname = exports.removeBaseServer = exports.normalizeUrlPathname = exports.isBaseAssets = exports.prependBase = void 0;
|
|
3
|
+
exports.isUriWithProtocol = exports.addUrlOrigin = exports.removeUrlOrigin = exports.modifyUrlPathname = exports.removeBaseServer = exports.normalizeUrlPathname = exports.isBaseAssets = exports.prependBase = void 0;
|
|
4
4
|
const parseUrl_js_1 = require("./parseUrl.js");
|
|
5
5
|
const assert_js_1 = require("./assert.js");
|
|
6
6
|
const slice_js_1 = require("./slice.js");
|
|
@@ -103,3 +103,8 @@ function addUrlOrigin(url, origin) {
|
|
|
103
103
|
return urlModified;
|
|
104
104
|
}
|
|
105
105
|
exports.addUrlOrigin = addUrlOrigin;
|
|
106
|
+
function isUriWithProtocol(uri) {
|
|
107
|
+
// https://en.wikipedia.org/wiki/List_of_URI_schemes
|
|
108
|
+
return /^[a-z0-9][a-z0-9\.\+\-]*:/i.test(uri);
|
|
109
|
+
}
|
|
110
|
+
exports.isUriWithProtocol = isUriWithProtocol;
|
|
@@ -55,7 +55,7 @@ function parseUrl(url, baseServer) {
|
|
|
55
55
|
searchAll[key] = [...(searchAll.hasOwnProperty(key) ? searchAll[key] : []), val];
|
|
56
56
|
});
|
|
57
57
|
// Origin + pathname
|
|
58
|
-
const { origin, pathname: pathnameResolved } =
|
|
58
|
+
const { origin, pathname: pathnameResolved } = getPathname(urlWithoutHashNorSearch, baseServer);
|
|
59
59
|
(0, assert_js_1.assert)(origin === null || origin === decodeSafe(origin)); // AFAICT decoding the origin is useless
|
|
60
60
|
(0, assert_js_1.assert)(pathnameResolved.startsWith('/'));
|
|
61
61
|
(0, assert_js_1.assert)(origin === null || url.startsWith(origin));
|
|
@@ -91,35 +91,43 @@ function decodeSafe(urlComponent) {
|
|
|
91
91
|
return urlComponent;
|
|
92
92
|
}
|
|
93
93
|
function decodePathname(urlPathname) {
|
|
94
|
+
urlPathname = urlPathname.replace(/\s+$/, '');
|
|
94
95
|
urlPathname = urlPathname
|
|
95
96
|
.split('/')
|
|
96
97
|
.map((dir) => decodeSafe(dir).split('/').join('%2F'))
|
|
97
98
|
.join('/');
|
|
98
|
-
urlPathname = urlPathname.replace(/\s/g, '');
|
|
99
99
|
return urlPathname;
|
|
100
100
|
}
|
|
101
|
-
function
|
|
101
|
+
function getPathname(url, baseServer) {
|
|
102
|
+
// Search and hash already extracted
|
|
103
|
+
(0, assert_js_1.assert)(!url.includes('?') && !url.includes('#'));
|
|
104
|
+
// url has origin
|
|
102
105
|
{
|
|
103
|
-
const { origin, pathname } = parseOrigin(
|
|
106
|
+
const { origin, pathname } = parseOrigin(url);
|
|
104
107
|
if (origin) {
|
|
105
108
|
return { origin, pathname };
|
|
106
109
|
}
|
|
107
|
-
(0, assert_js_1.assert)(pathname ===
|
|
110
|
+
(0, assert_js_1.assert)(pathname === url);
|
|
108
111
|
}
|
|
109
|
-
|
|
110
|
-
|
|
112
|
+
// url doesn't have origin
|
|
113
|
+
if (url.startsWith('/')) {
|
|
114
|
+
return { origin: null, pathname: url };
|
|
111
115
|
}
|
|
112
116
|
else {
|
|
113
|
-
//
|
|
117
|
+
// url is a relative path
|
|
118
|
+
// In the browser, this is the Base URL of the current URL.
|
|
114
119
|
// Safe access `window?.document?.baseURI` for users who shim `window` in Node.js
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
const baseURI = typeof window !== 'undefined' ? window?.document?.baseURI : undefined;
|
|
121
|
+
let base;
|
|
122
|
+
if (baseURI) {
|
|
123
|
+
const baseURIPathaname = parseOrigin(baseURI.split('?')[0]).pathname;
|
|
124
|
+
base = baseURIPathaname;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
base = baseServer;
|
|
128
|
+
}
|
|
129
|
+
const pathname = resolveUrlPathnameRelative(url, base);
|
|
130
|
+
return { origin: null, pathname };
|
|
123
131
|
}
|
|
124
132
|
}
|
|
125
133
|
function parseOrigin(url) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PROJECT_VERSION = exports.projectInfo = void 0;
|
|
4
4
|
const assertSingleInstance_js_1 = require("./assertSingleInstance.js");
|
|
5
|
-
const PROJECT_VERSION = '0.4.
|
|
5
|
+
const PROJECT_VERSION = '0.4.148-commit-7596dcd';
|
|
6
6
|
exports.PROJECT_VERSION = PROJECT_VERSION;
|
|
7
7
|
const projectInfo = {
|
|
8
8
|
projectName: 'Vike',
|
|
@@ -11,7 +11,7 @@ declare function createPageContext(urlOriginal: string): Promise<{
|
|
|
11
11
|
_pageConfigGlobal: import("../../shared/page-configs/PageConfig.js").PageConfigGlobalRuntime;
|
|
12
12
|
_allPageIds: string[];
|
|
13
13
|
_pageRoutes: import("../../shared/route/loadPageRoutes.js").PageRoutes;
|
|
14
|
-
_onBeforeRouteHook: import("../../shared/
|
|
14
|
+
_onBeforeRouteHook: import("../../shared/hooks/getHook.js").Hook | null;
|
|
15
15
|
} & import("../../shared/addUrlComputedProps.js").PageContextUrlComputedPropsClient & {
|
|
16
16
|
_urlRewrite: string | null;
|
|
17
17
|
}>;
|
|
@@ -27,7 +27,7 @@ async function getPageContextFromHooks_firstRender(pageContext) {
|
|
|
27
27
|
objectAssign(pageContextFromHooks, await loadPageFilesClientSide(pageContextFromHooks._pageId, pageContext));
|
|
28
28
|
{
|
|
29
29
|
const pageContextForHook = { ...pageContext, ...pageContextFromHooks };
|
|
30
|
-
if (
|
|
30
|
+
if (onBeforeRenderClientOnlyExists(pageContextForHook)) {
|
|
31
31
|
const pageContextFromHook = await executeOnBeforeRenderHookClientSide(pageContextForHook);
|
|
32
32
|
objectAssign(pageContextFromHooks, pageContextFromHook);
|
|
33
33
|
}
|
|
@@ -57,13 +57,17 @@ async function getPageContextFromHooks_uponNavigation(pageContext) {
|
|
|
57
57
|
}
|
|
58
58
|
async function getPageContextAlreadyRouted(pageContext, isErrorPage) {
|
|
59
59
|
let pageContextFromHooks = {};
|
|
60
|
+
objectAssign(pageContextFromHooks, { _hasPageContextFromClient: false });
|
|
60
61
|
objectAssign(pageContextFromHooks, await loadPageFilesClientSide(pageContext._pageId, pageContext));
|
|
62
|
+
let pageContextFetchedFromServer = false;
|
|
61
63
|
// Needs to be called before any client-side hook, because it may contain pageContextInit.user which is needed for guard() and onBeforeRender()
|
|
62
64
|
if (
|
|
63
65
|
// For the error page, we cannot fetch pageContext from the server because the pageContext JSON request is based on the URL
|
|
64
66
|
!isErrorPage &&
|
|
67
|
+
// true if pageContextInit has some client data or the onBeforeRender hook is server-side only:
|
|
65
68
|
(await hasPageContextServer({ ...pageContext, ...pageContextFromHooks }))) {
|
|
66
69
|
const pageContextFromServer = await fetchPageContextFromServer(pageContext);
|
|
70
|
+
pageContextFetchedFromServer = true;
|
|
67
71
|
if (!pageContextFromServer['_isError']) {
|
|
68
72
|
objectAssign(pageContextFromHooks, pageContextFromServer);
|
|
69
73
|
}
|
|
@@ -72,6 +76,7 @@ async function getPageContextAlreadyRouted(pageContext, isErrorPage) {
|
|
|
72
76
|
assert(errorPageId);
|
|
73
77
|
pageContextFromHooks = {};
|
|
74
78
|
objectAssign(pageContextFromHooks, {
|
|
79
|
+
_hasPageContextFromClient: false,
|
|
75
80
|
isHydration: false,
|
|
76
81
|
_pageId: errorPageId
|
|
77
82
|
});
|
|
@@ -91,25 +96,29 @@ async function getPageContextAlreadyRouted(pageContext, isErrorPage) {
|
|
|
91
96
|
if (!isErrorPage) {
|
|
92
97
|
// Should we really call the guard() hook on the client-side? Shouldn't we make the guard() hook a server-side only hook? Or maybe make its env configurable like onBeforeRender()?
|
|
93
98
|
await executeGuardHook({
|
|
94
|
-
_hasPageContextFromClient: false,
|
|
95
99
|
...pageContext,
|
|
96
100
|
...pageContextFromHooks
|
|
97
101
|
}, (pageContext) => preparePageContextForUserConsumptionClientSide(pageContext, true));
|
|
98
102
|
}
|
|
99
103
|
}
|
|
104
|
+
// For the error page, we also execute the client-side onBeforeRender() hook, but maybe we shouldn't? The server-side does it as well (but maybe it shouldn't).
|
|
100
105
|
{
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
const pageContextForHook = { ...pageContext, ...pageContextFromHooks };
|
|
107
|
+
if (onBeforeRenderClientOnlyExists(pageContextForHook) || !pageContextFetchedFromServer) {
|
|
108
|
+
// This won't do anything if no hook has been defined or if the hook's env.client is false.
|
|
109
|
+
const pageContextFromHook = await executeOnBeforeRenderHookClientSide(pageContextForHook);
|
|
110
|
+
objectAssign(pageContextFromHooks, pageContextFromHook);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
assert(pageContextFetchedFromServer);
|
|
114
|
+
}
|
|
107
115
|
}
|
|
108
116
|
return pageContextFromHooks;
|
|
109
117
|
}
|
|
110
118
|
async function executeOnBeforeRenderHookClientSide(pageContext) {
|
|
111
119
|
const hook = getHook(pageContext, 'onBeforeRender');
|
|
112
120
|
if (!hook) {
|
|
121
|
+
// No hook defined or hook's env.client is false
|
|
113
122
|
const pageContextFromOnBeforeRender = {
|
|
114
123
|
_hasPageContextFromClient: false
|
|
115
124
|
};
|
|
@@ -123,7 +132,7 @@ async function executeOnBeforeRenderHookClientSide(pageContext) {
|
|
|
123
132
|
...pageContext,
|
|
124
133
|
...pageContextFromOnBeforeRender
|
|
125
134
|
}, true);
|
|
126
|
-
const hookResult = await executeHook(() => onBeforeRender(pageContextForUserConsumption),
|
|
135
|
+
const hookResult = await executeHook(() => onBeforeRender(pageContextForUserConsumption), hook);
|
|
127
136
|
assertOnBeforeRenderHookReturn(hookResult, hook.hookFilePath);
|
|
128
137
|
const pageContextFromHook = hookResult?.pageContext;
|
|
129
138
|
objectAssign(pageContextFromOnBeforeRender, pageContextFromHook);
|
|
@@ -164,7 +173,7 @@ async function onBeforeRenderServerOnlyExists(pageContext) {
|
|
|
164
173
|
return hasOnBeforeRenderServerSideOnlyHook;
|
|
165
174
|
}
|
|
166
175
|
}
|
|
167
|
-
|
|
176
|
+
function onBeforeRenderClientOnlyExists(pageContext) {
|
|
168
177
|
if (pageContext._pageConfigs.length > 0) {
|
|
169
178
|
// V1
|
|
170
179
|
const pageConfig = getPageConfig(pageContext._pageId, pageContext._pageConfigs);
|
|
@@ -174,6 +183,7 @@ async function onBeforeRenderClientOnlyExists(pageContext) {
|
|
|
174
183
|
}
|
|
175
184
|
else {
|
|
176
185
|
// TODO/v1-release: remove
|
|
186
|
+
// Client-only onBeforeRender() hooks were never supported for the V0.4 design
|
|
177
187
|
return false;
|
|
178
188
|
}
|
|
179
189
|
}
|
|
@@ -14,8 +14,8 @@ function onBrowserHistoryNavigation() {
|
|
|
14
14
|
// - By user clicking on a hash link `<a href="#some-hash" />`
|
|
15
15
|
// - The popstate event is *only* triggered if `href` starts with '#' (even if `href` is '/#some-hash' while the current URL's pathname is '/' then the popstate still isn't triggered)
|
|
16
16
|
// - By JavaScript: `location.hash = 'some-hash'`
|
|
17
|
-
// - The `event` of `window.addEventListener('popstate', (event) => /*...*/)` is useless: the History API doesn't provide the previous state (the popped state), see https://stackoverflow.com/questions/48055323/is-history-state-always-the-same-as-popstate-event-state
|
|
18
|
-
window.addEventListener('popstate', () => {
|
|
17
|
+
// - The `event` argument of `window.addEventListener('popstate', (event) => /*...*/)` is useless: the History API doesn't provide the previous state (the popped state), see https://stackoverflow.com/questions/48055323/is-history-state-always-the-same-as-popstate-event-state
|
|
18
|
+
window.addEventListener('popstate', (ev) => {
|
|
19
19
|
const currentState = getState();
|
|
20
20
|
const scrollTarget = currentState.historyState.scrollPosition || 'scroll-to-top-or-hash';
|
|
21
21
|
const isUserLandPushStateNavigation = currentState.historyState.triggedBy === 'user';
|
|
@@ -7,7 +7,7 @@ import { createPageContext } from './createPageContext.js';
|
|
|
7
7
|
import { addLinkPrefetchHandlers } from './prefetch.js';
|
|
8
8
|
import { assertInfo, assertWarning, isReact } from './utils.js';
|
|
9
9
|
import { executeOnRenderClientHook } from '../shared/executeOnRenderClientHook.js';
|
|
10
|
-
import { assertHook } from '../../shared/hooks/getHook.js';
|
|
10
|
+
import { assertHook, getHook } from '../../shared/hooks/getHook.js';
|
|
11
11
|
import { isErrorFetchingStaticAssets } from '../shared/loadPageFilesClientSide.js';
|
|
12
12
|
import { pushHistory } from './history.js';
|
|
13
13
|
import { assertNoInfiniteAbortLoop, getPageContextFromAllRewrites, isAbortError, logAbortErrorHandled } from '../../shared/route/abort.js';
|
|
@@ -85,7 +85,11 @@ async function renderPageClientSide(renderArgs) {
|
|
|
85
85
|
const callTransitionHooks = !isFirstRender;
|
|
86
86
|
if (callTransitionHooks) {
|
|
87
87
|
if (!globalObject.isTransitioning) {
|
|
88
|
-
|
|
88
|
+
if (globalObject.onPageTransitionStart) {
|
|
89
|
+
const hook = globalObject.onPageTransitionStart;
|
|
90
|
+
const { hookFn } = hook;
|
|
91
|
+
await executeHook(() => hookFn(pageContext), hook);
|
|
92
|
+
}
|
|
89
93
|
globalObject.isTransitioning = true;
|
|
90
94
|
if (abortRender())
|
|
91
95
|
return;
|
|
@@ -207,13 +211,14 @@ async function renderPageClientSide(renderArgs) {
|
|
|
207
211
|
objectAssign(pageContext, pageContextFromHooks);
|
|
208
212
|
// Set global onPageTransitionStart()
|
|
209
213
|
assertHook(pageContext, 'onPageTransitionStart');
|
|
210
|
-
|
|
214
|
+
const onPageTransitionStartHook = getHook(pageContext, 'onPageTransitionStart');
|
|
215
|
+
globalObject.onPageTransitionStart = onPageTransitionStartHook;
|
|
211
216
|
// Set global hydrationCanBeAborted
|
|
212
217
|
if (pageContext.exports.hydrationCanBeAborted) {
|
|
213
218
|
setHydrationCanBeAborted();
|
|
214
219
|
}
|
|
215
220
|
else {
|
|
216
|
-
assertWarning(!isReact(), 'You seem to be using React; we recommend setting hydrationCanBeAborted to true, see https://vike.dev/
|
|
221
|
+
assertWarning(!isReact(), 'You seem to be using React; we recommend setting hydrationCanBeAborted to true, see https://vike.dev/hydrationCanBeAborted', { onlyOnce: true });
|
|
217
222
|
}
|
|
218
223
|
// There wasn't any `await` but result may change because we just called setHydrationCanBeAborted()
|
|
219
224
|
if (abortRender())
|
|
@@ -242,11 +247,10 @@ async function renderPageClientSide(renderArgs) {
|
|
|
242
247
|
// onHydrationEnd()
|
|
243
248
|
if (isFirstRender) {
|
|
244
249
|
assertHook(pageContext, 'onHydrationEnd');
|
|
245
|
-
const
|
|
246
|
-
if (
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
await executeHook(() => onHydrationEnd(pageContext), 'onHydrationEnd', hookFilePath);
|
|
250
|
+
const hook = getHook(pageContext, 'onHydrationEnd');
|
|
251
|
+
if (hook) {
|
|
252
|
+
const { hookFn } = hook;
|
|
253
|
+
await executeHook(() => hookFn(pageContext), hook);
|
|
250
254
|
if (abortRender(true))
|
|
251
255
|
return;
|
|
252
256
|
}
|
|
@@ -256,9 +260,11 @@ async function renderPageClientSide(renderArgs) {
|
|
|
256
260
|
return;
|
|
257
261
|
// onPageTransitionEnd()
|
|
258
262
|
if (callTransitionHooks) {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
263
|
+
assertHook(pageContext, 'onPageTransitionEnd');
|
|
264
|
+
const hook = getHook(pageContext, 'onPageTransitionEnd');
|
|
265
|
+
if (hook) {
|
|
266
|
+
const { hookFn } = hook;
|
|
267
|
+
await executeHook(() => hookFn(pageContext), hook);
|
|
262
268
|
if (abortRender(true))
|
|
263
269
|
return;
|
|
264
270
|
}
|
|
@@ -45,7 +45,7 @@ async function executeOnRenderClientHook(pageContext, isClientRouting) {
|
|
|
45
45
|
const renderHook = hook.hookFn;
|
|
46
46
|
assert(hookName);
|
|
47
47
|
// We don't use a try-catch wrapper because rendering errors are usually handled by the UI framework. (E.g. React's Error Boundaries.)
|
|
48
|
-
const hookResult = await executeHook(() => renderHook(pageContextForUserConsumption),
|
|
48
|
+
const hookResult = await executeHook(() => renderHook(pageContextForUserConsumption), hook);
|
|
49
49
|
assertUsage(hookResult === undefined, `The ${hookName}() hook defined by ${hook.hookFilePath} isn't allowed to return a value`);
|
|
50
50
|
}
|
|
51
51
|
function getUrlToShowToUser(pageContext) {
|
|
@@ -4,7 +4,7 @@ export { getPageContextSerializedInHtml };
|
|
|
4
4
|
function getPageContextSerializedInHtml() {
|
|
5
5
|
const id = 'vike_pageContext';
|
|
6
6
|
const elem = document.getElementById(id);
|
|
7
|
-
assertUsage(elem, `The element #${id} (which
|
|
7
|
+
assertUsage(elem, `The element #${id} (which Vike automatically injects into the HTML) is missing from the DOM. This may happen if your HTML is malformed. Make sure your HTML isn't malformed, and make sure you don't remove #${id} from the HTML nor from the DOM.`);
|
|
8
8
|
const pageContextJson = elem.textContent;
|
|
9
9
|
assert(pageContextJson);
|
|
10
10
|
const pageContextSerializedInHtml = parse(pageContextJson);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { getConfigValuesSerialized };
|
|
2
2
|
export { assertConfigValueIsSerializable };
|
|
3
3
|
import { assert, assertUsage, getPropAccessNotation } from '../../../utils.js';
|
|
4
|
-
import {
|
|
4
|
+
import { stringify } from '@brillout/json-serializer/stringify';
|
|
5
5
|
import pc from '@brillout/picocolors';
|
|
6
6
|
import { getConfigValueFilePathToShowToUser } from '../../../../../shared/page-configs/helpers.js';
|
|
7
7
|
import { serializeConfigValue } from '../../../../../shared/page-configs/serialize/serializeConfigValue.js';
|
|
@@ -47,22 +47,27 @@ function getConfigValueSerialized(value, configName, definedAt) {
|
|
|
47
47
|
configValueSerialized = stringify(value, { valueName, forbidReactElements: true });
|
|
48
48
|
}
|
|
49
49
|
catch (err) {
|
|
50
|
-
|
|
50
|
+
/*
|
|
51
|
+
let serializationErrMsg = ''
|
|
51
52
|
if (isJsonSerializerError(err)) {
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
serializationErrMsg = 'see serialization error printed above';
|
|
53
|
+
serializationErrMsg = err.messageCore
|
|
54
|
+
} else {
|
|
55
|
+
// When a property getter throws an error
|
|
56
|
+
console.error('Serialization error:')
|
|
57
|
+
console.error(err)
|
|
58
|
+
serializationErrMsg = 'see serialization error printed above'
|
|
59
59
|
}
|
|
60
|
+
*/
|
|
60
61
|
const configValueFilePathToShowToUser = getConfigValueFilePathToShowToUser({ definedAt });
|
|
61
62
|
assert(configValueFilePathToShowToUser);
|
|
62
63
|
assertUsage(false, [
|
|
63
|
-
`The
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
`The code of ${pc.cyan(configName)} cannot live inside ${configValueFilePathToShowToUser},`,
|
|
65
|
+
'see https://vike.dev/header-file#runtime-code'
|
|
66
|
+
/* I guess showing this is more confusing than adding value.
|
|
67
|
+
`(technically speaking: the value of ${pc.cyan(
|
|
68
|
+
configName
|
|
69
|
+
)} isn't serializable (${serializationErrMsg}) and it's therefore runtime code that needs to be imported).`
|
|
70
|
+
//*/
|
|
66
71
|
].join(' '));
|
|
67
72
|
}
|
|
68
73
|
configValueSerialized = JSON.stringify(configValueSerialized);
|
|
@@ -75,6 +75,9 @@ const configDefinitionsBuiltIn = {
|
|
|
75
75
|
onBeforeRenderEnv: {
|
|
76
76
|
env: { client: true },
|
|
77
77
|
_computed: (configValueSources) => !isConfigSet(configValueSources, 'onBeforeRender') ? null : getConfigEnv(configValueSources, 'onBeforeRender')
|
|
78
|
+
},
|
|
79
|
+
hooksTimeout: {
|
|
80
|
+
env: { server: true, client: true }
|
|
78
81
|
}
|
|
79
82
|
};
|
|
80
83
|
const configDefinitionsBuiltInGlobal = {
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
export { crawlPlusFiles };
|
|
2
|
+
import { assertPosixPath, assert, toPosixPath, assertWarning, scriptFileExtensionList, scriptFileExtensions } from '../../../../utils.js';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import glob from 'fast-glob';
|
|
5
|
+
import { exec } from 'child_process';
|
|
6
|
+
import { promisify } from 'util';
|
|
7
|
+
const execA = promisify(exec);
|
|
8
|
+
async function crawlPlusFiles(userRootDir, outDirAbsoluteFilesystem, isDev) {
|
|
9
|
+
assertPosixPath(userRootDir);
|
|
10
|
+
assertPosixPath(outDirAbsoluteFilesystem);
|
|
11
|
+
assert(outDirAbsoluteFilesystem.startsWith(userRootDir));
|
|
12
|
+
const outDir = path.posix.relative(userRootDir, outDirAbsoluteFilesystem);
|
|
13
|
+
assert(!outDir.startsWith('.'));
|
|
14
|
+
const timeBase = new Date().getTime();
|
|
15
|
+
let files = [];
|
|
16
|
+
const res = await gitLsFiles(userRootDir, outDir);
|
|
17
|
+
if (res &&
|
|
18
|
+
// Fallback to fast-glob for users that dynamically generate plus files (we assume generetad plus files to be skipped because they are usually included in .gitignore)
|
|
19
|
+
res.length > 0) {
|
|
20
|
+
files = res;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
files = await fastGlob(userRootDir, outDir);
|
|
24
|
+
}
|
|
25
|
+
{
|
|
26
|
+
const time = new Date().getTime() - timeBase;
|
|
27
|
+
if (isDev) {
|
|
28
|
+
// We only warn in dev, because while building it's expected to take a long time as fast-glob is competing for resources with other tasks
|
|
29
|
+
assertWarning(time < 2 * 1000, `Crawling your user files took an unexpected long time (${time}ms). Create a new issue on Vike's GitHub.`, {
|
|
30
|
+
onlyOnce: 'slow-page-files-search'
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const plusFiles = files.map((p) => {
|
|
35
|
+
p = toPosixPath(p);
|
|
36
|
+
assert(!p.startsWith(userRootDir));
|
|
37
|
+
const filePathRelativeToUserRootDir = path.posix.join('/', p);
|
|
38
|
+
const filePathAbsoluteFilesystem = path.posix.join(userRootDir, p);
|
|
39
|
+
return {
|
|
40
|
+
filePathRelativeToUserRootDir,
|
|
41
|
+
filePathAbsoluteFilesystem
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
return plusFiles;
|
|
45
|
+
}
|
|
46
|
+
// Same as fastGlob() but using `$ git ls-files`
|
|
47
|
+
async function gitLsFiles(userRootDir, outDir) {
|
|
48
|
+
// Test if Git is installed
|
|
49
|
+
{
|
|
50
|
+
let stdout;
|
|
51
|
+
try {
|
|
52
|
+
const res = await execA('git --version', { cwd: userRootDir });
|
|
53
|
+
stdout = res.stdout;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
assert(stdout.startsWith('git version '));
|
|
59
|
+
}
|
|
60
|
+
const cmd = [
|
|
61
|
+
'git ls-files',
|
|
62
|
+
...scriptFileExtensionList.map((ext) => `"**/+*.${ext}"`),
|
|
63
|
+
...getIgnorePatterns(outDir).map((pattern) => `--exclude="${pattern}"`),
|
|
64
|
+
// --others lists untracked files only (but using .gitignore because --exclude-standard)
|
|
65
|
+
// --cached adds the tracked files to the output
|
|
66
|
+
'--others --cached --exclude-standard'
|
|
67
|
+
].join(' ');
|
|
68
|
+
let stdout;
|
|
69
|
+
try {
|
|
70
|
+
const res = await execA(cmd, { cwd: userRootDir });
|
|
71
|
+
stdout = res.stdout;
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
if (err.message.includes('not a git repository'))
|
|
75
|
+
return null;
|
|
76
|
+
throw err;
|
|
77
|
+
}
|
|
78
|
+
let files = stdout.split('\n').filter(Boolean);
|
|
79
|
+
assert(!outDir.startsWith('/'));
|
|
80
|
+
files = files.filter(
|
|
81
|
+
// We have to repeat the same exclusion logic here because the `git ls-files` option --exclude only applies to untracked files. (We use --exclude only to speed up the command.)
|
|
82
|
+
(file) => getIgnoreFilter(file, outDir));
|
|
83
|
+
return files;
|
|
84
|
+
}
|
|
85
|
+
// Same as gitLsFiles() but using fast-glob
|
|
86
|
+
async function fastGlob(userRootDir, outDir) {
|
|
87
|
+
const files = await glob(`**/+*.${scriptFileExtensions}`, {
|
|
88
|
+
ignore: getIgnorePatterns(outDir),
|
|
89
|
+
cwd: userRootDir,
|
|
90
|
+
dot: false
|
|
91
|
+
});
|
|
92
|
+
return files;
|
|
93
|
+
}
|
|
94
|
+
// Same as getIgnoreFilter() but as glob pattern
|
|
95
|
+
function getIgnorePatterns(outDir) {
|
|
96
|
+
return [
|
|
97
|
+
'**/node_modules/**',
|
|
98
|
+
`${outDir}/**`,
|
|
99
|
+
// Allow:
|
|
100
|
+
// ```
|
|
101
|
+
// +Page.js
|
|
102
|
+
// +Page.telefunc.js
|
|
103
|
+
// ```
|
|
104
|
+
'**/*.telefunc.*'
|
|
105
|
+
];
|
|
106
|
+
}
|
|
107
|
+
// Same as getIgnorePatterns() but for Array.filter()
|
|
108
|
+
function getIgnoreFilter(file, outDir) {
|
|
109
|
+
return !file.includes('node_modules/') && !file.includes('.telefunc.') && !file.startsWith(`${outDir}/`);
|
|
110
|
+
}
|