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
|
@@ -2,10 +2,9 @@ export { getVikeConfig };
|
|
|
2
2
|
export { reloadVikeConfig };
|
|
3
3
|
export { vikeConfigDependencies };
|
|
4
4
|
export { isVikeConfigFile };
|
|
5
|
-
import { assertPosixPath, assert, isObject, assertUsage,
|
|
5
|
+
import { assertPosixPath, assert, isObject, assertUsage, assertWarning, objectEntries, hasProp, arrayIncludes, assertIsNotProductionRuntime, getMostSimilar, isNpmPackageImport, joinEnglish, lowerFirst, mergeCumulativeValues, requireResolve, getOutDirs, deepEqual, assertKeys } from '../../../utils.js';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import { configDefinitionsBuiltIn, configDefinitionsBuiltInGlobal } from './getVikeConfig/configDefinitionsBuiltIn.js';
|
|
8
|
-
import glob from 'fast-glob';
|
|
9
8
|
import { getLocationId, getFilesystemRouteString, getFilesystemRouteDefinedBy, isInherited, sortAfterInheritanceOrder, isGlobalLocation, applyFilesystemRoutingRootEffect } from './getVikeConfig/filesystemRouting.js';
|
|
10
9
|
import { isTmpFile, transpileAndExecuteFile } from './transpileAndExecuteFile.js';
|
|
11
10
|
import { parseImportData } from './replaceImportStatements.js';
|
|
@@ -18,6 +17,7 @@ import { getConfigDefinedAtString } from '../../../../../shared/page-configs/hel
|
|
|
18
17
|
import { assertExportsOfConfigFile, assertExportsOfValueFile } from '../../../../../shared/page-configs/assertExports.js';
|
|
19
18
|
import { getConfigVike } from '../../../../shared/getConfigVike.js';
|
|
20
19
|
import { assertConfigValueIsSerializable } from './getConfigValuesSerialized.js';
|
|
20
|
+
import { crawlPlusFiles } from './getVikeConfig/crawlPlusFiles.js';
|
|
21
21
|
assertIsNotProductionRuntime();
|
|
22
22
|
let devServerIsCorrupt = false;
|
|
23
23
|
let wasConfigInvalid = null;
|
|
@@ -70,7 +70,7 @@ async function getVikeConfig(config, isDev, tolerateInvalidConfig = false, exten
|
|
|
70
70
|
return await vikeConfigPromise;
|
|
71
71
|
}
|
|
72
72
|
async function loadInterfaceFiles(userRootDir, outDirRoot, isDev, extensions) {
|
|
73
|
-
const plusFiles = await findPlusFiles(userRootDir,
|
|
73
|
+
const plusFiles = await findPlusFiles(userRootDir, outDirRoot, isDev, extensions);
|
|
74
74
|
const configFiles = [];
|
|
75
75
|
const valueFiles = [];
|
|
76
76
|
plusFiles.forEach((f) => {
|
|
@@ -122,6 +122,7 @@ async function loadInterfaceFiles(userRootDir, outDirRoot, isDev, extensions) {
|
|
|
122
122
|
interfaceFilesByLocationId[locationId].push(interfaceFile);
|
|
123
123
|
}
|
|
124
124
|
}));
|
|
125
|
+
assertAllConfigsAreKnown(interfaceFilesByLocationId);
|
|
125
126
|
return interfaceFilesByLocationId;
|
|
126
127
|
}
|
|
127
128
|
function getConfigDefinition(configDefinitionsRelevant, configName, filePathToShowToUser) {
|
|
@@ -175,6 +176,18 @@ function getInterfaceFileFromConfigFile(configFile, isConfigExtend) {
|
|
|
175
176
|
});
|
|
176
177
|
return interfaceFile;
|
|
177
178
|
}
|
|
179
|
+
/** Show error message upon unknown config */
|
|
180
|
+
function assertAllConfigsAreKnown(interfaceFilesByLocationId) {
|
|
181
|
+
Object.entries(interfaceFilesByLocationId).forEach(([locationId, interfaceFiles]) => {
|
|
182
|
+
const interfaceFilesRelevant = getInterfaceFilesRelevant(interfaceFilesByLocationId, locationId);
|
|
183
|
+
const configDefinitionsRelevant = getConfigDefinitions(interfaceFilesRelevant);
|
|
184
|
+
interfaceFiles.forEach((interfaceFile) => {
|
|
185
|
+
Object.keys(interfaceFile.configMap).forEach((configName) => {
|
|
186
|
+
assertConfigExists(configName, Object.keys(configDefinitionsRelevant), interfaceFile.filePath.filePathToShowToUser);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
}
|
|
178
191
|
async function loadVikeConfig_withErrorHandling(userRootDir, outDirRoot, isDev, extensions, tolerateInvalidConfig) {
|
|
179
192
|
let hasError = false;
|
|
180
193
|
let ret;
|
|
@@ -265,18 +278,23 @@ async function loadVikeConfig(userRootDir, outDirRoot, isDev, extensions) {
|
|
|
265
278
|
};
|
|
266
279
|
return pageConfig;
|
|
267
280
|
}));
|
|
268
|
-
|
|
269
|
-
Object.entries(interfaceFilesByLocationId).forEach(([locationId, interfaceFiles]) => {
|
|
270
|
-
const interfaceFilesRelevant = getInterfaceFilesRelevant(interfaceFilesByLocationId, locationId);
|
|
271
|
-
const configDefinitionsRelevant = getConfigDefinitions(interfaceFilesRelevant);
|
|
272
|
-
interfaceFiles.forEach((interfaceFile) => {
|
|
273
|
-
Object.keys(interfaceFile.configMap).forEach((configName) => {
|
|
274
|
-
assertConfigExists(configName, Object.keys(configDefinitionsRelevant), interfaceFile.filePath.filePathToShowToUser);
|
|
275
|
-
});
|
|
276
|
-
});
|
|
277
|
-
});
|
|
281
|
+
assertPageConfigs(pageConfigs);
|
|
278
282
|
return { pageConfigs, pageConfigGlobal, globalVikeConfig };
|
|
279
283
|
}
|
|
284
|
+
function assertPageConfigs(pageConfigs) {
|
|
285
|
+
pageConfigs.forEach((pageConfig) => {
|
|
286
|
+
assertOnBeforeRenderEnv(pageConfig);
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
function assertOnBeforeRenderEnv(pageConfig) {
|
|
290
|
+
const onBeforeRenderConfig = pageConfig.configValueSources.onBeforeRender?.[0];
|
|
291
|
+
if (!onBeforeRenderConfig)
|
|
292
|
+
return;
|
|
293
|
+
const onBeforeRenderEnv = onBeforeRenderConfig.configEnv;
|
|
294
|
+
const isClientRouting = !!pageConfig.configValues.clientRouting?.value;
|
|
295
|
+
// When using Server Routing, loading a onBeforeRender() hook on the client-side hasn't any effect (the Server Routing's client runtime never calls it); it unnecessarily bloats client bundle sizes
|
|
296
|
+
assertUsage(!(onBeforeRenderEnv.client && !isClientRouting), `Page ${pageConfig.pageId} has an onBeforeRender() hook with env ${pc.cyan(JSON.stringify(onBeforeRenderEnv))} which doesn't make sense because the page is using Server Routing: onBeforeRender() can be run in the client only when using Client Routing.`);
|
|
297
|
+
}
|
|
280
298
|
function interfacefileIsAlreaydLoaded(interfaceFile) {
|
|
281
299
|
const configMapValues = Object.values(interfaceFile.configMap);
|
|
282
300
|
const isAlreadyLoaded = configMapValues.some((conf) => 'configValue' in conf);
|
|
@@ -776,39 +794,9 @@ function getComputed(configValueSources, configDefinitionsRelevant) {
|
|
|
776
794
|
});
|
|
777
795
|
return configValuesComputed;
|
|
778
796
|
}
|
|
779
|
-
async function findPlusFiles(userRootDir,
|
|
780
|
-
const
|
|
781
|
-
|
|
782
|
-
const ignorePatterns = [];
|
|
783
|
-
for (const dir of ignoreDirs) {
|
|
784
|
-
assertPosixPath(dir);
|
|
785
|
-
ignorePatterns.push(`${path.posix.relative(userRootDir, dir)}/**`);
|
|
786
|
-
}
|
|
787
|
-
const result = await glob(`**/+*.${scriptFileExtensions}`, {
|
|
788
|
-
ignore: [
|
|
789
|
-
'**/node_modules/**',
|
|
790
|
-
// Allow:
|
|
791
|
-
// ```
|
|
792
|
-
// +Page.js
|
|
793
|
-
// +Page.telefunc.js
|
|
794
|
-
// ```
|
|
795
|
-
'**/*.telefunc.*',
|
|
796
|
-
...ignorePatterns
|
|
797
|
-
],
|
|
798
|
-
cwd: userRootDir,
|
|
799
|
-
dot: false
|
|
800
|
-
});
|
|
801
|
-
const time = new Date().getTime() - timeBase;
|
|
802
|
-
if (isDev) {
|
|
803
|
-
// 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
|
|
804
|
-
assertWarning(time < 2 * 1000, `Crawling your user files took an unexpected long time (${time}ms). Create a new issue on Vike's GitHub.`, {
|
|
805
|
-
onlyOnce: 'slow-page-files-search'
|
|
806
|
-
});
|
|
807
|
-
}
|
|
808
|
-
const plusFiles = result.map((p) => {
|
|
809
|
-
p = toPosixPath(p);
|
|
810
|
-
const filePathRelativeToUserRootDir = path.posix.join('/', p);
|
|
811
|
-
const filePathAbsoluteFilesystem = path.posix.join(userRootDir, p);
|
|
797
|
+
async function findPlusFiles(userRootDir, outDirRoot, isDev, extensions) {
|
|
798
|
+
const files = await crawlPlusFiles(userRootDir, outDirRoot, isDev);
|
|
799
|
+
const plusFiles = files.map(({ filePathRelativeToUserRootDir, filePathAbsoluteFilesystem }) => {
|
|
812
800
|
return {
|
|
813
801
|
filePathRelativeToUserRootDir,
|
|
814
802
|
filePathAbsoluteVite: filePathRelativeToUserRootDir,
|
|
@@ -16,14 +16,14 @@ import { getConfigVike } from '../shared/getConfigVike.js';
|
|
|
16
16
|
import { getPageFilesServerSide } from '../../shared/getPageFiles.js';
|
|
17
17
|
import { getPageContextRequestUrl } from '../../shared/getPageContextRequestUrl.js';
|
|
18
18
|
import { getUrlFromRouteString } from '../../shared/route/resolveRouteString.js';
|
|
19
|
-
import { getConfigValue, getConfigValueFilePathToShowToUser
|
|
19
|
+
import { getConfigValue, getConfigValueFilePathToShowToUser } from '../../shared/page-configs/helpers.js';
|
|
20
20
|
import { loadConfigValues } from '../../shared/page-configs/loadConfigValues.js';
|
|
21
21
|
import { isErrorPage } from '../../shared/error-page.js';
|
|
22
22
|
import { addUrlComputedProps } from '../../shared/addUrlComputedProps.js';
|
|
23
23
|
import { assertPathIsFilesystemAbsolute } from '../../utils/assertPathIsFilesystemAbsolute.js';
|
|
24
24
|
import { isAbortError } from '../../shared/route/abort.js';
|
|
25
25
|
import { loadPageFilesServerSide } from '../runtime/renderPage/loadPageFilesServerSide.js';
|
|
26
|
-
import {
|
|
26
|
+
import { getHookFromPageConfig, getHookFromPageConfigGlobal, getHookTimeoutDefault } from '../../shared/hooks/getHook.js';
|
|
27
27
|
import { noRouteMatch } from '../../shared/route/noRouteMatch.js';
|
|
28
28
|
import { getVikeConfig } from '../plugin/plugins/importUserCode/v1-design/getVikeConfig.js';
|
|
29
29
|
async function prerenderFromAPI(options = {}) {
|
|
@@ -74,16 +74,22 @@ async function runPrerender(options, manuallyTriggered) {
|
|
|
74
74
|
await callOnBeforePrerenderStartHooks(prerenderContext, renderContext, concurrencyLimit, doNotPrerenderList);
|
|
75
75
|
await handlePagesWithStaticRoutes(prerenderContext, renderContext, doNotPrerenderList, concurrencyLimit);
|
|
76
76
|
await callOnPrerenderStartHook(prerenderContext, renderContext);
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
const prerenderedPageContexts = {};
|
|
78
|
+
let prerenderedCount = 0;
|
|
79
|
+
const onComplete = async (htmlFile) => {
|
|
80
|
+
prerenderedCount++;
|
|
81
|
+
if (htmlFile.pageId) {
|
|
82
|
+
prerenderedPageContexts[htmlFile.pageId] = htmlFile.pageContext;
|
|
83
|
+
}
|
|
84
|
+
await writeFiles(htmlFile, root, outDirClient, options.onPagePrerender, logLevel);
|
|
85
|
+
};
|
|
86
|
+
await routeAndPrerender(prerenderContext, concurrencyLimit, onComplete);
|
|
87
|
+
warnContradictoryNoPrerenderList(prerenderedPageContexts, doNotPrerenderList);
|
|
88
|
+
await prerender404(prerenderedPageContexts, renderContext, prerenderContext, onComplete);
|
|
82
89
|
if (logLevel === 'info') {
|
|
83
|
-
console.log(`${pc.green(`✓`)} ${
|
|
90
|
+
console.log(`${pc.green(`✓`)} ${prerenderedCount} HTML documents pre-rendered.`);
|
|
84
91
|
}
|
|
85
|
-
|
|
86
|
-
warnMissingPages(prerenderedPages, doNotPrerenderList, renderContext, partial);
|
|
92
|
+
warnMissingPages(prerenderedPageContexts, doNotPrerenderList, renderContext, partial);
|
|
87
93
|
}
|
|
88
94
|
async function collectDoNoPrerenderList(renderContext, pageConfigs, doNotPrerenderList, concurrencyLimit) {
|
|
89
95
|
// V1 design
|
|
@@ -151,18 +157,16 @@ async function callOnBeforePrerenderStartHooks(prerenderContext, renderContext,
|
|
|
151
157
|
await Promise.all(renderContext.pageConfigs.map((pageConfig) => concurrencyLimit(async () => {
|
|
152
158
|
const hookName = 'onBeforePrerenderStart';
|
|
153
159
|
const pageConfigLoaded = await loadConfigValues(pageConfig, false);
|
|
154
|
-
const
|
|
155
|
-
if (!
|
|
160
|
+
const hook = getHookFromPageConfig(pageConfigLoaded, hookName);
|
|
161
|
+
if (!hook)
|
|
156
162
|
return;
|
|
157
|
-
const hookFn =
|
|
158
|
-
const hookFilePath = getHookFilePathToShowToUser(configValue);
|
|
159
|
-
assert(hookFilePath);
|
|
160
|
-
assertHookFn(hookFn, { hookName, hookFilePath });
|
|
163
|
+
const { hookFn, hookFilePath, hookTimeout } = hook;
|
|
161
164
|
onBeforePrerenderStartHooks.push({
|
|
162
165
|
hookFn,
|
|
163
166
|
hookName: 'onBeforePrerenderStart',
|
|
164
167
|
hookFilePath,
|
|
165
|
-
pageId: pageConfig.pageId
|
|
168
|
+
pageId: pageConfig.pageId,
|
|
169
|
+
hookTimeout
|
|
166
170
|
});
|
|
167
171
|
})));
|
|
168
172
|
// 0.4 design
|
|
@@ -186,14 +190,15 @@ async function callOnBeforePrerenderStartHooks(prerenderContext, renderContext,
|
|
|
186
190
|
hookFn,
|
|
187
191
|
hookName: 'prerender',
|
|
188
192
|
hookFilePath,
|
|
189
|
-
pageId: p.pageId
|
|
193
|
+
pageId: p.pageId,
|
|
194
|
+
hookTimeout: getHookTimeoutDefault('onBeforePrerenderStart')
|
|
190
195
|
});
|
|
191
196
|
})));
|
|
192
|
-
await Promise.all(onBeforePrerenderStartHooks.map(({ hookFn, hookName, hookFilePath, pageId }) => concurrencyLimit(async () => {
|
|
197
|
+
await Promise.all(onBeforePrerenderStartHooks.map(({ hookFn, hookName, hookFilePath, pageId, hookTimeout }) => concurrencyLimit(async () => {
|
|
193
198
|
if (doNotPrerenderList.find((p) => p.pageId === pageId)) {
|
|
194
199
|
return;
|
|
195
200
|
}
|
|
196
|
-
const prerenderResult = await hookFn();
|
|
201
|
+
const prerenderResult = await executeHook(() => hookFn(), { hookName, hookFilePath, hookTimeout });
|
|
197
202
|
const result = normalizeOnPrerenderHookResult(prerenderResult, hookFilePath, hookName);
|
|
198
203
|
result.forEach(({ url, pageContext }) => {
|
|
199
204
|
{
|
|
@@ -292,23 +297,21 @@ async function callOnPrerenderStartHook(prerenderContext, renderContext) {
|
|
|
292
297
|
let onPrerenderStartHook;
|
|
293
298
|
// V1 design
|
|
294
299
|
if (renderContext.pageConfigs.length > 0) {
|
|
295
|
-
const
|
|
296
|
-
const
|
|
297
|
-
if (
|
|
298
|
-
|
|
299
|
-
// config.onPrerenderStart isn't a computed nor a cumulative config => definedAt should always be defined
|
|
300
|
-
const hookFilePath = getHookFilePathToShowToUser(configValue);
|
|
301
|
-
assert(hookFilePath);
|
|
300
|
+
const hookName = 'onPrerenderStart';
|
|
301
|
+
const hook = getHookFromPageConfigGlobal(renderContext.pageConfigGlobal, hookName);
|
|
302
|
+
if (hook) {
|
|
303
|
+
assert(hook.hookName === 'onPrerenderStart');
|
|
302
304
|
onPrerenderStartHook = {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
305
|
+
...hook,
|
|
306
|
+
// Make TypeScript happy
|
|
307
|
+
hookName
|
|
306
308
|
};
|
|
307
309
|
}
|
|
308
310
|
}
|
|
309
311
|
// Old design
|
|
310
312
|
// TODO/v1-release: remove
|
|
311
313
|
if (renderContext.pageConfigs.length === 0) {
|
|
314
|
+
const hookTimeout = getHookTimeoutDefault('onBeforePrerender');
|
|
312
315
|
const pageFilesWithOnBeforePrerenderHook = renderContext.pageFilesAll.filter((p) => {
|
|
313
316
|
assertExportNames(p);
|
|
314
317
|
if (!p.exportNames?.includes('onBeforePrerender'))
|
|
@@ -334,7 +337,8 @@ async function callOnPrerenderStartHook(prerenderContext, renderContext) {
|
|
|
334
337
|
onPrerenderStartHook = {
|
|
335
338
|
hookFn: hook.onBeforePrerender,
|
|
336
339
|
hookFilePath: hook.hookFilePath,
|
|
337
|
-
hookName: 'onBeforePrerender'
|
|
340
|
+
hookName: 'onBeforePrerender',
|
|
341
|
+
hookTimeout
|
|
338
342
|
};
|
|
339
343
|
}
|
|
340
344
|
if (!onPrerenderStartHook) {
|
|
@@ -369,7 +373,7 @@ async function callOnPrerenderStartHook(prerenderContext, renderContext) {
|
|
|
369
373
|
});
|
|
370
374
|
return prerenderContext.pageContexts;
|
|
371
375
|
}
|
|
372
|
-
}),
|
|
376
|
+
}), onPrerenderStartHook);
|
|
373
377
|
if (result === null || result === undefined) {
|
|
374
378
|
return;
|
|
375
379
|
}
|
|
@@ -411,7 +415,7 @@ async function callOnPrerenderStartHook(prerenderContext, renderContext) {
|
|
|
411
415
|
addUrlComputedProps(pageContext);
|
|
412
416
|
});
|
|
413
417
|
}
|
|
414
|
-
async function routeAndPrerender(prerenderContext,
|
|
418
|
+
async function routeAndPrerender(prerenderContext, concurrencyLimit, onComplete) {
|
|
415
419
|
const globalContext = getGlobalContext();
|
|
416
420
|
assert(globalContext.isPrerendering);
|
|
417
421
|
// Route all URLs
|
|
@@ -471,7 +475,7 @@ async function routeAndPrerender(prerenderContext, htmlFiles, prerenderedPages,
|
|
|
471
475
|
throw err;
|
|
472
476
|
}
|
|
473
477
|
const { documentHtml, pageContextSerialized } = res;
|
|
474
|
-
|
|
478
|
+
await onComplete({
|
|
475
479
|
urlOriginal,
|
|
476
480
|
pageContext,
|
|
477
481
|
htmlString: documentHtml,
|
|
@@ -479,11 +483,10 @@ async function routeAndPrerender(prerenderContext, htmlFiles, prerenderedPages,
|
|
|
479
483
|
doNotCreateExtraDirectory: prerenderContext._noExtraDir,
|
|
480
484
|
pageId
|
|
481
485
|
});
|
|
482
|
-
prerenderedPages[pageId] = pageContext;
|
|
483
486
|
})));
|
|
484
487
|
}
|
|
485
|
-
function warnContradictoryNoPrerenderList(
|
|
486
|
-
Object.entries(
|
|
488
|
+
function warnContradictoryNoPrerenderList(prerenderedPageContexts, doNotPrerenderList) {
|
|
489
|
+
Object.entries(prerenderedPageContexts).forEach(([pageId, pageContext]) => {
|
|
487
490
|
const doNotPrerenderListEntry = doNotPrerenderList.find((p) => p.pageId === pageId);
|
|
488
491
|
const { urlOriginal, _providedByHook: providedByHook } = pageContext;
|
|
489
492
|
{
|
|
@@ -495,7 +498,7 @@ function warnContradictoryNoPrerenderList(prerenderedPages, doNotPrerenderList)
|
|
|
495
498
|
assertWarning(false, `The ${providedByHook.hookName}() hook defined by ${providedByHook.hookFilePath} returns the URL ${pc.cyan(urlOriginal)}, while ${setByConfigFile} sets the config ${pc.cyan(setByConfigName)} to ${pc.cyan(String(setByConfigValue))}. This is contradictory: either don't set the config ${pc.cyan(setByConfigName)} to ${pc.cyan(String(setByConfigValue))} or remove the URL ${pc.cyan(urlOriginal)} from the list of URLs to be pre-rendered.`, { onlyOnce: true });
|
|
496
499
|
});
|
|
497
500
|
}
|
|
498
|
-
function warnMissingPages(
|
|
501
|
+
function warnMissingPages(prerenderedPageContexts, doNotPrerenderList, renderContext, partial) {
|
|
499
502
|
const isV1 = renderContext.pageConfigs.length > 0;
|
|
500
503
|
const hookName = isV1 ? 'onBeforePrerenderStart' : 'prerender';
|
|
501
504
|
/* TODO/after-v1-design-release: document setting `prerender: false` as an alternative to using prerender.partial (both in the warnings and the docs)
|
|
@@ -503,7 +506,7 @@ function warnMissingPages(prerenderedPages, doNotPrerenderList, renderContext, p
|
|
|
503
506
|
const msgAddendum = `Explicitly opt-out by setting the config ${optOutName} to ${isV1 ? 'false' : 'true'} or use the option prerender.partial`
|
|
504
507
|
*/
|
|
505
508
|
renderContext.allPageIds
|
|
506
|
-
.filter((pageId) => !
|
|
509
|
+
.filter((pageId) => !prerenderedPageContexts[pageId])
|
|
507
510
|
.filter((pageId) => !doNotPrerenderList.find((p) => p.pageId === pageId))
|
|
508
511
|
.filter((pageId) => !isErrorPage(pageId, renderContext.pageConfigs))
|
|
509
512
|
.forEach((pageId) => {
|
|
@@ -511,8 +514,8 @@ function warnMissingPages(prerenderedPages, doNotPrerenderList, renderContext, p
|
|
|
511
514
|
assertWarning(partial, `Cannot pre-render page ${pageAt} because it has a non-static route, while no ${hookName}() hook returned any URL matching the page's route. You need to use a ${hookName}() hook (https://vike.dev/${hookName}) providing a list of URLs for ${pageAt} that should be pre-rendered. If you don't want to pre-render ${pageAt} then use the option prerender.partial (https://vike.dev/prerender-config#partial) to suppress this warning.`, { onlyOnce: true });
|
|
512
515
|
});
|
|
513
516
|
}
|
|
514
|
-
async function prerender404(
|
|
515
|
-
if (!
|
|
517
|
+
async function prerender404(prerenderedPageContexts, renderContext, prerenderContext, onComplete) {
|
|
518
|
+
if (!Object.values(prerenderedPageContexts).find(({ urlOriginal }) => urlOriginal === '/404')) {
|
|
516
519
|
let result;
|
|
517
520
|
try {
|
|
518
521
|
result = await prerender404Page(renderContext, prerenderContext.pageContextInit);
|
|
@@ -524,7 +527,7 @@ async function prerender404(htmlFiles, renderContext, prerenderContext) {
|
|
|
524
527
|
if (result) {
|
|
525
528
|
const urlOriginal = '/404';
|
|
526
529
|
const { documentHtml, pageContext } = result;
|
|
527
|
-
|
|
530
|
+
await onComplete({
|
|
528
531
|
urlOriginal,
|
|
529
532
|
pageContext,
|
|
530
533
|
htmlString: documentHtml,
|
|
@@ -535,59 +538,57 @@ async function prerender404(htmlFiles, renderContext, prerenderContext) {
|
|
|
535
538
|
}
|
|
536
539
|
}
|
|
537
540
|
}
|
|
538
|
-
async function
|
|
541
|
+
async function writeFiles({ urlOriginal, pageContext, htmlString, pageContextSerialized, doNotCreateExtraDirectory }, root, outDirClient, onPagePrerender, logLevel) {
|
|
539
542
|
assert(urlOriginal.startsWith('/'));
|
|
540
543
|
const writeJobs = [
|
|
541
|
-
write(urlOriginal, pageContext, '.html', htmlString, root, outDirClient, doNotCreateExtraDirectory,
|
|
544
|
+
write(urlOriginal, pageContext, '.html', htmlString, root, outDirClient, doNotCreateExtraDirectory, onPagePrerender, logLevel)
|
|
542
545
|
];
|
|
543
546
|
if (pageContextSerialized !== null) {
|
|
544
|
-
writeJobs.push(write(urlOriginal, pageContext, '.pageContext.json', pageContextSerialized, root, outDirClient, doNotCreateExtraDirectory,
|
|
547
|
+
writeJobs.push(write(urlOriginal, pageContext, '.pageContext.json', pageContextSerialized, root, outDirClient, doNotCreateExtraDirectory, onPagePrerender, logLevel));
|
|
545
548
|
}
|
|
546
549
|
await Promise.all(writeJobs);
|
|
547
550
|
}
|
|
548
|
-
function write(urlOriginal, pageContext, fileExtension, fileContent, root, outDirClient, doNotCreateExtraDirectory,
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
outDirClientRelative = outDirClientRelative + '/';
|
|
586
|
-
}
|
|
587
|
-
console.log(`${pc.dim(outDirClientRelative)}${pc.blue(filePathRelative)}`);
|
|
551
|
+
async function write(urlOriginal, pageContext, fileExtension, fileContent, root, outDirClient, doNotCreateExtraDirectory, onPagePrerender, logLevel) {
|
|
552
|
+
let fileUrl;
|
|
553
|
+
if (fileExtension === '.html') {
|
|
554
|
+
fileUrl = urlToFile(urlOriginal, '.html', doNotCreateExtraDirectory);
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
fileUrl = getPageContextRequestUrl(urlOriginal);
|
|
558
|
+
}
|
|
559
|
+
assertPosixPath(fileUrl);
|
|
560
|
+
assert(fileUrl.startsWith('/'));
|
|
561
|
+
const filePathRelative = fileUrl.slice(1);
|
|
562
|
+
assert(!filePathRelative.startsWith('/'));
|
|
563
|
+
assertPosixPath(outDirClient);
|
|
564
|
+
assertPosixPath(filePathRelative);
|
|
565
|
+
const filePath = path.posix.join(outDirClient, filePathRelative);
|
|
566
|
+
if (onPagePrerender) {
|
|
567
|
+
const prerenderPageContext = {};
|
|
568
|
+
objectAssign(prerenderPageContext, pageContext);
|
|
569
|
+
objectAssign(prerenderPageContext, {
|
|
570
|
+
_prerenderResult: {
|
|
571
|
+
filePath,
|
|
572
|
+
fileContent
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
await onPagePrerender(prerenderPageContext);
|
|
576
|
+
}
|
|
577
|
+
else {
|
|
578
|
+
const { promises } = await import('fs');
|
|
579
|
+
const { writeFile, mkdir } = promises;
|
|
580
|
+
await mkdir(path.posix.dirname(filePath), { recursive: true });
|
|
581
|
+
await writeFile(filePath, fileContent);
|
|
582
|
+
if (logLevel === 'info') {
|
|
583
|
+
assertPosixPath(root);
|
|
584
|
+
assertPosixPath(outDirClient);
|
|
585
|
+
let outDirClientRelative = path.posix.relative(root, outDirClient);
|
|
586
|
+
if (!outDirClientRelative.endsWith('/')) {
|
|
587
|
+
outDirClientRelative = outDirClientRelative + '/';
|
|
588
588
|
}
|
|
589
|
+
console.log(`${pc.dim(outDirClientRelative)}${pc.blue(filePathRelative)}`);
|
|
589
590
|
}
|
|
590
|
-
}
|
|
591
|
+
}
|
|
591
592
|
}
|
|
592
593
|
function normalizeOnPrerenderHookResult(prerenderResult, prerenderHookFile, hookName) {
|
|
593
594
|
if (Array.isArray(prerenderResult)) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { injectAssets__public };
|
|
2
2
|
import { assertUsage, assertWarning, castProp, hasProp } from '../../utils.js';
|
|
3
3
|
import { injectHtmlTagsToString } from '../injectAssets.js';
|
|
4
|
-
// TODO: remove
|
|
4
|
+
// TODO/v1-release: remove
|
|
5
5
|
async function injectAssets__public(htmlString, pageContext) {
|
|
6
6
|
assertWarning(false, '`_injectAssets()` is deprecated and will be removed.', { onlyOnce: true, showStackTrace: true });
|
|
7
7
|
assertUsage(typeof htmlString === 'string', '[injectAssets(htmlString, pageContext)]: Argument `htmlString` should be a string.', { showStackTrace: true });
|
|
@@ -174,7 +174,7 @@ async function renderTemplate(templateContent, pageContext) {
|
|
|
174
174
|
};
|
|
175
175
|
assertUsage(!isPromise(templateVar), getErrMsg('a promise', `Did you forget to ${pc.cyan('await')} the promise?`));
|
|
176
176
|
if (templateVar === undefined || templateVar === null) {
|
|
177
|
-
assertWarning(false, getErrMsg(`${pc.cyan(String(templateVar))} which will be converted to an empty string`, `Pass
|
|
177
|
+
assertWarning(false, getErrMsg(`${pc.cyan(String(templateVar))} which will be converted to an empty string`, `Pass the empty string ${pc.cyan("''")} instead of ${pc.cyan(String(templateVar))} to remove this warning.`), { onlyOnce: false });
|
|
178
178
|
templateVar = '';
|
|
179
179
|
}
|
|
180
180
|
{
|
package/dist/esm/node/runtime/renderPage/createHttpResponseObject/assertNoInfiniteHttpRedirect.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { assertNoInfiniteHttpRedirect };
|
|
2
|
-
declare function assertNoInfiniteHttpRedirect(
|
|
2
|
+
declare function assertNoInfiniteHttpRedirect(urlRedirectTarget: string, urlLogical: string): void;
|
package/dist/esm/node/runtime/renderPage/createHttpResponseObject/assertNoInfiniteHttpRedirect.js
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
export { assertNoInfiniteHttpRedirect };
|
|
2
|
-
import { assert, assertUsage, getGlobalObject } from '../../utils.js';
|
|
2
|
+
import { assert, assertUsage, getGlobalObject, isUriWithProtocol } from '../../utils.js';
|
|
3
3
|
import pc from '@brillout/picocolors';
|
|
4
4
|
const globalObject = getGlobalObject('assertNoInfiniteHttpRedirect.ts', {
|
|
5
5
|
redirectGraph: {}
|
|
6
6
|
});
|
|
7
|
-
function assertNoInfiniteHttpRedirect(
|
|
8
|
-
if (
|
|
9
|
-
// We assume that
|
|
10
|
-
// - There isn't a reliable way to check whether the redirect points to an external origin or the same origin
|
|
7
|
+
function assertNoInfiniteHttpRedirect(urlRedirectTarget, urlLogical) {
|
|
8
|
+
if (isUriWithProtocol(urlRedirectTarget)) {
|
|
9
|
+
// We assume that urlRedirectTarget points to an origin that is external (not the same origin), and we can therefore assume that the app doesn't define an infinite loop (in itself).
|
|
10
|
+
// - There isn't a reliable way to check whether the redirect points to an external origin or the same origin. For same origins, we assume/hope the user to pass the URL without origin.
|
|
11
11
|
// ```js
|
|
12
|
-
// //
|
|
12
|
+
// // For same-origin, the user usually/hopefully passes a URL without origin
|
|
13
13
|
// renderPage({ urlOriginal: '/some/pathname' })
|
|
14
14
|
// ```
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
|
-
assert(
|
|
18
|
-
assert(
|
|
17
|
+
assert(urlRedirectTarget.startsWith('/'));
|
|
18
|
+
assert(urlLogical.startsWith('/'));
|
|
19
19
|
const graph = copy(globalObject.redirectGraph);
|
|
20
|
-
graph[
|
|
21
|
-
graph[
|
|
20
|
+
graph[urlRedirectTarget] ?? (graph[urlRedirectTarget] = new Set());
|
|
21
|
+
graph[urlRedirectTarget].add(urlLogical);
|
|
22
22
|
validate(graph);
|
|
23
23
|
globalObject.redirectGraph = graph;
|
|
24
24
|
}
|
|
@@ -26,6 +26,9 @@ function copy(G) {
|
|
|
26
26
|
return Object.fromEntries(Object.entries(G).map(([key, val]) => [key, new Set(val)]));
|
|
27
27
|
}
|
|
28
28
|
// Adapted from: https://stackoverflow.com/questions/60904464/detect-cycle-in-directed-graph/60907076#60907076
|
|
29
|
+
function validate(G) {
|
|
30
|
+
Object.keys(G).forEach((n) => check(G, n, []));
|
|
31
|
+
}
|
|
29
32
|
function check(G, n, path) {
|
|
30
33
|
if (path.includes(n)) {
|
|
31
34
|
const cycle = path.slice(path.indexOf(n)).concat(n);
|
|
@@ -33,6 +36,3 @@ function check(G, n, path) {
|
|
|
33
36
|
}
|
|
34
37
|
G[n]?.forEach((node) => check(G, node, [...path, n]));
|
|
35
38
|
}
|
|
36
|
-
function validate(G) {
|
|
37
|
-
Object.keys(G).forEach((n) => check(G, n, []));
|
|
38
|
-
}
|
|
@@ -25,4 +25,4 @@ declare function createHttpResponseObject(htmlRender: null | HtmlRender, renderH
|
|
|
25
25
|
abortStatusCode?: AbortStatusCode;
|
|
26
26
|
}): Promise<HttpResponse | null>;
|
|
27
27
|
declare function createHttpResponsePageContextJson(pageContextSerialized: string): Promise<HttpResponse>;
|
|
28
|
-
declare function createHttpResponseObjectRedirect({ url, statusCode }: UrlRedirect,
|
|
28
|
+
declare function createHttpResponseObjectRedirect({ url, statusCode }: UrlRedirect, urlLogical: string): HttpResponse;
|
|
@@ -33,9 +33,9 @@ async function createHttpResponsePageContextJson(pageContextSerialized) {
|
|
|
33
33
|
return httpResponse;
|
|
34
34
|
}
|
|
35
35
|
function createHttpResponseObjectRedirect({ url, statusCode },
|
|
36
|
-
// The URL
|
|
37
|
-
|
|
38
|
-
assertNoInfiniteHttpRedirect(url,
|
|
36
|
+
// The URL we assume the redirect to be logically based on
|
|
37
|
+
urlLogical) {
|
|
38
|
+
assertNoInfiniteHttpRedirect(url, urlLogical);
|
|
39
39
|
assert(url);
|
|
40
40
|
assert(statusCode);
|
|
41
41
|
assert(300 <= statusCode && statusCode <= 399);
|
|
@@ -13,7 +13,7 @@ async function executeOnBeforeRenderHooks(pageContext) {
|
|
|
13
13
|
}
|
|
14
14
|
const onBeforeRender = hook.hookFn;
|
|
15
15
|
preparePageContextForUserConsumptionServerSide(pageContext);
|
|
16
|
-
const hookResult = await executeHook(() => onBeforeRender(pageContext),
|
|
16
|
+
const hookResult = await executeHook(() => onBeforeRender(pageContext), hook);
|
|
17
17
|
assertOnBeforeRenderHookReturn(hookResult, hook.hookFilePath);
|
|
18
18
|
const pageContextFromHook = hookResult?.pageContext;
|
|
19
19
|
Object.assign(pageContext, pageContextFromHook);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
export { executeOnRenderHtmlHook };
|
|
2
2
|
export type { RenderHook };
|
|
3
3
|
import { type HtmlRender } from '../html/renderHtml.js';
|
|
4
|
+
import { type Hook } from '../../../shared/hooks/getHook.js';
|
|
4
5
|
import type { PageAsset } from './getPageAssets.js';
|
|
5
6
|
import { type PageContextForUserConsumptionServerSide } from './preparePageContextForUserConsumptionServerSide.js';
|
|
6
7
|
import type { PageConfigRuntime } from '../../../shared/page-configs/PageConfig.js';
|
|
7
8
|
import type { PageContextSerialization } from '../html/serializePageContextClientSide.js';
|
|
8
9
|
type GetPageAssets = () => Promise<PageAsset[]>;
|
|
9
|
-
type RenderHook = {
|
|
10
|
-
hookFilePath: string;
|
|
10
|
+
type RenderHook = Hook & {
|
|
11
11
|
hookName: HookName;
|
|
12
12
|
};
|
|
13
13
|
type HookName = 'onRenderHtml' | 'render';
|
|
@@ -12,7 +12,7 @@ async function executeOnRenderHtmlHook(pageContext) {
|
|
|
12
12
|
const { renderHook, hookFn } = getRenderHook(pageContext);
|
|
13
13
|
objectAssign(pageContext, { _renderHook: renderHook });
|
|
14
14
|
preparePageContextForUserConsumptionServerSide(pageContext);
|
|
15
|
-
const hookReturnValue = await executeHook(() => hookFn(pageContext), renderHook
|
|
15
|
+
const hookReturnValue = await executeHook(() => hookFn(pageContext), renderHook);
|
|
16
16
|
const { documentHtml, pageContextProvidedByRenderHook, pageContextPromise, injectFilter } = processHookReturnValue(hookReturnValue, renderHook);
|
|
17
17
|
Object.assign(pageContext, pageContextProvidedByRenderHook);
|
|
18
18
|
objectAssign(pageContext, { _pageContextPromise: pageContextPromise });
|
|
@@ -50,10 +50,10 @@ function getRenderHook(pageContext) {
|
|
|
50
50
|
}
|
|
51
51
|
if (hook) {
|
|
52
52
|
assert(hookName);
|
|
53
|
-
const { hookFilePath, hookFn } = hook;
|
|
53
|
+
const { hookFilePath, hookFn, hookTimeout } = hook;
|
|
54
54
|
hookFound = {
|
|
55
55
|
hookFn,
|
|
56
|
-
renderHook: { hookFilePath, hookName }
|
|
56
|
+
renderHook: { hookFn, hookFilePath, hookName, hookTimeout }
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -7,7 +7,7 @@ import pc from '@brillout/picocolors';
|
|
|
7
7
|
const streamDocs = 'See https://vike.dev/stream for more information.';
|
|
8
8
|
function getHttpResponseBody(htmlRender, renderHook) {
|
|
9
9
|
if (typeof htmlRender !== 'string') {
|
|
10
|
-
assertUsage(false, getErrMsg(htmlRender, renderHook, 'body', `Use ${pc.cyan('pageContext.httpResponse.pipe()')}
|
|
10
|
+
assertUsage(false, getErrMsg(htmlRender, renderHook, 'body', `Use ${pc.cyan('pageContext.httpResponse.pipe()')} instead`));
|
|
11
11
|
}
|
|
12
12
|
const body = htmlRender;
|
|
13
13
|
return body;
|