diff --git a/docs/pages/prerender/+Page.mdx b/docs/pages/prerender/+Page.mdx index 1de7dc33d5f..084f28c7ed0 100644 --- a/docs/pages/prerender/+Page.mdx +++ b/docs/pages/prerender/+Page.mdx @@ -183,6 +183,50 @@ You can then manually trigger pre-rendering using: - +### `value` + +When you set `prerender` to an object then you also enable pre-rendering. In other words: + +```js +// pages/+config.js + +export default { + // Setting it to an empty object: + prerender: {}, + // Is equivalent to: + prerender: true +} +``` + +By setting `prerender.value` to `null` you opt-out from this. In other words: + +```js +// pages/+config.js + +export default { + // This: + prerender: { value: null }, + // Is equivalent to that: + prerender: null +} +``` + +It's used by Vike extensions to change pre-rendering settings without enabling pre-rendering on behalf of the user. + +```js +// node_modules/vike-some-extension/+config.js + +export default { + prerender: { + // Change pre-rendering setting: + partial: true, + // Without enabling pre-rendering: + value: null + } +} +``` + + ## See also - diff --git a/test/playground/pages/+config.ts b/test/playground/pages/+config.ts index 7162c6e1019..941617f8d76 100644 --- a/test/playground/pages/+config.ts +++ b/test/playground/pages/+config.ts @@ -3,8 +3,10 @@ import vikeReact from 'vike-react/config' export default { title: 'Big Playground', + prerenderSetOverEffect: true, prerender: { - // TEST: prerender.noExtraDir + value: null, + parallel: 4, noExtraDir: true }, redirects: { diff --git a/test/playground/vite.config.ts b/test/playground/vite.config.ts index 99d65dd814f..1330a470d91 100644 --- a/test/playground/vite.config.ts +++ b/test/playground/vite.config.ts @@ -30,7 +30,7 @@ type Vike = ReturnType // TEST: getVikeConfig() function testVikeConfig(vike: Vike) { assert(typeof vike.config.prerender![0] === 'object') - assert(vike.config.prerender![0].noExtraDir) + assert(vike.config.prerender![0].parallel === 4) assert(vike.pages) assert(vike.pages['/pages/index']!.config.prerender![0] === false) assert(vike.pages['/pages/markdown']!.config.prerender![0]) @@ -65,4 +65,7 @@ function testPrerenderSettings(vike: Vike) { const wasPrerendered = pageIdsPrerendered.includes(pageId) assert(wasPrerendered === prerendered, debug) }) + + // TEST: prerender.noExtraDir + prerenderContext.output.forEach(({ filePath }) => assert(!filePath.endsWith('index.html'), filePath)) } diff --git a/vike/node/plugin/plugins/commonConfig.ts b/vike/node/plugin/plugins/commonConfig.ts index ca16d23f213..c31049e9df1 100644 --- a/vike/node/plugin/plugins/commonConfig.ts +++ b/vike/node/plugin/plugins/commonConfig.ts @@ -22,8 +22,9 @@ import { isViteCliCall } from '../shared/isViteCliCall.js' import { isVikeCliOrApi } from '../../api/context.js' import { getVikeConfig2, type VikeConfigObject } from './importUserCode/v1-design/getVikeConfig.js' import { assertViteRoot, getViteRoot, normalizeViteRoot } from '../../api/prepareViteApiCall.js' -import { isPrerenderEnabled, temp_disablePrerenderAutoRun } from '../../prerender/context.js' +import { temp_disablePrerenderAutoRun } from '../../prerender/context.js' import type { PrerenderContextPublic } from '../../prerender/runPrerender.js' +import { resolvePrerenderConfigGlobal } from '../../prerender/resolvePrerenderConfig.js' const pluginName = 'vike:commonConfig' declare module 'vite' { @@ -69,7 +70,7 @@ function commonConfig(vikeVitePluginOptions: unknown): Plugin[] { }, // TODO/v1-release: remove https://github.com/vikejs/vike/issues/2122 configVikePromise: Promise.resolve({ - prerender: isPrerenderEnabled(vikeConfig) + prerender: resolvePrerenderConfigGlobal(vikeConfig).isEnabled }) } } diff --git a/vike/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.ts b/vike/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.ts index 728d02f952e..6562adb5fb8 100644 --- a/vike/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.ts +++ b/vike/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.ts @@ -236,7 +236,7 @@ async function loadVikeConfig(userRootDir: string, vikeVitePluginOptions: unknow userRootDir ) - // interop vike(options) in vite.config.js + // Backwards compatibility for vike(options) in vite.config.js temp_interopVikeVitePlugin(pageConfigGlobal, vikeVitePluginOptions, userRootDir) // global @@ -917,9 +917,7 @@ function assertMetaUsage( }) } -// TODO/now update perma links -// https://github.com/vikejs/vike/blob/052ed41ffe67097c25026d7409f8741c820ea6c8/test/playground/vite.config.ts#L39 -// https://github.com/vikejs/vike/blob/052ed41ffe67097c25026d7409f8741c820ea6c8/test/playground/pages/config-meta/effect/e2e-test.ts#L16 +// Test: https://github.com/vikejs/vike/blob/441a37c4c1a3b07bb8f6efb1d1f7be297a53974a/test/playground/vite.config.ts#L39 function applyEffectsConfVal(configValueSources: ConfigValueSources, configDefinitions: ConfigDefinitions) { objectEntries(configDefinitions).forEach(([configNameEffect, configDefEffect]) => { const sourceEffect = configValueSources[configNameEffect]?.[0] @@ -938,6 +936,7 @@ function applyEffectsConfVal(configValueSources: ConfigValueSources, configDefin ) }) } +// Test: https://github.com/vikejs/vike/blob/441a37c4c1a3b07bb8f6efb1d1f7be297a53974a/test/playground/pages/config-meta/effect/e2e-test.ts#L16 function applyEffectsMetaEnv(configValueSources: ConfigValueSources, configDefinitions: ConfigDefinitions) { objectEntries(configDefinitions).forEach(([configNameEffect, configDefEffect]) => { const sourceEffect = configValueSources[configNameEffect]?.[0] diff --git a/vike/node/prerender/context.ts b/vike/node/prerender/context.ts index ee2991fe2b5..39f1b37fd92 100644 --- a/vike/node/prerender/context.ts +++ b/vike/node/prerender/context.ts @@ -1,4 +1,3 @@ -export { isPrerenderEnabled } export { isPrerenderAutoRunEnabled } export { temp_disablePrerenderAutoRun } export { isPrerendering } @@ -9,14 +8,10 @@ import { getGlobalObject } from '../../utils/getGlobalObject.js' import { resolvePrerenderConfigGlobal } from './resolvePrerenderConfig.js' const globalObject = getGlobalObject<{ isDisabled?: true; isPrerendering?: true }>('prerender/context.ts', {}) -function isPrerenderEnabled(vikeConfig: VikeConfigObject) { - const prerenderConfigGlobal = resolvePrerenderConfigGlobal(vikeConfig) - return !!prerenderConfigGlobal -} function isPrerenderAutoRunEnabled(vikeConfig: VikeConfigObject) { const prerenderConfigGlobal = resolvePrerenderConfigGlobal(vikeConfig) return ( - isPrerenderEnabled(vikeConfig) && + prerenderConfigGlobal.isEnabled && !(prerenderConfigGlobal || {}).disableAutoRun && !globalObject.isDisabled && vikeConfig.global.config.disableAutoFullBuild !== 'prerender' diff --git a/vike/node/prerender/resolvePrerenderConfig.ts b/vike/node/prerender/resolvePrerenderConfig.ts index 11359dd32bd..7e9f43798c1 100644 --- a/vike/node/prerender/resolvePrerenderConfig.ts +++ b/vike/node/prerender/resolvePrerenderConfig.ts @@ -1,38 +1,38 @@ export { resolvePrerenderConfigGlobal } export { resolvePrerenderConfigLocal } -import type { ConfigResolved } from '../../shared/page-configs/Config/PageContextConfig.js' import { VikeConfigObject } from '../plugin/plugins/importUserCode/v1-design/getVikeConfig.js' -import { assert, isArray } from './utils.js' +import { assert, isArray, objectAssign } from './utils.js' import { getConfigValueBuildTime } from '../../shared/page-configs/getConfigValueBuildTime.js' import type { PageConfigBuildTime } from '../../shared/page-configs/PageConfig.js' -import { getConfigValueFilePathToShowToUser } from '../../shared/page-configs/helpers.js' -type PrerenderConfigSemiResolved = ConfigResolved['prerender'] -type PrerenderConfigGlobal = false | Exclude[number]>, boolean> - -// TODO/now: -// - prerender.value - -function resolvePrerenderConfigGlobal(vikeConfig: VikeConfigObject): PrerenderConfigGlobal { +function resolvePrerenderConfigGlobal(vikeConfig: VikeConfigObject) { const prerenderConfigs = vikeConfig.global.config.prerender - if ( - !prerenderConfigs && - !vikeConfig.pageConfigs.some((pageConfig) => resolvePrerenderConfigLocal(pageConfig)?.value) - ) { - return false - } let prerenderConfigList = prerenderConfigs || [] // Needed because of backwards compatibility of `vike({prerender:true})` in `vite.config.js`; after we remove it we can remove this line. prerenderConfigList = prerenderConfigList.filter(isObject2) assert(prerenderConfigList.every(isObject2)) // Help TS - const prerenderConfigGlobal: PrerenderConfigGlobal = { + const prerenderConfigGlobal = { partial: pickFirst(prerenderConfigList.map((c) => c.partial)) ?? false, noExtraDir: pickFirst(prerenderConfigList.map((c) => c.noExtraDir)) ?? false, parallel: pickFirst(prerenderConfigList.map((c) => c.parallel)) ?? true, disableAutoRun: pickFirst(prerenderConfigList.map((c) => c.disableAutoRun)) ?? false } + + const prerenderConfigGlobalLocalValue = prerenderConfigList.map((c) => c.value).filter((v) => v !== null) + const defaultLocalValue = + pickFirst(prerenderConfigGlobalLocalValue) ?? + (prerenderConfigGlobalLocalValue.length > 0 || + // Backwards compatibility for with vike({ prerender: true }) in vite.config.js + prerenderConfigs?.some((p) => p === true) || + false) + objectAssign(prerenderConfigGlobal, { + defaultLocalValue, + isEnabled: + defaultLocalValue || vikeConfig.pageConfigs.some((pageConfig) => resolvePrerenderConfigLocal(pageConfig)?.value) + }) + return prerenderConfigGlobal } function resolvePrerenderConfigLocal(pageConfig: PageConfigBuildTime) { @@ -43,9 +43,7 @@ function resolvePrerenderConfigLocal(pageConfig: PageConfigBuildTime) { const value = values[0] assert(typeof value === 'boolean') assert(isArray(configValue.definedAtData)) - const configValueFilePathToShowToUser = getConfigValueFilePathToShowToUser(configValue.definedAtData[0]!) - assert(configValueFilePathToShowToUser) - const prerenderConfigLocal = { value, configValueFilePathToShowToUser } + const prerenderConfigLocal = { value } return prerenderConfigLocal } diff --git a/vike/node/prerender/runPrerender.ts b/vike/node/prerender/runPrerender.ts index 81905d89129..7ae5be814e9 100644 --- a/vike/node/prerender/runPrerender.ts +++ b/vike/node/prerender/runPrerender.ts @@ -89,17 +89,7 @@ type HtmlFile = { pageId: string | null } -type DoNotPrerenderList = ({ pageId: string; setByConfigFile: string } & ( - | { - // TODO/v1-release: remove 0.4 case - setByConfigName: 'doNotPrerender' - setByConfigValue: true - } - | { - setByConfigName: 'prerender' - setByConfigValue: false - } -))[] +type DoNotPrerenderList = { pageId: string }[] type ProvidedByHook = null | { hookFilePath: string hookName: 'onBeforePrerenderStart' | 'prerender' @@ -219,17 +209,18 @@ async function runPrerender(options: PrerenderOptions = {}, standaloneTrigger?: const { root } = viteConfig const prerenderConfigGlobal = resolvePrerenderConfigGlobal(vikeConfig) validatePrerenderConfig(prerenderConfigGlobal) - if (!prerenderConfigGlobal) { + const { partial, noExtraDir, parallel, defaultLocalValue, isEnabled } = prerenderConfigGlobal + if (!isEnabled) { assert(standaloneTrigger) + // TODO/now: make it assertUsage() and remove dist/server/entry.mjs whenever possible assertWarning( prerenderConfigGlobal, - `You're executing ${pc.cyan(standaloneTrigger)} but the config ${pc.cyan('prerender')} isn't set to true`, + `You're executing ${pc.cyan(standaloneTrigger)} but you didn't enable pre-rendering. Use the config ${pc.cyan('prerender')} (${pc.underline('https://vike.dev/prerender')}) to enable it.`, { onlyOnce: true } ) } - const { partial = false, noExtraDir = false, parallel = true } = prerenderConfigGlobal || {} const concurrencyLimit = pLimit( parallel === false || parallel === 0 ? 1 : parallel === true || parallel === undefined ? cpus().length : parallel @@ -248,7 +239,13 @@ async function runPrerender(options: PrerenderOptions = {}, standaloneTrigger?: } const doNotPrerenderList: DoNotPrerenderList = [] - await collectDoNoPrerenderList(vikeConfig.pageConfigs, doNotPrerenderList, concurrencyLimit, globalContext) + await collectDoNoPrerenderList( + vikeConfig.pageConfigs, + doNotPrerenderList, + defaultLocalValue, + concurrencyLimit, + globalContext + ) await callOnBeforePrerenderStartHooks(prerenderContext, globalContext, concurrencyLimit, doNotPrerenderList) @@ -286,21 +283,23 @@ async function runPrerender(options: PrerenderOptions = {}, standaloneTrigger?: async function collectDoNoPrerenderList( pageConfigs: PageConfigBuildTime[], doNotPrerenderList: DoNotPrerenderList, + defaultLocalValue: boolean, concurrencyLimit: PLimit, globalContext: GlobalContextInternal ) { // V1 design pageConfigs.forEach((pageConfig) => { const prerenderConfigLocal = resolvePrerenderConfigLocal(pageConfig) - if (!prerenderConfigLocal) return - const { value, configValueFilePathToShowToUser } = prerenderConfigLocal - if (value === false) { - doNotPrerenderList.push({ - pageId: pageConfig.pageId, - setByConfigName: 'prerender', - setByConfigValue: false, - setByConfigFile: configValueFilePathToShowToUser - }) + const { pageId } = pageConfig + if (!prerenderConfigLocal) { + if (!defaultLocalValue) { + doNotPrerenderList.push({ pageId }) + } + } else { + const { value } = prerenderConfigLocal + if (value === false) { + doNotPrerenderList.push({ pageId }) + } } }) @@ -341,12 +340,7 @@ async function collectDoNoPrerenderList( return } else { // Don't pre-render `pageId` - doNotPrerenderList.push({ - pageId, - setByConfigFile: p.filePath, - setByConfigName: 'doNotPrerender', - setByConfigValue: doNotPrerender - }) + doNotPrerenderList.push({ pageId }) } } }) @@ -855,16 +849,11 @@ function warnContradictoryNoPrerenderList( const isContradictory = !!doNotPrerenderListEntry && providedByHook if (!isContradictory) return } - const { setByConfigName, setByConfigValue, setByConfigFile } = doNotPrerenderListEntry 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.`, + )} matching the route of the page ${pc.cyan(pageId)} which isn't configured to be pre-rendered. This is contradictory: either enable pre-rendering for ${pc.cyan(pageId)} or remove the URL ${pc.cyan(urlOriginal)} from the list of URLs to be pre-rendered.`, { onlyOnce: true } ) }) diff --git a/vike/node/prerender/utils.ts b/vike/node/prerender/utils.ts index 66d39833800..02c34b70c91 100644 --- a/vike/node/prerender/utils.ts +++ b/vike/node/prerender/utils.ts @@ -18,3 +18,4 @@ export * from '../../utils/isArray.js' export * from '../../utils/isObject.js' export * from '../../utils/changeEnumerable.js' export * from '../../utils/makePublicCopy.js' +export * from '../../utils/isNotNullish.js' diff --git a/vike/shared/page-configs/Config.ts b/vike/shared/page-configs/Config.ts index faa37476733..4812b72b4c9 100644 --- a/vike/shared/page-configs/Config.ts +++ b/vike/shared/page-configs/Config.ts @@ -360,6 +360,14 @@ type ConfigBuiltIn = { * @default false */ disableAutoRun?: boolean + /** + * Set prerender settings without enabling pre-rendering. + * + * https://vike.dev/prerender#value + * + * @default true + */ + value?: boolean | null } /**