vike
Version:
The Framework *You* Control - Next.js & Nuxt alternative for unprecedented flexibility and dependability.
110 lines (109 loc) • 5.49 kB
JavaScript
export { assertExtensionsConventions };
export { assertExtensionsRequire };
import pc from '@brillout/picocolors';
import { isObjectOfStrings } from '../../../../../../utils/isObjectOfStrings.js';
import { PROJECT_VERSION, assert, assertUsage, assertWarning, findPackageJson } from '../../../../utils.js';
import { getConfVal } from '../getVikeConfig.js';
import path from '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(pageConfig) {
const plusFilesRelevantList = pageConfig.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 = getConfigRequireValue(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, reqVersion]) => {
const errBase = `${pc.bold(name)} requires ${pc.bold(reqName)}`;
if (reqName === 'vike') {
assertUsage(isVersionRange(PROJECT_VERSION, reqVersion), `${errBase} version ${pc.bold(reqVersion)}, but ${pc.bold(PROJECT_VERSION)} is installed.`);
return;
}
const extensionVersion = extensions[reqName];
assertUsage(extensionVersion, `${errBase}.`);
assertUsage(isVersionRange(extensionVersion, reqVersion), `${errBase} version ${pc.bold(reqVersion)}, but ${pc.bold(extensionVersion)} is installed.`);
});
});
}
function getConfigRequireValue(plusFile) {
const confVal = getConfVal(plusFile, 'require');
if (!confVal)
return null;
assert(confVal.valueIsLoaded);
const require = confVal.value;
const { filePathToShowToUserResolved } = plusFile.filePath;
assert(filePathToShowToUserResolved);
assertUsage(isObjectOfStrings(require), `The setting ${pc.bold('require')} defined at ${filePathToShowToUserResolved} should be an object with string values (${pc.bold('Record<string, string>')}).`);
return require;
}
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);
}