vike
Version:
The Framework *You* Control - Next.js & Nuxt alternative for unprecedented flexibility and dependability.
186 lines (182 loc) • 8.15 kB
JavaScript
export { getVirtualFileEntry };
import { assert, assertPosixPath, scriptFileExtensionPattern, debugGlob, isVersionOrAbove, assertWarning, } from '../../utils.js';
import { isVirtualFileIdEntry } from '../../../shared/virtualFiles/virtualFileEntry.js';
import { version as viteVersion } from 'vite';
import { fileTypes } from '../../../../shared/getPageFiles/fileTypes.js';
import path from 'node:path';
import { getVirtualFilePageConfigsEager } from './getVirtualFilePageConfigsEager.js';
import { getVikeConfigInternal, isV1Design as isV1Design_ } from '../../shared/resolveVikeConfigInternal.js';
import { getOutDirs } from '../../shared/getOutDirs.js';
import { isViteServerBuild_options } from '../../shared/isViteServerBuild.js';
import { resolveIncludeAssetsImportedByServer } from '../../../runtime/renderPage/getPageAssets.js';
async function getVirtualFileEntry(id, options, config, isDev) {
const idParsed = isVirtualFileIdEntry(id);
assert(idParsed);
const { isForClientSide, isClientRouting } = idParsed;
assert(isForClientSide === !isViteServerBuild_options(options));
const code = await getCode(config, isForClientSide, isClientRouting, isDev, id);
return code;
}
async function getCode(config, isForClientSide, isClientRouting, isDev, id) {
const { command } = config;
assert(command === 'serve' || command === 'build');
const isBuild = command === 'build';
assert(isDev === !isBuild);
let content = '';
{
const globRoots = getGlobRoots(config);
debugGlob('Glob roots: ', globRoots);
content += await generateGlobImports(globRoots, isBuild, isForClientSide, isClientRouting, isDev, id);
}
debugGlob(`Glob imports for ${isForClientSide ? 'client' : 'server'}:\n`, content);
return content;
}
function determineInjection({ fileType, isForClientSide, isClientRouting, isPrerendering, isBuild, }) {
if (!isForClientSide) {
return {
includeImport: fileType === '.page.server' || fileType === '.page' || fileType === '.page.route',
includeExportNames: isPrerendering && isBuild
? fileType === '.page.client' || fileType === '.page.server' || fileType === '.page' // We extensively use `PageFile['exportNames']` while pre-rendering, in order to avoid loading page files unnecessarily, and therefore reducing memory usage.
: fileType === '.page.client',
};
}
else {
const includeImport = fileType === '.page.client' || fileType === '.css' || fileType === '.page';
if (!isClientRouting) {
return {
includeImport,
includeExportNames: false,
};
}
else {
return {
includeImport: includeImport || fileType === '.page.route',
includeExportNames: fileType === '.page.client' || fileType === '.page.server' || fileType === '.page',
};
}
}
}
async function generateGlobImports(globRoots, isBuild, isForClientSide, isClientRouting, isDev, id) {
let fileContent = `// Generated by Vike
export const pageFilesLazy = {};
export const pageFilesEager = {};
export const pageFilesExportNamesLazy = {};
export const pageFilesExportNamesEager = {};
export const pageFilesList = [];
export const neverLoaded = {};
${await getVirtualFilePageConfigsEager(isForClientSide, isDev, id, isClientRouting)}
`;
// We still use import.meta.glob() when using th V1 design in order to not break the V1 design deprecation warning
const isV1Design = isV1Design_();
const vikeConfig = await getVikeConfigInternal();
// Old design => no + files => only to enable pre-rendering is setting `vike({prerender})` in vite.config.js
const isPrerendering = !!vikeConfig.config.prerender;
fileTypes
.filter((fileType) => fileType !== '.css')
.forEach((fileType) => {
const { includeImport, includeExportNames } = determineInjection({
fileType,
isForClientSide,
isClientRouting,
isPrerendering,
isBuild,
});
if (includeImport) {
fileContent += getGlobs(globRoots, isBuild, fileType, null, isV1Design);
}
if (includeExportNames) {
fileContent += getGlobs(globRoots, isBuild, fileType, 'extractExportNames', isV1Design);
}
});
const includeAssetsImportedByServer = resolveIncludeAssetsImportedByServer(vikeConfig.config);
if (includeAssetsImportedByServer && isForClientSide) {
fileContent += getGlobs(globRoots, isBuild, '.page.server', 'extractAssets', isV1Design);
}
return fileContent;
}
function getGlobs(globRoots, isBuild, fileType, query, isV1Design) {
const isEager = isBuild && (query === 'extractExportNames' || fileType === '.page.route');
let pageFilesVar;
if (query === 'extractExportNames') {
if (!isEager) {
pageFilesVar = 'pageFilesExportNamesLazy';
}
else {
pageFilesVar = 'pageFilesExportNamesEager';
}
}
else if (query === 'extractAssets') {
assert(!isEager);
pageFilesVar = 'neverLoaded';
}
else if (!query) {
if (!isEager) {
pageFilesVar = 'pageFilesLazy';
}
else {
// Used for `.page.route.js` files
pageFilesVar = 'pageFilesEager';
}
}
else {
assert(false);
}
const varNameSuffix = (fileType === '.page' && 'Isomorph') ||
(fileType === '.page.client' && 'Client') ||
(fileType === '.page.server' && 'Server') ||
(fileType === '.page.route' && 'Route');
assert(varNameSuffix);
const varName = `${pageFilesVar}${varNameSuffix}`;
const varNameLocals = [];
return [
...globRoots.map((globRoot, i) => {
const varNameLocal = `${varName}${i + 1}`;
varNameLocals.push(varNameLocal);
const globIncludePath = `'${getGlobPath(globRoot.includeDir, fileType)}'`;
const globExcludePath = globRoot.excludeDir ? `'!${getGlobPath(globRoot.excludeDir, fileType)}'` : null;
const globOptions = { eager: isEager };
if (query) {
const isNewViteInterface = isVersionOrAbove(viteVersion, '5.1.0');
if (isNewViteInterface &&
// When used for the old design, the new syntax breaks Vike's CI (surprinsigly so). I couldn't reproduce locally (I didn't dig much).
isV1Design) {
globOptions.query = `?${query}`;
}
else {
globOptions.as = query;
const msg = [
"Update to the new V1 design to get rid of Vite's warning:",
'The glob option "as" has been deprecated in favour of "query".',
'See https://vike.dev/migration/v1-design for how to migrate.',
].join(' ');
assertWarning(!isNewViteInterface, msg, { onlyOnce: true });
}
}
const globPaths = globExcludePath ? `[${globIncludePath}, ${globExcludePath}]` : `[${globIncludePath}]`;
const globLine = `const ${varNameLocal} = import.meta.glob(${globPaths}, ${JSON.stringify(globOptions)});`;
return globLine;
}),
`const ${varName} = {${varNameLocals.map((varNameLocal) => `...${varNameLocal}`).join(',')}};`,
`${pageFilesVar}['${fileType}'] = ${varName};`,
'',
].join('\n');
}
function getGlobRoots(config) {
const globRoots = [
{
includeDir: '/',
excludeDir: path.posix.relative(config.root, getOutDirs(config).outDirRoot),
},
];
return globRoots;
}
function getGlobPath(globRootDir, fileType) {
assertPosixPath(globRootDir);
let globPath = [...globRootDir.split('/'), '**', `*${fileType}.${scriptFileExtensionPattern}`]
.filter(Boolean)
.join('/');
if (!globPath.startsWith('/')) {
globPath = '/' + globPath;
}
return globPath;
}