UNPKG

vike

Version:

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

132 lines (131 loc) 6.18 kB
export { assertExtensionsConventions }; export { assertExtensionsRequire }; import pc from '@brillout/picocolors'; import { PROJECT_VERSION, assert, assertUsage, assertWarning, findPackageJson, isObject } from '../../utils.js'; import { getConfVal } from '../resolveVikeConfigInternal.js'; import path from 'node:path'; import semver from 'semver'; function assertExtensionsConventions(plusFile) { assertExtensionName(plusFile); assertConfigExportPath(plusFile); } function assertConfigExportPath(plusFile) { const { importPathAbsolute, filePathAbsoluteFilesystem } = plusFile.filePath; // Ejected Vike extension if (!importPathAbsolute) { const p = filePathAbsoluteFilesystem; assert(!p.includes('node_modules')); return; } const name = getNameValue(plusFile); assert(name); // already asserted in assertExtensionName() const importPathAbsoluteExpected = `${name}/config`; assertWarning(importPathAbsolute === importPathAbsoluteExpected, `The Vike configuration of ${pc.bold(name)} is exported at ${pc.bold(importPathAbsolute)}, but it should be exported at ${pc.bold(importPathAbsoluteExpected)} instead.`, { onlyOnce: true }); } function assertExtensionName(plusFile) { const filePathToShowToUser = getFilePathToShowToUser(plusFile); const name = getNameValue(plusFile); assertUsage(name, `Vike extension name missing: the config ${filePathToShowToUser} must define the setting ${pc.cyan('name')}`); } function assertExtensionsRequire(plusFiles) { const plusFilesRelevantList = plusFiles; // Collect extensions const extensions = {}; plusFilesRelevantList.forEach((plusFile) => { const name = getNameValue(plusFile); if (name) { const version = getExtensionVersion(name, plusFile); extensions[name] = version; } }); // Enforce `require` plusFilesRelevantList.forEach((plusFile) => { const require = resolveRequireSetting(plusFile); if (!require) return; const name = getNameValue(plusFile); const filePathToShowToUser = getFilePathToShowToUser(plusFile); assertUsage(name, `Setting ${pc.bold('name')} is required for being able to use setting ${pc.bold('require')} in ${filePathToShowToUser}.`); Object.entries(require).forEach(([reqName, req]) => { const errBase = `${pc.bold(name)} requires ${pc.bold(reqName)}`; if (reqName === 'vike') { let errMsg = `${errBase} version ${pc.bold(req.version)}, but ${pc.bold(PROJECT_VERSION)} is installed.`; if (req.optional) { errMsg += " Either update it, or remove it (it's an optional peer dependency)."; } assertUsage(isVersionRange(PROJECT_VERSION, req.version), errMsg); return; } const extensionVersion = extensions[reqName]; if (!extensionVersion) { if (req.optional) { return; } else { assertUsage(false, `${errBase}.`); } } assertUsage(isVersionRange(extensionVersion, req.version), `${errBase} version ${pc.bold(req.version)}, but ${pc.bold(extensionVersion)} is installed.`); }); }); } function resolveRequireSetting(plusFile) { const confVal = getConfVal(plusFile, 'require'); if (!confVal) return null; assert(confVal.valueIsLoaded); const requireValue = confVal.value; const { filePathToShowToUserResolved } = plusFile.filePath; assert(filePathToShowToUserResolved); assertUsage(isObject(requireValue), `The setting ${pc.bold('+require')} defined at ${filePathToShowToUserResolved} should be an object`); const requireSetting = {}; Object.entries(requireValue).forEach(([reqName, req]) => { if (typeof req === 'string') { requireSetting[reqName] = { version: req, optional: false }; return; } if (isObject(req)) { requireSetting[reqName] = req; return; } assertUsage(false, `Invalid +require[${JSON.stringify(reqName)}] value ${pc.cyan(JSON.stringify(req))}`); }); return requireSetting; } function getNameValue(plusFile) { const confVal = getConfVal(plusFile, 'name'); if (!confVal) return null; assert(confVal.valueIsLoaded); const name = confVal.value; const filePathToShowToUser = getFilePathToShowToUser(plusFile); assertUsage(typeof name === 'string', `The setting ${pc.bold('name')} defined at ${filePathToShowToUser} should be a string.`); return name; } // We use a forever cache: users need to restart the dev server anyways when touching node_modules/**/* (I presume Vite doesn't pick up node_modules/**/* changes). const extensionsVersion = {}; function getExtensionVersion(name, plusFile) { if (!extensionsVersion[name]) { const extensionConfigFilePath = plusFile.filePath.filePathAbsoluteFilesystem; const found = findPackageJson(path.posix.dirname(extensionConfigFilePath)); assert(found); const { packageJson, packageJsonPath } = found; const filePathToShowToUser = getFilePathToShowToUser(plusFile); const nameExpected = packageJson.name; assertWarning(name === nameExpected, `The setting ${pc.bold('name')} defined at ${filePathToShowToUser} is ${pc.bold(JSON.stringify(name))}, but it should be equal to ${pc.bold(JSON.stringify(nameExpected))} (the value of ${packageJsonPath}${pc.dim('#')}${pc.bold('name')})`, { onlyOnce: true }); const { version } = packageJson; assert(typeof version === 'string'); extensionsVersion[name] = version; } return extensionsVersion[name]; } function getFilePathToShowToUser(plusFile) { const { filePathToShowToUserResolved } = plusFile.filePath; assert(filePathToShowToUserResolved); return filePathToShowToUserResolved; } function isVersionRange(version, range) { // Remove pre-release tag version = version.split('-')[0]; return semver.satisfies(version, range); }