vike
Version:
The Framework *You* Control - Next.js & Nuxt alternative for unprecedented flexibility and dependability.
173 lines (172 loc) • 7.44 kB
JavaScript
export { importUserCode };
import { normalizePath } from 'vite';
import { getVirtualFilePageConfigValuesAll } from './v1-design/virtual-files/getVirtualFilePageConfigValuesAll.js';
import { getVirtualFileImportUserCode } from './getVirtualFileImportUserCode.js';
import { assert, assertPosixPath } from '../../utils.js';
import { resolveVirtualFileId, isVirtualFileId, getVirtualFileId } from '../../../shared/virtual-files.js';
import { isVirtualFileIdPageConfigValuesAll } from '../../../shared/virtual-files/virtualFilePageConfigValuesAll.js';
import { isVirtualFileIdImportUserCode } from '../../../shared/virtual-files/virtualFileImportUserCode.js';
import { reloadVikeConfig, isV1Design, getVikeConfig, getVikeConfigOptional } from './v1-design/getVikeConfig.js';
import pc from '@brillout/picocolors';
import { logConfigInfo } from '../../shared/loggerNotProd.js';
import { getModuleFilePathAbsolute } from '../../shared/getFilePath.js';
import { updateUserFiles } from '../../../runtime/globalContext.js';
import { isPlusFile } from './v1-design/getVikeConfig/crawlPlusFiles.js';
function importUserCode() {
let config;
let vikeConfig;
return {
name: 'vike:importUserCode',
async configResolved(config_) {
vikeConfig = await getVikeConfig(config_);
config = config_;
// TODO/v1-release: remove
if (!isV1Design(config))
config.experimental.importGlobRestoreExtension = true;
},
resolveId(id) {
if (isVirtualFileId(id)) {
return resolveVirtualFileId(id);
}
},
async handleHotUpdate(ctx) {
try {
return await handleHotUpdate(ctx, config);
}
catch (err) {
// Vite swallows errors thrown by handleHotUpdate()
console.error(err);
throw err;
}
},
async load(id, options) {
if (!isVirtualFileId(id))
return undefined;
id = getVirtualFileId(id);
const isDev = config._isDev;
assert(typeof isDev === 'boolean');
if (isVirtualFileIdPageConfigValuesAll(id)) {
const code = await getVirtualFilePageConfigValuesAll(id, isDev, config);
return code;
}
if (isVirtualFileIdImportUserCode(id)) {
const code = await getVirtualFileImportUserCode(id, options, vikeConfig, config, isDev);
return code;
}
},
configureServer(server) {
handleFileAddRemove(server, config);
}
};
}
function handleFileAddRemove(server, config) {
server.watcher.prependListener('add', (f) => listener(f, false));
server.watcher.prependListener('unlink', (f) => listener(f, true));
return;
async function listener(file, isRemove) {
file = normalizePath(file);
if (isPlusFile(file) || (await isVikeConfigDependency(file, server.moduleGraph))?.modifiesVikeVirtualFiles) {
invalidateVikeVirtualFiles(server);
reloadConfig(file, config, isRemove ? 'removed' : 'created');
}
}
}
function invalidateVikeVirtualFiles(server) {
const vikeVirtualFiles = getVikeVirtualFiles(server);
vikeVirtualFiles.forEach((mod) => {
server.moduleGraph.invalidateModule(mod);
});
}
async function handleHotUpdate(ctx, config) {
const { file, server } = ctx;
const isVikeConfig = await isVikeConfigDependency(ctx.file, ctx.server.moduleGraph);
if (isVikeConfig) {
if (isVikeConfig.modifiesVikeVirtualFiles) {
/* Tailwind breaks this assertion, see https://github.com/vikejs/vike/discussions/1330#discussioncomment-7787238
const isViteModule = ctx.modules.length > 0
assert(!isViteModule)
*/
// Ensure server.ssrLoadModule() loads fresh Vike virtual files (`reloadConfig()` > `updateUserFiles()` > `server.ssrLoadModule()`)
invalidateVikeVirtualFiles(server);
reloadConfig(file, config, 'modified');
// Triggers a full page reload
const vikeVirtualFiles = getVikeVirtualFiles(server);
return vikeVirtualFiles;
}
else {
// Ensure we invalidate `file` *before* server.ssrLoadModule() in updateUserFiles()
// Vite already invalidates it, but possibly *after* handleHotUpdate() and thus after server.ssrLoadModule()
ctx.modules.forEach((mod) => server.moduleGraph.invalidateModule(mod));
updateUserFiles();
}
}
}
async function isVikeConfigDependency(filePathAbsoluteFilesystem, moduleGraph) {
// Check config-only files, for example all pages/+config.js dependencies. (There aren't part of Vite's module graph.)
assertPosixPath(filePathAbsoluteFilesystem);
const vikeConfigObject = await getVikeConfigOptional();
if (vikeConfigObject) {
const { vikeConfigDependencies } = vikeConfigObject;
vikeConfigDependencies.forEach((f) => assertPosixPath(f));
if (vikeConfigDependencies.has(filePathAbsoluteFilesystem))
return { modifiesVikeVirtualFiles: true };
}
// Check using Vite's module graph, for example all +htmlAttributes dependencies.
// Alternatively, simply call updateUserFiles() on every handleHotUpdate() call.
const importers = getImporters(filePathAbsoluteFilesystem, moduleGraph);
const isPlusValueFileDependency = Array.from(importers).some((importer) => importer.file && isPlusFile(importer.file));
if (isPlusValueFileDependency)
return { modifiesVikeVirtualFiles: false };
return null;
}
function reloadConfig(filePath, config, op) {
{
const filePathToShowToUserResolved = getModuleFilePathAbsolute(filePath, config);
const msg = `${op} ${pc.dim(filePathToShowToUserResolved)}`;
logConfigInfo(msg, 'info');
}
reloadVikeConfig(config);
updateUserFiles();
}
function getVikeVirtualFiles(server) {
const vikeVirtualFiles = Array.from(server.moduleGraph.urlToModuleMap.keys())
.filter((url) => isVirtualFileIdPageConfigValuesAll(url) || isVirtualFileIdImportUserCode(url))
.map((url) => {
const mod = server.moduleGraph.urlToModuleMap.get(url);
assert(mod);
return mod;
});
return vikeVirtualFiles;
}
// Get all transitive importers (including the module itself)
function getImporters(file, moduleGraph) {
const importers = new Set();
const mods = moduleGraph.getModulesByFile(file);
if (!mods)
return importers;
for (const mod of mods) {
getModuleImporters(mod).forEach((importer) => {
if (importer)
importers.add(importer);
});
}
return importers;
}
function getModuleImporters(mod, seen = new Set()) {
if (seen.has(mod))
return new Set();
seen.add(mod);
const importers = new Set();
if (mod.id)
importers.add(mod);
// Traverse through the importers (modules that import this module)
for (const importer of mod.importers) {
if (importer.id)
importers.add(importer);
getModuleImporters(importer, seen).forEach((importerTransitive) => {
if (importerTransitive)
importers.add(importerTransitive);
});
}
return importers;
}