vike 0.4.147-commit-f9a91f3 → 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.
Files changed (52) hide show
  1. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getConfigValuesSerialized.js +17 -12
  2. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.js +3 -0
  3. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/crawlPlusFiles.js +12 -0
  4. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.js +28 -10
  5. package/dist/cjs/node/prerender/runPrerender.js +20 -22
  6. package/dist/cjs/node/runtime/html/renderHtml.js +1 -1
  7. package/dist/cjs/node/runtime/renderPage/executeOnBeforeRenderHook.js +1 -1
  8. package/dist/cjs/node/runtime/renderPage/executeOnRenderHtmlHook.js +3 -3
  9. package/dist/cjs/node/runtime/renderPage/renderPageAlreadyRouted.js +21 -18
  10. package/dist/cjs/shared/getPageFiles/parseGlobResults.js +3 -3
  11. package/dist/cjs/shared/hooks/executeHook.js +18 -29
  12. package/dist/cjs/shared/hooks/getHook.js +104 -3
  13. package/dist/cjs/shared/page-configs/helpers/getConfigDefinedAtString.js +1 -1
  14. package/dist/cjs/shared/route/executeGuardHook.js +3 -2
  15. package/dist/cjs/shared/route/executeOnBeforeRouteHook.js +4 -4
  16. package/dist/cjs/shared/route/loadPageRoutes.js +10 -15
  17. package/dist/cjs/utils/projectInfo.js +1 -1
  18. package/dist/esm/client/client-routing-runtime/createPageContext.d.ts +1 -1
  19. package/dist/esm/client/client-routing-runtime/getPageContextFromHooks.js +1 -1
  20. package/dist/esm/client/client-routing-runtime/renderPageClientSide.js +18 -12
  21. package/dist/esm/client/shared/executeOnRenderClientHook.js +1 -1
  22. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getConfigValuesSerialized.js +17 -12
  23. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.js +3 -0
  24. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/crawlPlusFiles.js +12 -0
  25. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.js +28 -10
  26. package/dist/esm/node/prerender/runPrerender.js +22 -24
  27. package/dist/esm/node/runtime/html/renderHtml.js +1 -1
  28. package/dist/esm/node/runtime/renderPage/executeOnBeforeRenderHook.js +1 -1
  29. package/dist/esm/node/runtime/renderPage/executeOnRenderHtmlHook.d.ts +2 -2
  30. package/dist/esm/node/runtime/renderPage/executeOnRenderHtmlHook.js +3 -3
  31. package/dist/esm/node/runtime/renderPage/renderPageAlreadyRouted.d.ts +7 -7
  32. package/dist/esm/node/runtime/renderPage/renderPageAlreadyRouted.js +22 -19
  33. package/dist/esm/shared/getPageFiles/parseGlobResults.js +1 -1
  34. package/dist/esm/shared/hooks/executeHook.d.ts +2 -2
  35. package/dist/esm/shared/hooks/executeHook.js +18 -29
  36. package/dist/esm/shared/hooks/getHook.d.ts +17 -7
  37. package/dist/esm/shared/hooks/getHook.js +103 -3
  38. package/dist/esm/shared/page-configs/Config.d.ts +21 -13
  39. package/dist/esm/shared/page-configs/helpers/getConfigDefinedAtString.d.ts +1 -1
  40. package/dist/esm/shared/page-configs/helpers/getConfigDefinedAtString.js +1 -1
  41. package/dist/esm/shared/route/executeGuardHook.js +4 -3
  42. package/dist/esm/shared/route/executeOnBeforeRouteHook.d.ts +1 -8
  43. package/dist/esm/shared/route/executeOnBeforeRouteHook.js +6 -6
  44. package/dist/esm/shared/route/index.d.ts +2 -2
  45. package/dist/esm/shared/route/loadPageRoutes.d.ts +2 -2
  46. package/dist/esm/shared/route/loadPageRoutes.js +11 -16
  47. package/dist/esm/utils/projectInfo.d.ts +2 -2
  48. package/dist/esm/utils/projectInfo.js +1 -1
  49. package/package.json +1 -1
  50. /package/dist/cjs/shared/page-configs/serialize/{assertPageConfigs.js → assertPageConfigsSerialized.js} +0 -0
  51. /package/dist/esm/shared/page-configs/serialize/{assertPageConfigs.d.ts → assertPageConfigsSerialized.d.ts} +0 -0
  52. /package/dist/esm/shared/page-configs/serialize/{assertPageConfigs.js → assertPageConfigsSerialized.js} +0 -0
@@ -53,22 +53,27 @@ function getConfigValueSerialized(value, configName, definedAt) {
53
53
  configValueSerialized = (0, stringify_1.stringify)(value, { valueName, forbidReactElements: true });
54
54
  }
55
55
  catch (err) {
56
- let serializationErrMsg = '';
57
- if ((0, stringify_1.isJsonSerializerError)(err)) {
58
- serializationErrMsg = err.messageCore;
59
- }
60
- else {
61
- // When a property getter throws an error
62
- console.error('Serialization error:');
63
- console.error(err);
64
- serializationErrMsg = 'see serialization error printed above';
56
+ /*
57
+ let serializationErrMsg = ''
58
+ if (isJsonSerializerError(err)) {
59
+ serializationErrMsg = err.messageCore
60
+ } else {
61
+ // When a property getter throws an error
62
+ console.error('Serialization error:')
63
+ console.error(err)
64
+ serializationErrMsg = 'see serialization error printed above'
65
65
  }
66
+ */
66
67
  const configValueFilePathToShowToUser = (0, helpers_js_1.getConfigValueFilePathToShowToUser)({ definedAt });
67
68
  (0, utils_js_1.assert)(configValueFilePathToShowToUser);
68
69
  (0, utils_js_1.assertUsage)(false, [
69
- `The value of the config ${picocolors_1.default.cyan(configName)} cannot be defined inside the file ${configValueFilePathToShowToUser}:`,
70
- `its value must be defined in an another file and then imported by ${configValueFilePathToShowToUser}. (Because its value isn't serializable: ${serializationErrMsg}.)`,
71
- `Only serializable config values can be defined inside +config.h.js files, see https://vike.dev/header-file.`
70
+ `The code of ${picocolors_1.default.cyan(configName)} cannot live inside ${configValueFilePathToShowToUser},`,
71
+ 'see https://vike.dev/header-file#runtime-code'
72
+ /* I guess showing this is more confusing than adding value.
73
+ `(technically speaking: the value of ${pc.cyan(
74
+ configName
75
+ )} isn't serializable (${serializationErrMsg}) and it's therefore runtime code that needs to be imported).`
76
+ //*/
72
77
  ].join(' '));
73
78
  }
74
79
  configValueSerialized = JSON.stringify(configValueSerialized);
@@ -76,6 +76,9 @@ const configDefinitionsBuiltIn = {
76
76
  onBeforeRenderEnv: {
77
77
  env: { client: true },
78
78
  _computed: (configValueSources) => !isConfigSet(configValueSources, 'onBeforeRender') ? null : getConfigEnv(configValueSources, 'onBeforeRender')
79
+ },
80
+ hooksTimeout: {
81
+ env: { server: true, client: true }
79
82
  }
80
83
  };
81
84
  exports.configDefinitionsBuiltIn = configDefinitionsBuiltIn;
@@ -51,6 +51,18 @@ async function crawlPlusFiles(userRootDir, outDirAbsoluteFilesystem, isDev) {
51
51
  exports.crawlPlusFiles = crawlPlusFiles;
52
52
  // Same as fastGlob() but using `$ git ls-files`
53
53
  async function gitLsFiles(userRootDir, outDir) {
54
+ // Test if Git is installed
55
+ {
56
+ let stdout;
57
+ try {
58
+ const res = await execA('git --version', { cwd: userRootDir });
59
+ stdout = res.stdout;
60
+ }
61
+ catch {
62
+ return null;
63
+ }
64
+ (0, utils_js_1.assert)(stdout.startsWith('git version '));
65
+ }
54
66
  const cmd = [
55
67
  'git ls-files',
56
68
  ...utils_js_1.scriptFileExtensionList.map((ext) => `"**/+*.${ext}"`),
@@ -127,6 +127,7 @@ async function loadInterfaceFiles(userRootDir, outDirRoot, isDev, extensions) {
127
127
  interfaceFilesByLocationId[locationId].push(interfaceFile);
128
128
  }
129
129
  }));
130
+ assertAllConfigsAreKnown(interfaceFilesByLocationId);
130
131
  return interfaceFilesByLocationId;
131
132
  }
132
133
  function getConfigDefinition(configDefinitionsRelevant, configName, filePathToShowToUser) {
@@ -180,6 +181,18 @@ function getInterfaceFileFromConfigFile(configFile, isConfigExtend) {
180
181
  });
181
182
  return interfaceFile;
182
183
  }
184
+ /** Show error message upon unknown config */
185
+ function assertAllConfigsAreKnown(interfaceFilesByLocationId) {
186
+ Object.entries(interfaceFilesByLocationId).forEach(([locationId, interfaceFiles]) => {
187
+ const interfaceFilesRelevant = getInterfaceFilesRelevant(interfaceFilesByLocationId, locationId);
188
+ const configDefinitionsRelevant = getConfigDefinitions(interfaceFilesRelevant);
189
+ interfaceFiles.forEach((interfaceFile) => {
190
+ Object.keys(interfaceFile.configMap).forEach((configName) => {
191
+ assertConfigExists(configName, Object.keys(configDefinitionsRelevant), interfaceFile.filePath.filePathToShowToUser);
192
+ });
193
+ });
194
+ });
195
+ }
183
196
  async function loadVikeConfig_withErrorHandling(userRootDir, outDirRoot, isDev, extensions, tolerateInvalidConfig) {
184
197
  let hasError = false;
185
198
  let ret;
@@ -270,18 +283,23 @@ async function loadVikeConfig(userRootDir, outDirRoot, isDev, extensions) {
270
283
  };
271
284
  return pageConfig;
272
285
  }));
273
- // Show error message upon unknown config
274
- Object.entries(interfaceFilesByLocationId).forEach(([locationId, interfaceFiles]) => {
275
- const interfaceFilesRelevant = getInterfaceFilesRelevant(interfaceFilesByLocationId, locationId);
276
- const configDefinitionsRelevant = getConfigDefinitions(interfaceFilesRelevant);
277
- interfaceFiles.forEach((interfaceFile) => {
278
- Object.keys(interfaceFile.configMap).forEach((configName) => {
279
- assertConfigExists(configName, Object.keys(configDefinitionsRelevant), interfaceFile.filePath.filePathToShowToUser);
280
- });
281
- });
282
- });
286
+ assertPageConfigs(pageConfigs);
283
287
  return { pageConfigs, pageConfigGlobal, globalVikeConfig };
284
288
  }
289
+ function assertPageConfigs(pageConfigs) {
290
+ pageConfigs.forEach((pageConfig) => {
291
+ assertOnBeforeRenderEnv(pageConfig);
292
+ });
293
+ }
294
+ function assertOnBeforeRenderEnv(pageConfig) {
295
+ const onBeforeRenderConfig = pageConfig.configValueSources.onBeforeRender?.[0];
296
+ if (!onBeforeRenderConfig)
297
+ return;
298
+ const onBeforeRenderEnv = onBeforeRenderConfig.configEnv;
299
+ const isClientRouting = !!pageConfig.configValues.clientRouting?.value;
300
+ // 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
301
+ (0, utils_js_1.assertUsage)(!(onBeforeRenderEnv.client && !isClientRouting), `Page ${pageConfig.pageId} has an onBeforeRender() hook with env ${picocolors_1.default.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.`);
302
+ }
285
303
  function interfacefileIsAlreaydLoaded(interfaceFile) {
286
304
  const configMapValues = Object.values(interfaceFile.configMap);
287
305
  const isAlreadyLoaded = configMapValues.some((conf) => 'configValue' in conf);
@@ -185,18 +185,16 @@ async function callOnBeforePrerenderStartHooks(prerenderContext, renderContext,
185
185
  await Promise.all(renderContext.pageConfigs.map((pageConfig) => concurrencyLimit(async () => {
186
186
  const hookName = 'onBeforePrerenderStart';
187
187
  const pageConfigLoaded = await (0, loadConfigValues_js_1.loadConfigValues)(pageConfig, false);
188
- const configValue = (0, helpers_js_1.getConfigValue)(pageConfigLoaded, hookName);
189
- if (!configValue)
188
+ const hook = (0, getHook_js_1.getHookFromPageConfig)(pageConfigLoaded, hookName);
189
+ if (!hook)
190
190
  return;
191
- const hookFn = configValue.value;
192
- const hookFilePath = (0, helpers_js_1.getHookFilePathToShowToUser)(configValue);
193
- (0, utils_js_1.assert)(hookFilePath);
194
- (0, getHook_js_1.assertHookFn)(hookFn, { hookName, hookFilePath });
191
+ const { hookFn, hookFilePath, hookTimeout } = hook;
195
192
  onBeforePrerenderStartHooks.push({
196
193
  hookFn,
197
194
  hookName: 'onBeforePrerenderStart',
198
195
  hookFilePath,
199
- pageId: pageConfig.pageId
196
+ pageId: pageConfig.pageId,
197
+ hookTimeout
200
198
  });
201
199
  })));
202
200
  // 0.4 design
@@ -220,14 +218,15 @@ async function callOnBeforePrerenderStartHooks(prerenderContext, renderContext,
220
218
  hookFn,
221
219
  hookName: 'prerender',
222
220
  hookFilePath,
223
- pageId: p.pageId
221
+ pageId: p.pageId,
222
+ hookTimeout: (0, getHook_js_1.getHookTimeoutDefault)('onBeforePrerenderStart')
224
223
  });
225
224
  })));
226
- await Promise.all(onBeforePrerenderStartHooks.map(({ hookFn, hookName, hookFilePath, pageId }) => concurrencyLimit(async () => {
225
+ await Promise.all(onBeforePrerenderStartHooks.map(({ hookFn, hookName, hookFilePath, pageId, hookTimeout }) => concurrencyLimit(async () => {
227
226
  if (doNotPrerenderList.find((p) => p.pageId === pageId)) {
228
227
  return;
229
228
  }
230
- const prerenderResult = await hookFn();
229
+ const prerenderResult = await (0, utils_js_1.executeHook)(() => hookFn(), { hookName, hookFilePath, hookTimeout });
231
230
  const result = normalizeOnPrerenderHookResult(prerenderResult, hookFilePath, hookName);
232
231
  result.forEach(({ url, pageContext }) => {
233
232
  {
@@ -326,23 +325,21 @@ async function callOnPrerenderStartHook(prerenderContext, renderContext) {
326
325
  let onPrerenderStartHook;
327
326
  // V1 design
328
327
  if (renderContext.pageConfigs.length > 0) {
329
- const { pageConfigGlobal } = renderContext;
330
- const configValue = pageConfigGlobal.configValues.onPrerenderStart;
331
- if (configValue?.value) {
332
- const { value: hookFn } = configValue;
333
- // config.onPrerenderStart isn't a computed nor a cumulative config => definedAt should always be defined
334
- const hookFilePath = (0, helpers_js_1.getHookFilePathToShowToUser)(configValue);
335
- (0, utils_js_1.assert)(hookFilePath);
328
+ const hookName = 'onPrerenderStart';
329
+ const hook = (0, getHook_js_1.getHookFromPageConfigGlobal)(renderContext.pageConfigGlobal, hookName);
330
+ if (hook) {
331
+ (0, utils_js_1.assert)(hook.hookName === 'onPrerenderStart');
336
332
  onPrerenderStartHook = {
337
- hookFn,
338
- hookName: 'onPrerenderStart',
339
- hookFilePath
333
+ ...hook,
334
+ // Make TypeScript happy
335
+ hookName
340
336
  };
341
337
  }
342
338
  }
343
339
  // Old design
344
340
  // TODO/v1-release: remove
345
341
  if (renderContext.pageConfigs.length === 0) {
342
+ const hookTimeout = (0, getHook_js_1.getHookTimeoutDefault)('onBeforePrerender');
346
343
  const pageFilesWithOnBeforePrerenderHook = renderContext.pageFilesAll.filter((p) => {
347
344
  assertExportNames(p);
348
345
  if (!p.exportNames?.includes('onBeforePrerender'))
@@ -368,7 +365,8 @@ async function callOnPrerenderStartHook(prerenderContext, renderContext) {
368
365
  onPrerenderStartHook = {
369
366
  hookFn: hook.onBeforePrerender,
370
367
  hookFilePath: hook.hookFilePath,
371
- hookName: 'onBeforePrerender'
368
+ hookName: 'onBeforePrerender',
369
+ hookTimeout
372
370
  };
373
371
  }
374
372
  if (!onPrerenderStartHook) {
@@ -403,7 +401,7 @@ async function callOnPrerenderStartHook(prerenderContext, renderContext) {
403
401
  });
404
402
  return prerenderContext.pageContexts;
405
403
  }
406
- }), hookName, hookFilePath);
404
+ }), onPrerenderStartHook);
407
405
  if (result === null || result === undefined) {
408
406
  return;
409
407
  }
@@ -179,7 +179,7 @@ async function renderTemplate(templateContent, pageContext) {
179
179
  };
180
180
  (0, utils_js_1.assertUsage)(!(0, utils_js_1.isPromise)(templateVar), getErrMsg('a promise', `Did you forget to ${picocolors_1.default.cyan('await')} the promise?`));
181
181
  if (templateVar === undefined || templateVar === null) {
182
- (0, utils_js_1.assertWarning)(false, getErrMsg(`${picocolors_1.default.cyan(String(templateVar))} which will be converted to an empty string`, `Pass an empty string instead of ${picocolors_1.default.cyan(String(templateVar))} to remove this warning.`), { onlyOnce: false });
182
+ (0, utils_js_1.assertWarning)(false, getErrMsg(`${picocolors_1.default.cyan(String(templateVar))} which will be converted to an empty string`, `Pass the empty string ${picocolors_1.default.cyan("''")} instead of ${picocolors_1.default.cyan(String(templateVar))} to remove this warning.`), { onlyOnce: false });
183
183
  templateVar = '';
184
184
  }
185
185
  {
@@ -15,7 +15,7 @@ async function executeOnBeforeRenderHooks(pageContext) {
15
15
  }
16
16
  const onBeforeRender = hook.hookFn;
17
17
  (0, preparePageContextForUserConsumptionServerSide_js_1.preparePageContextForUserConsumptionServerSide)(pageContext);
18
- const hookResult = await (0, utils_js_1.executeHook)(() => onBeforeRender(pageContext), 'onBeforeRender', hook.hookFilePath);
18
+ const hookResult = await (0, utils_js_1.executeHook)(() => onBeforeRender(pageContext), hook);
19
19
  (0, assertOnBeforeRenderHookReturn_js_1.assertOnBeforeRenderHookReturn)(hookResult, hook.hookFilePath);
20
20
  const pageContextFromHook = hookResult?.pageContext;
21
21
  Object.assign(pageContext, pageContextFromHook);
@@ -17,7 +17,7 @@ async function executeOnRenderHtmlHook(pageContext) {
17
17
  const { renderHook, hookFn } = getRenderHook(pageContext);
18
18
  (0, utils_js_1.objectAssign)(pageContext, { _renderHook: renderHook });
19
19
  (0, preparePageContextForUserConsumptionServerSide_js_1.preparePageContextForUserConsumptionServerSide)(pageContext);
20
- const hookReturnValue = await (0, utils_js_1.executeHook)(() => hookFn(pageContext), renderHook.hookName, renderHook.hookFilePath);
20
+ const hookReturnValue = await (0, utils_js_1.executeHook)(() => hookFn(pageContext), renderHook);
21
21
  const { documentHtml, pageContextProvidedByRenderHook, pageContextPromise, injectFilter } = processHookReturnValue(hookReturnValue, renderHook);
22
22
  Object.assign(pageContext, pageContextProvidedByRenderHook);
23
23
  (0, utils_js_1.objectAssign)(pageContext, { _pageContextPromise: pageContextPromise });
@@ -56,10 +56,10 @@ function getRenderHook(pageContext) {
56
56
  }
57
57
  if (hook) {
58
58
  (0, utils_js_1.assert)(hookName);
59
- const { hookFilePath, hookFn } = hook;
59
+ const { hookFilePath, hookFn, hookTimeout } = hook;
60
60
  hookFound = {
61
61
  hookFn,
62
- renderHook: { hookFilePath, hookName }
62
+ renderHook: { hookFn, hookFilePath, hookName, hookTimeout }
63
63
  };
64
64
  }
65
65
  }
@@ -155,7 +155,7 @@ async function getRenderContext() {
155
155
  const globalContext = (0, globalContext_js_1.getGlobalContext)();
156
156
  const { pageFilesAll, allPageIds, pageConfigs, pageConfigGlobal } = await (0, getPageFiles_js_1.getPageFilesAll)(false, globalContext.isProduction);
157
157
  const { pageRoutes, onBeforeRouteHook } = await (0, loadPageRoutes_js_1.loadPageRoutes)(pageFilesAll, pageConfigs, pageConfigGlobal, allPageIds);
158
- assertNonMixedDesign(pageFilesAll, pageConfigs);
158
+ assertV1Design(pageFilesAll, pageConfigs);
159
159
  const renderContext = {
160
160
  pageFilesAll: pageFilesAll,
161
161
  pageConfigs,
@@ -167,21 +167,24 @@ async function getRenderContext() {
167
167
  return renderContext;
168
168
  }
169
169
  exports.getRenderContext = getRenderContext;
170
- function assertNonMixedDesign(pageFilesAll, pageConfigs) {
171
- if (pageFilesAll.length === 0 || pageConfigs.length === 0)
172
- return;
173
- const indent = '- ';
174
- const v1Files = (0, utils_js_1.unique)(pageConfigs
175
- .map((p) => Object.values(p.configValues)
176
- .map(helpers_js_1.getConfigValueFilePathToShowToUser)
177
- .filter(utils_js_1.isNotNullish)
178
- .map((filePathToShowToUser) => indent + filePathToShowToUser))
179
- .flat(2));
180
- (0, utils_js_1.assertUsage)(false, [
181
- 'Mixing the new V1 design with the old V0.4 design is forbidden.',
182
- 'V1 files:',
183
- ...v1Files,
184
- 'V0.4 files:',
185
- ...pageFilesAll.map((p) => indent + p.filePath)
186
- ].join('\n'));
170
+ function assertV1Design(pageFilesAll, pageConfigs) {
171
+ const isV1Design = pageConfigs.length !== 0;
172
+ const isDesignOld = pageFilesAll.length !== 0;
173
+ if (isV1Design && isDesignOld) {
174
+ const indent = '- ';
175
+ const v1Files = (0, utils_js_1.unique)(pageConfigs
176
+ .map((p) => Object.values(p.configValues)
177
+ .map(helpers_js_1.getConfigValueFilePathToShowToUser)
178
+ .filter(utils_js_1.isNotNullish)
179
+ .map((filePathToShowToUser) => indent + filePathToShowToUser))
180
+ .flat(2));
181
+ (0, utils_js_1.assertUsage)(false, [
182
+ 'Mixing the new V1 design with the old V0.4 design is forbidden.',
183
+ 'V1 files:',
184
+ ...v1Files,
185
+ 'V0.4 files:',
186
+ ...pageFilesAll.map((p) => indent + p.filePath)
187
+ ].join('\n'));
188
+ }
189
+ (0, utils_js_1.assertWarning)(!isDesignOld, 'You are using the old deprecated design, update to the new V1 design, see https://vike.dev/migration/v1-design', { onlyOnce: true });
187
190
  }
@@ -5,7 +5,7 @@ const utils_js_1 = require("../utils.js");
5
5
  const assert_exports_old_design_js_1 = require("./assert_exports_old_design.js");
6
6
  const getPageFileObject_js_1 = require("./getPageFileObject.js");
7
7
  const fileTypes_js_1 = require("./fileTypes.js");
8
- const assertPageConfigs_js_1 = require("../page-configs/serialize/assertPageConfigs.js");
8
+ const assertPageConfigsSerialized_js_1 = require("../page-configs/serialize/assertPageConfigsSerialized.js");
9
9
  const parsePageConfigs_js_1 = require("../page-configs/serialize/parsePageConfigs.js");
10
10
  function parseGlobResults(pageFilesExports) {
11
11
  (0, utils_js_1.assert)((0, utils_js_1.hasProp)(pageFilesExports, 'isGeneratedFile'));
@@ -21,8 +21,8 @@ function parseGlobResults(pageFilesExports) {
21
21
  (0, utils_js_1.assert)((0, utils_js_1.hasProp)(pageFilesExports, 'pageConfigsSerialized'));
22
22
  (0, utils_js_1.assert)((0, utils_js_1.hasProp)(pageFilesExports, 'pageConfigGlobalSerialized'));
23
23
  const { pageConfigsSerialized, pageConfigGlobalSerialized } = pageFilesExports;
24
- (0, assertPageConfigs_js_1.assertPageConfigsSerialized)(pageConfigsSerialized);
25
- (0, assertPageConfigs_js_1.assertPageConfigGlobalSerialized)(pageConfigGlobalSerialized);
24
+ (0, assertPageConfigsSerialized_js_1.assertPageConfigsSerialized)(pageConfigsSerialized);
25
+ (0, assertPageConfigsSerialized_js_1.assertPageConfigGlobalSerialized)(pageConfigGlobalSerialized);
26
26
  const { pageConfigs, pageConfigGlobal } = (0, parsePageConfigs_js_1.parsePageConfigs)(pageConfigsSerialized, pageConfigGlobalSerialized);
27
27
  const pageFilesMap = {};
28
28
  parseGlobResult(pageFilesExports.pageFilesLazy).forEach(({ filePath, pageFile, globValue }) => {
@@ -14,8 +14,8 @@ function isUserHookError(err) {
14
14
  return globalObject.userHookErrors.get(err) ?? false;
15
15
  }
16
16
  exports.isUserHookError = isUserHookError;
17
- function executeHook(hookFn, hookName, hookFilePath) {
18
- const { timeoutErr, timeoutWarn } = getTimeouts(hookName);
17
+ function executeHook(hookFnCaller, hook) {
18
+ const { hookName, hookFilePath, hookTimeout: { error: timeoutErr, warning: timeoutWarn } } = hook;
19
19
  let resolve;
20
20
  let reject;
21
21
  const promise = new Promise((resolve_, reject_) => {
@@ -29,19 +29,23 @@ function executeHook(hookFn, hookName, hookFilePath) {
29
29
  };
30
30
  });
31
31
  const clearTimeouts = () => {
32
- clearTimeout(t1);
33
- clearTimeout(t2);
32
+ if (currentTimeoutWarn)
33
+ clearTimeout(currentTimeoutWarn);
34
+ if (currentTimeoutErr)
35
+ clearTimeout(currentTimeoutErr);
34
36
  };
35
- const t1 = setTimeout(() => {
36
- (0, assert_js_1.assertWarning)(false, `The ${hookName}() hook defined by ${hookFilePath} is taking more than ${(0, humanizeTime_js_1.humanizeTime)(timeoutWarn)}`, { onlyOnce: false });
37
- }, timeoutWarn);
38
- const t2 = setTimeout(() => {
39
- const err = (0, assert_js_1.getProjectError)(`Hook timeout: the ${hookName}() hook defined by ${hookFilePath} didn't finish after ${(0, humanizeTime_js_1.humanizeTime)(timeoutErr)}`);
40
- reject(err);
41
- }, timeoutErr);
37
+ const currentTimeoutWarn = isNotDisabled(timeoutWarn) &&
38
+ setTimeout(() => {
39
+ (0, assert_js_1.assertWarning)(false, `The ${hookName}() hook defined by ${hookFilePath} is slow: it's taking more than ${(0, humanizeTime_js_1.humanizeTime)(timeoutWarn)} (https://vike.dev/hooksTimeout)`, { onlyOnce: false });
40
+ }, timeoutWarn);
41
+ const currentTimeoutErr = isNotDisabled(timeoutErr) &&
42
+ setTimeout(() => {
43
+ const err = (0, assert_js_1.getProjectError)(`The ${hookName}() hook defined by ${hookFilePath} timed out: it didn't finish after ${(0, humanizeTime_js_1.humanizeTime)(timeoutErr)} (https://vike.dev/hooksTimeout)`);
44
+ reject(err);
45
+ }, timeoutErr);
42
46
  (async () => {
43
47
  try {
44
- const ret = await hookFn();
48
+ const ret = await hookFnCaller();
45
49
  resolve(ret);
46
50
  }
47
51
  catch (err) {
@@ -54,21 +58,6 @@ function executeHook(hookFn, hookName, hookFilePath) {
54
58
  return promise;
55
59
  }
56
60
  exports.executeHook = executeHook;
57
- function getTimeouts(hookName) {
58
- if (hookName === 'onBeforeRoute') {
59
- return {
60
- timeoutErr: 5 * 1000,
61
- timeoutWarn: 1 * 1000
62
- };
63
- }
64
- if (hookName === 'onBeforePrerender') {
65
- return {
66
- timeoutErr: 10 * 60 * 1000,
67
- timeoutWarn: 30 * 1000
68
- };
69
- }
70
- return {
71
- timeoutErr: 40 * 1000,
72
- timeoutWarn: 4 * 1000
73
- };
61
+ function isNotDisabled(timeout) {
62
+ return !!timeout && timeout !== Infinity;
74
63
  }
@@ -1,11 +1,18 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.assertHookFn = exports.assertHook = exports.getHook = void 0;
6
+ exports.getHookTimeoutDefault = exports.assertHook = exports.getHookFromPageConfigGlobal = exports.getHookFromPageConfig = exports.getHook = void 0;
7
+ const helpers_js_1 = require("../page-configs/helpers.js");
4
8
  const utils_js_1 = require("../utils.js");
9
+ const picocolors_1 = __importDefault(require("@brillout/picocolors"));
5
10
  function getHook(pageContext, hookName) {
6
11
  if (!(hookName in pageContext.exports)) {
7
12
  return null;
8
13
  }
14
+ const { hooksTimeout } = pageContext.config;
15
+ const hookTimeout = getHookTimeout(hooksTimeout, hookName);
9
16
  const hookFn = pageContext.exports[hookName];
10
17
  const file = pageContext.exportsAll[hookName][0];
11
18
  (0, utils_js_1.assert)(file.exportValue === hookFn);
@@ -15,9 +22,41 @@ function getHook(pageContext, hookName) {
15
22
  (0, utils_js_1.assert)(hookFilePath);
16
23
  (0, utils_js_1.assert)(!hookFilePath.endsWith(' '));
17
24
  assertHookFn(hookFn, { hookName, hookFilePath });
18
- return { hookFn, hookName, hookFilePath };
25
+ return { hookFn, hookName, hookFilePath, hookTimeout };
19
26
  }
20
27
  exports.getHook = getHook;
28
+ function getHookFromPageConfig(pageConfig, hookName) {
29
+ const configValue = (0, helpers_js_1.getConfigValue)(pageConfig, hookName);
30
+ const hooksTimeout = (0, helpers_js_1.getConfigValue)(pageConfig, 'hooksTimeout')?.value;
31
+ if (!configValue)
32
+ return null;
33
+ const hookFn = configValue.value;
34
+ if (!hookFn)
35
+ return null;
36
+ const hookFilePath = (0, helpers_js_1.getHookFilePathToShowToUser)(configValue);
37
+ // hook isn't a computed nor a cumulative config => definedAt should always be defined
38
+ (0, utils_js_1.assert)(hookFilePath);
39
+ assertHookFn(hookFn, { hookName, hookFilePath });
40
+ const hookTimeout = getHookTimeout(hooksTimeout, hookName);
41
+ return { hookFn, hookName, hookFilePath, hookTimeout };
42
+ }
43
+ exports.getHookFromPageConfig = getHookFromPageConfig;
44
+ function getHookFromPageConfigGlobal(pageConfigGlobal, hookName) {
45
+ const configValue = pageConfigGlobal.configValues[hookName];
46
+ if (!configValue)
47
+ return null;
48
+ const hookFn = configValue.value;
49
+ if (!hookFn)
50
+ return null;
51
+ const hookFilePath = (0, helpers_js_1.getHookFilePathToShowToUser)(configValue);
52
+ // hook isn't a computed nor a cumulative config => definedAt should always be defined
53
+ (0, utils_js_1.assert)(hookFilePath);
54
+ assertHookFn(hookFn, { hookName, hookFilePath });
55
+ // We could use the global value of config.hooksTimeout but it requires some non-trivial refactoring
56
+ const hookTimeout = getHookTimeoutDefault(hookName);
57
+ return { hookFn, hookName, hookFilePath, hookTimeout };
58
+ }
59
+ exports.getHookFromPageConfigGlobal = getHookFromPageConfigGlobal;
21
60
  function assertHook(pageContext, hookName) {
22
61
  getHook(pageContext, hookName);
23
62
  }
@@ -28,4 +67,66 @@ function assertHookFn(hookFn, { hookName, hookFilePath }) {
28
67
  (0, utils_js_1.assertUsage)((0, utils_js_1.isCallable)(hookFn), `Hook ${hookName}() defined by ${hookFilePath} should be a function`);
29
68
  (0, utils_js_1.checkType)(hookFn);
30
69
  }
31
- exports.assertHookFn = assertHookFn;
70
+ function getHookTimeout(hooksTimeoutProvidedByUser, hookName) {
71
+ const hooksTimeoutProvidedbyUserNormalized = getHooksTimeoutProvidedByUserNormalized(hooksTimeoutProvidedByUser);
72
+ if (hooksTimeoutProvidedbyUserNormalized === false)
73
+ return { error: false, warning: false };
74
+ const providedbyUser = hooksTimeoutProvidedbyUserNormalized[hookName];
75
+ const hookTimeout = getHookTimeoutDefault(hookName);
76
+ if (providedbyUser?.error !== undefined)
77
+ hookTimeout.error = providedbyUser.error;
78
+ if (providedbyUser?.warning !== undefined)
79
+ hookTimeout.warning = providedbyUser.warning;
80
+ return hookTimeout;
81
+ }
82
+ // Ideally this should be called only once and at build-time (to avoid bloating the client-side bundle), but we didn't implement any mechanism to valide config values at build-time yet
83
+ function getHooksTimeoutProvidedByUserNormalized(hooksTimeoutProvidedByUser) {
84
+ if (hooksTimeoutProvidedByUser === undefined)
85
+ return {};
86
+ if (hooksTimeoutProvidedByUser === false)
87
+ return false;
88
+ (0, utils_js_1.assertUsage)((0, utils_js_1.isObject)(hooksTimeoutProvidedByUser), `Setting ${picocolors_1.default.cyan('hooksTimeout')} should be ${picocolors_1.default.cyan('false')} or an object`);
89
+ const hooksTimeoutProvidedByUserNormalized = {};
90
+ Object.entries(hooksTimeoutProvidedByUser).forEach(([hookName, hookTimeoutProvidedbyUser]) => {
91
+ if (hookTimeoutProvidedbyUser === false) {
92
+ hooksTimeoutProvidedByUserNormalized[hookName] = { error: false, warning: false };
93
+ return;
94
+ }
95
+ (0, utils_js_1.assertUsage)((0, utils_js_1.isObject)(hookTimeoutProvidedbyUser), `Setting ${picocolors_1.default.cyan(`hooksTimeout.${hookName}`)} should be ${picocolors_1.default.cyan('false')} or an object`);
96
+ const [error, warning] = ['error', 'warning'].map((timeoutName) => {
97
+ const timeoutVal = hookTimeoutProvidedbyUser[timeoutName];
98
+ if (timeoutVal === undefined || timeoutVal === false)
99
+ return timeoutVal;
100
+ const errPrefix = `Setting ${picocolors_1.default.cyan(`hooksTimeout.${hookName}.${timeoutName}`)} should be`;
101
+ (0, utils_js_1.assertUsage)(typeof timeoutVal === 'number', `${errPrefix} ${picocolors_1.default.cyan('false')} or a number`);
102
+ (0, utils_js_1.assertUsage)(timeoutVal > 0, `${errPrefix} a positive number`);
103
+ return timeoutVal;
104
+ });
105
+ hooksTimeoutProvidedByUserNormalized[hookName] = { error, warning };
106
+ });
107
+ return hooksTimeoutProvidedByUserNormalized;
108
+ }
109
+ function getHookTimeoutDefault(hookName) {
110
+ if (hookName === 'onBeforeRoute') {
111
+ return {
112
+ error: 5 * 1000,
113
+ warning: 1 * 1000
114
+ };
115
+ }
116
+ if (hookName === 'onPrerenderStart' ||
117
+ hookName === 'onBeforePrerenderStart' ||
118
+ // TODO/v1-release: remove
119
+ // Old V0.4 design hooks (https://vike.dev/migration/v1-design#renamed-hooks)
120
+ hookName === 'onBeforePrerender' ||
121
+ hookName === 'prerender') {
122
+ return {
123
+ error: 10 * 60 * 1000,
124
+ warning: 30 * 1000
125
+ };
126
+ }
127
+ return {
128
+ error: 30 * 1000,
129
+ warning: 4 * 1000
130
+ };
131
+ }
132
+ exports.getHookTimeoutDefault = getHookTimeoutDefault;
@@ -10,7 +10,7 @@ const getExportPath_js_1 = require("../getExportPath.js");
10
10
  function getConfigDefinedAtString(sentenceBegin, configName, { definedAt }) {
11
11
  const definedAtString = getDefinedAtString(definedAt, configName);
12
12
  const definedAtStr = definedAtString === 'internally' ? definedAtString : `at ${definedAtString}`;
13
- let configNameStr = `${configName}${sentenceBegin === 'Hook' ? '()' : ''}`;
13
+ let configNameStr = `${configName}${ /*sentenceBegin === 'Hook' ? '()' :*/''}`;
14
14
  const configDefinedAt = `${sentenceBegin} ${picocolors_1.default.cyan(configNameStr)} defined ${definedAtStr}`;
15
15
  return configDefinedAt;
16
16
  }
@@ -21,7 +21,7 @@ async function executeGuardHook(pageContext, prepareForUserConsumption) {
21
21
  const res = prepareForUserConsumption(pageContext);
22
22
  if (res)
23
23
  pageContextForUserConsumption = res;
24
- const hookResult = await (0, utils_js_1.executeHook)(() => guard(pageContextForUserConsumption), 'guard', hook.hookFilePath);
24
+ const hookResult = await (0, utils_js_1.executeHook)(() => guard(pageContextForUserConsumption), hook);
25
25
  (0, utils_js_1.assertUsage)(hookResult === undefined, `The guard() hook of ${hook.hookFilePath} returns a value, but guard() doesn't accept any return value`);
26
26
  }
27
27
  exports.executeGuardHook = executeGuardHook;
@@ -35,6 +35,7 @@ function findPageGuard(pageId, pageFilesAll) {
35
35
  if (!hookFn)
36
36
  return null;
37
37
  const hookFilePath = filePath;
38
+ const hookTimeout = (0, getHook_js_1.getHookTimeoutDefault)('guard');
38
39
  (0, utils_js_1.assertUsage)((0, utils_js_1.isCallable)(hookFn), `guard() defined by ${hookFilePath} should be a function`);
39
- return { hookFn, hookName: 'guard', hookFilePath };
40
+ return { hookFn, hookName: 'guard', hookFilePath, hookTimeout };
40
41
  }
@@ -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 executeHook(pageContext._onBeforeRouteHook, pageContext);
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 executeHook(onBeforeRouteHook, pageContext) {
41
- let hookReturn = onBeforeRouteHook.onBeforeRoute(pageContext);
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 ||