UNPKG

vike

Version:

The Framework *You* Control - Next.js & Nuxt alternative for unprecedented flexibility and dependability.

199 lines (198 loc) 9.48 kB
export { loadPageRoutes }; export { loadPageRoutesSync }; import { isErrorPageId } from '../error-page.js'; import { assert, assertUsage } from '../../utils/assert.js'; import { hasProp } from '../../utils/hasProp.js'; import { isArray } from '../../utils/isArray.js'; import { isCallable } from '../../utils/isCallable.js'; import { slice } from '../../utils/slice.js'; import { deduceRouteStringFromFilesystemPath } from './deduceRouteStringFromFilesystemPath.js'; import { getConfigValueRuntime } from '../page-configs/getConfigValueRuntime.js'; import { getDefinedAtString } from '../page-configs/getConfigDefinedAt.js'; import { warnDeprecatedAllowKey } from './resolveRouteFunction.js'; import { getHookFromPageConfigGlobal, getHookTimeoutDefault } from '../hooks/getHook.js'; async function loadPageRoutes( // Remove all arguments and use GlobalContextServerInternal instead? pageFilesAll, pageConfigs, pageConfigGlobal, allPageIds) { // TO-DO/next-major-release: remove this line, remove this function, rename loadPageRoutesSync() to loadPageRoutes() await Promise.all(pageFilesAll.filter((p) => p.fileType === '.page.route').map((p) => p.loadFile?.())); return loadPageRoutesSync(pageFilesAll, pageConfigs, pageConfigGlobal, allPageIds); } function loadPageRoutesSync( // Remove all arguments and use GlobalContextServerInternal instead? pageFilesAll, pageConfigs, pageConfigGlobal, allPageIds) { const { onBeforeRouteHook, filesystemRoots } = getGlobalHooks(pageFilesAll, pageConfigs, pageConfigGlobal); const pageRoutes = getPageRoutes(filesystemRoots, pageFilesAll, pageConfigs, allPageIds); return { pageRoutes, onBeforeRouteHook }; } function getPageRoutes(filesystemRoots, pageFilesAll, pageConfigs, allPageIds) { const pageRoutes = []; // V1 Design if (pageConfigs.length > 0) { assert(filesystemRoots === null); const comesFromV1PageConfig = true; pageConfigs .filter((p) => !p.isErrorPage) .forEach((pageConfig) => { const pageId = pageConfig.pageId; let pageRoute = null; { const configName = 'route'; const configValue = getConfigValueRuntime(pageConfig, configName); if (configValue) { const route = configValue.value; assert(configValue.definedAtData); const definedAtString = getDefinedAtString(configValue.definedAtData, configName); if (typeof route === 'string') { pageRoute = { pageId, comesFromV1PageConfig, routeString: route, routeDefinedAtString: definedAtString, routeType: 'STRING', }; } else { const { definedAtData } = configValue; assert(!isArray(definedAtData) && !definedAtData.definedBy); const { filePathToShowToUser } = definedAtData; assert(filePathToShowToUser); assert(isCallable(route)); // TO-DO/next-major-release: remove if (getConfigValueRuntime(pageConfig, 'iKnowThePerformanceRisksOfAsyncRouteFunctions', 'boolean')) warnDeprecatedAllowKey(); pageRoute = { pageId, comesFromV1PageConfig, routeFunction: route, routeFunctionFilePath: filePathToShowToUser, routeDefinedAtString: definedAtString, routeType: 'FUNCTION', }; } } } if (!pageRoute) { const { routeFilesystem } = pageConfig; assert(routeFilesystem); const { routeString, definedAtLocation } = routeFilesystem; assert(routeFilesystem.routeString.startsWith('/')); pageRoute = { pageId, routeFilesystemDefinedBy: definedAtLocation, comesFromV1PageConfig, routeString, routeDefinedAtString: null, routeType: 'FILESYSTEM', }; } assert(pageRoute); pageRoutes.push(pageRoute); }); } // Old design // TO-DO/next-major-release: remove if (pageConfigs.length === 0) { assert(filesystemRoots); const comesFromV1PageConfig = false; allPageIds .filter((pageId) => !isErrorPageId(pageId, false)) .forEach((pageId) => { const pageRouteFile = pageFilesAll.find((p) => p.pageId === pageId && p.fileType === '.page.route'); if (!pageRouteFile || !('default' in pageRouteFile.fileExports)) { const routeString = deduceRouteStringFromFilesystemPath(pageId, filesystemRoots); assert(routeString.startsWith('/')); assert(!routeString.endsWith('/') || routeString === '/'); pageRoutes.push({ pageId, comesFromV1PageConfig, routeString, routeDefinedAtString: null, routeFilesystemDefinedBy: `${pageId}.page.*`, routeType: 'FILESYSTEM', }); } else { const { filePath, fileExports } = pageRouteFile; assert(fileExports.default); if (hasProp(fileExports, 'default', 'string')) { const routeString = fileExports.default; assertUsage(routeString.startsWith('/'), `A Route String should start with a leading slash '/' but ${filePath} has \`export default '${routeString}'\`. Make sure to \`export default '/${routeString}'\` instead.`); pageRoutes.push({ pageId, comesFromV1PageConfig, routeString, routeDefinedAtString: filePath, routeType: 'STRING', }); return; } if (hasProp(fileExports, 'default', 'function')) { const routeFunction = fileExports.default; { const allowKey = 'iKnowThePerformanceRisksOfAsyncRouteFunctions'; if (allowKey in fileExports) { warnDeprecatedAllowKey(); } } pageRoutes.push({ pageId, comesFromV1PageConfig, routeFunction, routeFunctionFilePath: filePath, routeDefinedAtString: filePath, routeType: 'FUNCTION', }); return; } assertUsage(false, `The default export of ${filePath} should be a string or a function.`); } }); } return pageRoutes; } function getGlobalHooks(pageFilesAll, pageConfigs, pageConfigGlobal) { // V1 Design if (pageConfigs.length > 0) { const hook = getHookFromPageConfigGlobal(pageConfigGlobal, 'onBeforeRoute'); return { onBeforeRouteHook: hook, filesystemRoots: null }; } // Old design // TO-DO/next-major-release: remove let onBeforeRouteHook = null; const filesystemRoots = []; pageFilesAll .filter((p) => p.fileType === '.page.route' && p.isDefaultPageFile) .forEach(({ filePath, fileExports }) => { assert(fileExports); if ('onBeforeRoute' in fileExports) { assertUsage(hasProp(fileExports, 'onBeforeRoute', 'function'), `\`export { onBeforeRoute }\` of ${filePath} should be a function.`); const { onBeforeRoute } = fileExports; const hookName = 'onBeforeRoute'; onBeforeRouteHook = { hookFilePath: filePath, hookFn: onBeforeRoute, hookName, hookTimeout: getHookTimeoutDefault(hookName), }; } if ('filesystemRoutingRoot' in fileExports) { assertUsage(hasProp(fileExports, 'filesystemRoutingRoot', 'string'), `\`export { filesystemRoutingRoot }\` of ${filePath} should be a string.`); assertUsage(hasProp(fileExports, 'filesystemRoutingRoot', 'string'), `\`export { filesystemRoutingRoot }\` of ${filePath} is \`'${fileExports.filesystemRoutingRoot}'\` but it should start with a leading slash \`/\`.`); filesystemRoots.push({ filesystemRoot: dirname(filePath), urlRoot: fileExports.filesystemRoutingRoot, }); } }); return { onBeforeRouteHook, filesystemRoots }; } function dirname(filePath) { assert(filePath.startsWith('/')); assert(!filePath.endsWith('/')); const paths = filePath.split('/'); const dirPath = slice(paths, 0, -1).join('/') || '/'; assert(dirPath.startsWith('/')); assert(!dirPath.endsWith('/') || dirPath === '/'); return dirPath; }