vike
Version:
The Framework *You* Control - Next.js & Nuxt alternative for unprecedented flexibility and dependability.
453 lines (452 loc) • 23.1 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.vikeConfigErrorRecoverMsg = void 0;
exports.getGlobalContext = getGlobalContext;
exports.getGlobalContextSync = getGlobalContextSync;
exports.getGlobalContextAsync = getGlobalContextAsync;
exports.getGlobalContextServerInternal = getGlobalContextServerInternal;
exports.getViteDevServer = getViteDevServer;
exports.getViteConfig = getViteConfig;
exports.initGlobalContext_renderPage = initGlobalContext_renderPage;
exports.initGlobalContext_runPrerender = initGlobalContext_runPrerender;
exports.initGlobalContext_getPagesAndRoutes = initGlobalContext_getPagesAndRoutes;
exports.setGlobalContext_viteDevServer = setGlobalContext_viteDevServer;
exports.setGlobalContext_viteConfig = setGlobalContext_viteConfig;
exports.setGlobalContext_isPrerendering = setGlobalContext_isPrerendering;
exports.setGlobalContext_isProduction = setGlobalContext_isProduction;
exports.setGlobalContext_buildEntry = setGlobalContext_buildEntry;
exports.clearGlobalContext = clearGlobalContext;
exports.assertBuildInfo = assertBuildInfo;
exports.updateUserFiles = updateUserFiles;
// The core logic revolves around:
// - virtualFileExports is the main requirement
// - In production: globalObject.buildEntry which is the production entry set by @brillout/vite-plugin-server-entry
// - loadBuildEntry() sets globalObject.buildEntry and then sets virtualFileExports
// - With vike-server it's set at server start: @brillout/vite-plugin-server-entry injects `import './entry.mjs'` (the production entry generated by @brillout/vite-plugin-server-entry) as first line of code of dist/server/index.mjs while dist/server/entry.mjs calls setGlobalContext_buildEntry()
// - Without vike-server it's manually loaded here using importServerProductionEntry() which uses @brillout/vite-plugin-server-entry's autoImporter or crawler
// - In development: globalObject.viteDevServer which is Vite's development server
// - globalObject.viteDevServer is used by updateUserFiles() which then sets virtualFileExports
const utils_js_1 = require("./utils.js");
const runtime_1 = require("@brillout/vite-plugin-server-entry/runtime");
const virtualFileEntry_js_1 = require("../shared/virtualFiles/virtualFileEntry.js");
const picocolors_1 = __importDefault(require("@brillout/picocolors"));
const loadPageRoutes_js_1 = require("../../shared/route/loadPageRoutes.js");
const assertV1Design_js_1 = require("../shared/assertV1Design.js");
const resolveBase_js_1 = require("../shared/resolveBase.js");
const createGlobalContextShared_js_1 = require("../../shared/createGlobalContextShared.js");
const prepareGlobalContextForPublicUsage_js_1 = require("../../shared/prepareGlobalContextForPublicUsage.js");
const loggerRuntime_js_1 = require("./loggerRuntime.js");
const getVikeConfigError_js_1 = require("../shared/getVikeConfigError.js");
const isNewError_js_1 = require("./renderPage/isNewError.js");
const debug = (0, utils_js_1.createDebugger)('vike:globalContext');
const globalObject = (0, utils_js_1.getGlobalObject)('runtime/globalContext.ts', getInitialGlobalContext());
// Trick to break down TypeScript circular dependency
// https://chat.deepseek.com/a/chat/s/d7e9f90a-c7f3-4108-9cd5-4ad6caed3539
const globalObjectTyped = globalObject;
const vikeConfigErrorRecoverMsg = picocolors_1.default.bold(picocolors_1.default.green('Vike config loaded'));
exports.vikeConfigErrorRecoverMsg = vikeConfigErrorRecoverMsg;
async function getGlobalContextServerInternal() {
// getGlobalContextServerInternal() should always be called after initGlobalContext()
(0, utils_js_1.assert)(globalObject.isInitialized);
assertGlobalContextIsDefined();
if (globalObject.isProduction !== true)
await globalObject.waitForUserFilesUpdate;
const { globalContext } = globalObjectTyped;
assertIsDefined(globalContext);
return { globalContext };
}
function assertIsDefined(globalContext) {
if (!globalContext) {
debug('globalContext', globalContext);
debug('assertIsDefined()', new Error().stack);
(0, utils_js_1.assert)(false);
}
}
function assertGlobalContextIsDefined() {
assertIsDefined(globalObjectTyped.globalContext);
(0, utils_js_1.assert)(globalObject.globalContext);
}
// We purposely return GlobalContext instead of GlobalContextServer because `import { getGlobalContext } from 'vike'` can resolve to the client-side implementation.
/**
* Get runtime information about your app.
*
* https://vike.dev/getGlobalContext
*/
async function getGlobalContext() {
debug('getGlobalContext()');
const { isProduction } = globalObject;
// This assertion cannot fail for vike-server users (because when using vike-server it's guaranteed that globalObject.isProduction is set before executing any user-land code and any Vike extension code).
(0, utils_js_1.assertUsage)(isProduction !== undefined, "The global context isn't set yet, use getGlobalContextAsync() instead.");
(0, utils_js_1.assert)(typeof globalObject.isProduction === 'boolean');
return await getGlobalContextAsync(isProduction);
}
/**
* Get runtime information about your app.
*
* https://vike.dev/getGlobalContext
*/
async function getGlobalContextAsync(isProduction) {
debug('getGlobalContextAsync()');
(0, utils_js_1.assertUsage)(typeof isProduction === 'boolean', `[getGlobalContextAsync(isProduction)] Argument ${picocolors_1.default.cyan('isProduction')} ${isProduction === undefined ? 'is missing' : `should be ${picocolors_1.default.cyan('true')} or ${picocolors_1.default.cyan('false')}`}`);
setIsProduction(isProduction);
if (!globalObject.globalContext)
await initGlobalContext_getGlobalContextAsync();
if (!isProduction)
await globalObject.waitForUserFilesUpdate;
assertGlobalContextIsDefined();
return getGlobalContextForPublicUsage();
}
/**
* Get runtime information about your app.
*
* https://vike.dev/getGlobalContext
*
* @deprecated
*/
function getGlobalContextSync() {
debug('getGlobalContextSync()');
const { globalContext } = globalObjectTyped;
(0, utils_js_1.assertUsage)(globalContext, createGlobalContextShared_js_1.getGlobalContextSyncErrMsg);
(0, utils_js_1.assertWarning)(false,
// We discourage users from using it because `pageContext.globalContext` is safer: I ain't sure but there could be race conditions when using `getGlobalContextSync()` inside React/Vue components upon HMR.
// We're lying about "is going to be deprecated in the next major release": let's keep it and see if users need it (so far I can't see a use case for it).
'getGlobalContextSync() is going to be deprecated in the next major release, see https://vike.dev/getGlobalContext', { onlyOnce: true });
return getGlobalContextForPublicUsage();
}
function getGlobalContextForPublicUsage() {
const { globalContext } = globalObjectTyped;
(0, utils_js_1.assert)(globalContext);
const globalContextForPublicUsage = (0, prepareGlobalContextForPublicUsage_js_1.prepareGlobalContextForPublicUsage)(globalContext);
return globalContextForPublicUsage;
}
async function setGlobalContext_viteDevServer(viteDevServer) {
debug('setGlobalContext_viteDevServer()');
setIsProduction(false);
// We cannot cache globalObject.viteDevServer because it's fully replaced when the user modifies vite.config.js => Vite's dev server is fully reloaded and a new viteDevServer replaces the previous one.
if (!globalObject.viteDevServer) {
assertIsNotInitializedYet();
}
(0, utils_js_1.assert)(globalObject.viteConfig);
globalObject.viteDevServer = viteDevServer;
globalObject.viteDevServerPromiseResolve(viteDevServer);
const { success } = await updateUserFiles();
if (!success)
return;
assertGlobalContextIsDefined();
}
function setGlobalContext_viteConfig(viteConfig, viteConfigRuntime) {
if (globalObject.viteConfig)
return;
assertIsNotInitializedYet();
globalObject.viteConfig = viteConfig;
globalObject.viteConfigRuntime = viteConfigRuntime;
}
function assertIsNotInitializedYet() {
// In development, globalObject.viteDevServer always needs to be awaited for before initializing globalObject.globalContext
(0, utils_js_1.assert)(!globalObject.globalContext);
}
function setGlobalContext_isPrerendering() {
globalObject.isPrerendering = true;
setIsProduction(true);
}
function setGlobalContext_isProduction(isProduction, tolerateContraditction) {
if (globalObject.isProduction === undefined) {
setIsProduction(isProduction);
}
else {
(0, utils_js_1.assert)(globalObject.isProduction === isProduction || tolerateContraditction);
}
}
function getViteDevServer() {
return globalObject.viteDevServer ?? null;
}
function getViteConfig() {
return globalObject.viteConfig ?? null;
}
async function initGlobalContext_renderPage() {
debug('initGlobalContext_renderPage()');
// `globalObject.isProduction === undefined` when using production server without `vike-server`. (There isn't any reliable signal we can use to determine early whether the environment is production or development.)
if (globalObject.isProduction === undefined)
setIsProduction(true);
await initGlobalContext();
}
async function initGlobalContext_runPrerender() {
debug('initGlobalContext_runPrerender()');
(0, utils_js_1.assert)(globalObject.isPrerendering === true);
(0, utils_js_1.assert)(globalObject.isProduction === true);
if (globalObject.initGlobalContext_runPrerender_alreadyCalled)
return;
globalObject.initGlobalContext_runPrerender_alreadyCalled = true;
(0, utils_js_1.assert)(globalObject.isPrerendering);
(0, utils_js_1.assert)(globalObject.viteConfig);
// We assume initGlobalContext_runPrerender() to be called before:
// - initGlobalContext_renderPage()
// - initGlobalContext_getGlobalContextAsync()
assertIsNotInitializedYet();
await initGlobalContext();
}
async function initGlobalContext_getGlobalContextAsync() {
debug('initGlobalContext_getGlobalContextAsync()');
await initGlobalContext();
}
async function initGlobalContext_getPagesAndRoutes() {
debug('initGlobalContext_getPagesAndRoutes()');
setIsProduction(true);
await initGlobalContext();
}
async function initGlobalContext() {
const { isProduction } = globalObject;
(0, utils_js_1.assert)(typeof isProduction === 'boolean');
if (!isProduction) {
await globalObject.viteDevServerPromise;
(0, utils_js_1.assert)(globalObject.waitForUserFilesUpdate);
await globalObject.waitForUserFilesUpdate;
}
else {
await loadBuildEntry(globalObject.viteConfigRuntime?.build.outDir);
}
assertGlobalContextIsDefined();
globalObject.isInitialized = true;
}
function setIsProduction(isProduction) {
debug('setIsProduction', isProduction);
(0, utils_js_1.assert)(typeof isProduction === 'boolean');
if (globalObject.isProduction !== undefined)
(0, utils_js_1.assert)(globalObject.isProduction === isProduction);
globalObject.isProduction = isProduction;
}
function assertViteManifest(manifest) {
(0, utils_js_1.assert)((0, utils_js_1.isPlainObject)(manifest));
/* We should include these assertions but we don't as a workaround for PWA manifests: https://github.com/vikejs/vike/issues/769
Instead, we should rename the vite manifest e.g. with https://vitejs.dev/config/build-options.html#build-manifest
Object.entries(manifest)
// circumvent esbuild bug: esbuild adds a `default` key to JSON upon `require('./some.json')`.
.filter(([key]) => key !== 'default')
.forEach(([_, entry]) => {
assert(isPlainObject(entry))
assert(typeof entry.file === 'string')
})
*/
}
async function loadBuildEntry(outDir) {
debug('loadBuildEntry()');
if (globalObject.globalContext) {
return;
}
if (!globalObject.buildEntry) {
debug('importServerProductionEntry()');
// importServerProductionEntry() loads dist/server/entry.mjs which calls setGlobalContext_buildEntry()
await (0, runtime_1.importServerProductionEntry)({ outDir });
if (!globalObject.buildEntry) {
debug('globalObject.buildEntryPrevious');
// Needed, for example, when calling the API prerender() then preview() because both trigger a importServerProductionEntry() call but only the first only is applied because of the import() cache. (A proper implementation would be to clear the import() cache, but it probably isn't possible on platforms such as Cloudflare Workers.)
globalObject.buildEntry = globalObject.buildEntryPrevious;
}
(0, utils_js_1.assert)(globalObject.buildEntry);
// If using `inject` then dist/server/index.js imports dist/server/entry.js and loadBuildEntry() isn't needed.
// If dist/server/entry.js isn't imported then this means the user is running the original server entry `$ ts-node server/index.ts`.
(0, utils_js_1.assertWarning)(
// vike-server => `inject === true`
// vike-node => `inject === [ 'index' ]` => we don't show the warning to vike-node users (I don't remember why).
globalObject.buildInfo?.viteConfigRuntime.vitePluginServerEntry.inject !== true || globalObject.isPrerendering, `Run the built server entry (e.g. ${picocolors_1.default.cyan('$ node dist/server/index.mjs')}) instead of the original server entry (e.g. ${picocolors_1.default.cyan('$ ts-node server/index.ts')})`, { onlyOnce: true });
}
const { buildEntry } = globalObject;
assertBuildEntry(buildEntry);
globalObject.assetsManifest = buildEntry.assetsManifest;
globalObject.buildInfo = buildEntry.buildInfo;
await setGlobalContext(buildEntry.virtualFileExports);
}
async function setGlobalContext_buildEntry(buildEntry) {
debug('setGlobalContext_buildEntry()');
setIsProduction(true);
assertBuildEntry(buildEntry);
globalObject.buildEntry = buildEntry;
globalObject.buildEntryPrevious = buildEntry;
(0, utils_js_1.assert)(globalObject.buildEntry); // ensure no infinite loop
await loadBuildEntry();
assertGlobalContextIsDefined();
}
function assertBuildEntry(buildEntry) {
(0, utils_js_1.assert)((0, utils_js_1.isObject)(buildEntry));
(0, utils_js_1.assert)((0, utils_js_1.hasProp)(buildEntry, 'virtualFileExports', 'object'));
const { virtualFileExports } = buildEntry;
(0, utils_js_1.assert)((0, utils_js_1.hasProp)(buildEntry, 'assetsManifest', 'object'));
const { assetsManifest } = buildEntry;
assertViteManifest(assetsManifest);
(0, utils_js_1.assert)((0, utils_js_1.hasProp)(buildEntry, 'buildInfo', 'object'));
const { buildInfo } = buildEntry;
assertBuildInfo(buildInfo);
(0, utils_js_1.checkType)({ virtualFileExports, assetsManifest, buildInfo });
}
function assertBuildInfo(buildInfo) {
(0, utils_js_1.assert)((0, utils_js_1.isObject)(buildInfo));
(0, utils_js_1.assert)((0, utils_js_1.hasProp)(buildInfo, 'versionAtBuildTime', 'string'));
assertVersionAtBuildTime(buildInfo.versionAtBuildTime);
(0, utils_js_1.assert)((0, utils_js_1.hasProp)(buildInfo, 'viteConfigRuntime', 'object'));
(0, utils_js_1.assert)((0, utils_js_1.hasProp)(buildInfo.viteConfigRuntime, '_baseViteOriginal', 'string'));
(0, utils_js_1.assert)((0, utils_js_1.hasProp)(buildInfo.viteConfigRuntime, 'root', 'string'));
(0, utils_js_1.assert)((0, utils_js_1.hasProp)(buildInfo.viteConfigRuntime, 'build', 'object'));
(0, utils_js_1.assert)((0, utils_js_1.hasProp)(buildInfo.viteConfigRuntime.build, 'outDir', 'string'));
(0, utils_js_1.assert)((0, utils_js_1.hasProp)(buildInfo.viteConfigRuntime, 'vitePluginServerEntry', 'object'));
(0, utils_js_1.assert)((0, utils_js_1.hasProp)(buildInfo, 'usesClientRouter', 'boolean'));
}
function assertVersionAtBuildTime(versionAtBuildTime) {
const versionAtRuntime = utils_js_1.PROJECT_VERSION;
const pretty = (version) => picocolors_1.default.bold(`vike@${version}`);
(0, utils_js_1.assertUsage)(versionAtBuildTime === versionAtRuntime, `Re-build your app (you're using ${pretty(versionAtRuntime)} but your app was built with ${pretty(versionAtBuildTime)})`);
}
async function updateUserFiles() {
(0, utils_js_1.assert)(!globalObject.isProduction);
const { promise, resolve } = (0, utils_js_1.genPromise)();
globalObject.waitForUserFilesUpdate = promise;
globalObject.waitForUserFilesUpdateResolve ?? (globalObject.waitForUserFilesUpdateResolve = []);
globalObject.waitForUserFilesUpdateResolve.push(resolve);
const onError = (err) => {
if (!(0, isNewError_js_1.hasAlreadyLogged)(err)) {
(0, loggerRuntime_js_1.logRuntimeError)(err, null);
}
(0, getVikeConfigError_js_1.setVikeConfigError)({ errorRuntime: { err } });
globalObject.vikeConfigHasRuntimeError = true;
return { success: false };
};
const onSuccess = () => {
if (globalObject.vikeConfigHasRuntimeError) {
(0, utils_js_1.assert)(loggerRuntime_js_1.logRuntimeInfo); // always defined in dev
(0, loggerRuntime_js_1.logRuntimeInfo)(vikeConfigErrorRecoverMsg, null, 'error-recover');
}
globalObject.vikeConfigHasRuntimeError = false;
(0, getVikeConfigError_js_1.setVikeConfigError)({ errorRuntime: false });
globalObject.waitForUserFilesUpdateResolve.forEach((resolve) => resolve());
globalObject.waitForUserFilesUpdateResolve = [];
resolve();
return { success: true };
};
const isOutdated = () =>
// There is a newer call — let the new call supersede the old one.
// We deliberately swallow the intermetidate state (including any potential error) — it's now outdated and has existed only for a very short period of time.
globalObject.waitForUserFilesUpdate !== promise ||
// Avoid race condition: abort if there is a new globalObject.viteDevServer (happens when vite.config.js is modified => Vite's dev server is fully reloaded).
viteDevServer !== globalObject.viteDevServer;
const { viteDevServer } = globalObject;
(0, utils_js_1.assert)(viteDevServer);
let hasError = false;
let virtualFileExports;
let err;
try {
virtualFileExports = await viteDevServer.ssrLoadModule(virtualFileEntry_js_1.virtualFileIdEntryServer);
}
catch (err_) {
hasError = true;
err = err_;
}
if (isOutdated())
return { success: false };
if (hasError)
return onError(err);
virtualFileExports = virtualFileExports.default || virtualFileExports;
if ((0, getVikeConfigError_js_1.getVikeConfigErrorBuild)()) {
return { success: false };
}
try {
await setGlobalContext(virtualFileExports);
}
catch (err_) {
hasError = true;
err = err_;
}
if (isOutdated())
return { success: false };
if (hasError)
return onError(err);
return onSuccess();
}
async function setGlobalContext(virtualFileExports) {
(0, utils_js_1.assert)(!(0, getVikeConfigError_js_1.getVikeConfigErrorBuild)());
const globalContext = await (0, createGlobalContextShared_js_1.createGlobalContextShared)(virtualFileExports, globalObject, addGlobalContext);
(0, assertV1Design_js_1.assertV1Design)(
// pageConfigs is PageConfigRuntime[] but assertV1Design() requires PageConfigBuildTime[]
globalContext._pageConfigs.length > 0, globalContext._pageFilesAll);
assertGlobalContextIsDefined();
(0, utils_js_1.onSetupRuntime)();
// Never actually used, only used for TypeScript `ReturnType<typeof setGlobalContext>`
return globalContext;
}
async function addGlobalContext(globalContext) {
const { pageRoutes, onBeforeRouteHook } = await (0, loadPageRoutes_js_1.loadPageRoutes)(globalContext._pageFilesAll, globalContext._pageConfigs, globalContext._pageConfigGlobal, globalContext._allPageIds);
const globalContextBase = {
isClientSide: false,
_pageRoutes: pageRoutes,
_onBeforeRouteHook: onBeforeRouteHook,
};
const { viteDevServer, viteConfig, viteConfigRuntime, isPrerendering, isProduction } = globalObject;
(0, utils_js_1.assert)(typeof isProduction === 'boolean');
if (!isProduction) {
(0, utils_js_1.assert)(viteDevServer);
(0, utils_js_1.assert)(globalContext); // main common requirement
(0, utils_js_1.assert)(viteConfig);
(0, utils_js_1.assert)(viteConfigRuntime);
(0, utils_js_1.assert)(!isPrerendering);
return {
...globalContextBase,
...resolveBaseRuntime(viteConfigRuntime, globalContext.config),
_isProduction: false,
_isPrerendering: false,
assetsManifest: null,
_viteDevServer: viteDevServer,
viteConfig,
viteConfigRuntime,
};
}
else {
(0, utils_js_1.assert)(globalObject.buildEntry);
(0, utils_js_1.assert)(globalContext); // main common requiement
const { buildInfo, assetsManifest } = globalObject;
(0, utils_js_1.assert)(buildInfo);
(0, utils_js_1.assert)(assetsManifest);
const globalContextBase2 = {
...globalContextBase,
...resolveBaseRuntime(buildInfo.viteConfigRuntime, globalContext.config),
_isProduction: true,
assetsManifest,
_viteDevServer: null,
viteConfigRuntime: buildInfo.viteConfigRuntime,
_usesClientRouter: buildInfo.usesClientRouter,
};
if (isPrerendering) {
(0, utils_js_1.assert)(viteConfig);
return {
...globalContextBase2,
_isPrerendering: true,
viteConfig,
};
}
else {
return {
...globalContextBase2,
_isPrerendering: false,
viteConfig: null,
};
}
}
}
function clearGlobalContext() {
debug('clearGlobalContext()');
(0, utils_js_1.objectReplace)(globalObject, getInitialGlobalContext(), ['buildEntryPrevious']);
}
function getInitialGlobalContext() {
debug('getInitialGlobalContext()');
const { promise: viteDevServerPromise, resolve: viteDevServerPromiseResolve } = (0, utils_js_1.genPromise)();
return {
viteDevServerPromise,
viteDevServerPromiseResolve,
};
}
function resolveBaseRuntime(viteConfigRuntime, config) {
const baseViteOriginal = viteConfigRuntime._baseViteOriginal;
const baseServerUnresolved = config.baseServer ?? null;
const baseAssetsUnresolved = config.baseAssets ?? null;
return (0, resolveBase_js_1.resolveBase)(baseViteOriginal, baseServerUnresolved, baseAssetsUnresolved);
}