bit-bin
Version:
<a href="https://opensource.org/licenses/Apache-2.0"><img alt="apache" src="https://img.shields.io/badge/License-Apache%202.0-blue.svg"></a> <a href="https://github.com/teambit/bit/blob/master/CONTRIBUTING.md"><img alt="prs" src="https://img.shields.io/b
442 lines (328 loc) • 15.2 kB
JavaScript
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = validateVersionInstance;
function _ramda() {
const data = _interopRequireDefault(require("ramda"));
_ramda = function () {
return data;
};
return data;
}
function _packageJsonValidator() {
const data = require("package-json-validator");
_packageJsonValidator = function () {
return data;
};
return data;
}
function _validateNpmPackageName() {
const data = _interopRequireDefault(require("validate-npm-package-name"));
_validateNpmPackageName = function () {
return data;
};
return data;
}
function _validateType() {
const data = _interopRequireDefault(require("../utils/validate-type"));
_validateType = function () {
return data;
};
return data;
}
function _bitId() {
const data = require("../bit-id");
_bitId = function () {
return data;
};
return data;
}
function _versionInvalid() {
const data = _interopRequireDefault(require("./exceptions/version-invalid"));
_versionInvalid = function () {
return data;
};
return data;
}
function _utils() {
const data = require("../utils");
_utils = function () {
return data;
};
return data;
}
function _dependencies() {
const data = require("../consumer/component/dependencies");
_dependencies = function () {
return data;
};
return data;
}
function _packageJsonFile() {
const data = _interopRequireDefault(require("../consumer/component/package-json-file"));
_packageJsonFile = function () {
return data;
};
return data;
}
function _consumerOverrides() {
const data = require("../consumer/config/consumer-overrides");
_consumerOverrides = function () {
return data;
};
return data;
}
function _componentOverrides() {
const data = require("../consumer/config/component-overrides");
_componentOverrides = function () {
return data;
};
return data;
}
function _dependencies2() {
const data = require("../consumer/component/dependencies/dependencies");
_dependencies2 = function () {
return data;
};
return data;
}
function _constants() {
const data = require("../constants");
_constants = function () {
return data;
};
return data;
}
function _generalError() {
const data = _interopRequireDefault(require("../error/general-error"));
_generalError = function () {
return data;
};
return data;
}
/**
* make sure a Version instance is correct. throw an exceptions if it is not.
*/
function validateVersionInstance(version) {
const message = 'unable to save Version object';
const validateBitId = (bitId, field, validateVersion = true, validateScope = true) => {
if (validateVersion && !bitId.hasVersion()) {
throw new (_versionInvalid().default)(`${message}, the ${field} ${bitId.toString()} does not have a version`);
}
if (validateScope && !bitId.scope) {
throw new (_versionInvalid().default)(`${message}, the ${field} ${bitId.toString()} does not have a scope`);
}
};
const validateBitIdStr = (bitIdStr, field, validateVersion = true, validateScope = true) => {
(0, _validateType().default)(message, bitIdStr, field, 'string');
let bitId;
try {
bitId = _bitId().BitId.parse(bitIdStr, true);
} catch (err) {
throw new (_versionInvalid().default)(`${message}, the ${field} has an invalid Bit id`);
}
validateBitId(bitId, field, validateVersion, validateScope);
};
const _validateEnv = env => {
if (!env) return;
if (typeof env === 'string') {
// Do not validate version - for backward compatibility
validateBitIdStr(env, 'environment-id', false);
return;
}
(0, _validateType().default)(message, env, 'env', 'object');
if (!env.name) {
throw new (_versionInvalid().default)(`${message}, the environment is missing the name attribute`);
}
validateBitIdStr(env.name, 'env.name');
if (env.files) {
const compilerName = env.name || '';
env.files.forEach(file => {
if (!file.name) {
throw new (_versionInvalid().default)(`${message}, the environment ${compilerName} has a file which missing the name attribute`);
}
});
}
};
/**
* Validate that the package name and version are valid
* @param {*} packageName
* @param {*} packageVersion
*/
const _validatePackageDependency = (packageVersion, packageName) => {
const packageNameValidateResult = (0, _validateNpmPackageName().default)(packageName);
if (!packageNameValidateResult.validForNewPackages && !packageNameValidateResult.validForOldPackages) {
const errors = packageNameValidateResult.errors || [];
throw new (_versionInvalid().default)(`${packageName} is invalid package name, errors: ${errors.join()}`);
} // don't use semver.valid and semver.validRange to validate the package version because it
// can be also a URL, Git URL or Github URL. see here: https://docs.npmjs.com/files/package.json#dependencies
(0, _validateType().default)(message, packageVersion, `version of "${packageName}"`, 'string');
};
const _validatePackageDependencies = packageDependencies => {
(0, _validateType().default)(message, packageDependencies, 'packageDependencies', 'object');
_ramda().default.forEachObjIndexed(_validatePackageDependency, packageDependencies);
};
const _validateEnvPackages = (envPackages, fieldName) => {
(0, _validateType().default)(message, envPackages, fieldName, 'object');
Object.keys(envPackages).forEach(dependencyType => {
if (!_constants().DEPENDENCIES_FIELDS.includes(dependencyType)) {
throw new (_versionInvalid().default)(`${message}, the property ${dependencyType} inside ${fieldName} is invalid, allowed values are ${_constants().DEPENDENCIES_FIELDS.join(', ')}`);
}
(0, _validateType().default)(message, envPackages[dependencyType], `${fieldName}.${dependencyType}`, 'object');
Object.keys(envPackages[dependencyType]).forEach(pkg => {
(0, _validateType().default)(message, envPackages[dependencyType][pkg], `${fieldName}.${dependencyType}.${pkg}`, 'string');
});
});
};
const validateFile = (file, field) => {
(0, _validateType().default)(message, file, field, 'object');
if (!(0, _utils().isValidPath)(file.relativePath)) {
throw new (_versionInvalid().default)(`${message}, the ${field} ${file.relativePath} is invalid`);
}
if (!file.name && field !== 'artifact') {
throw new (_versionInvalid().default)(`${message}, the ${field} ${file.relativePath} is missing the name attribute`);
}
if (!file.file) throw new (_versionInvalid().default)(`${message}, the ${field} ${file.relativePath} is missing the hash`);
if (file.name) (0, _validateType().default)(message, file.name, `${field}.name`, 'string');
(0, _validateType().default)(message, file.file, `${field}.file`, 'object');
(0, _validateType().default)(message, file.file.hash, `${field}.file.hash`, 'string');
};
const _validateExtension = extension => {
if (extension.extensionId) {
validateBitId(extension.extensionId, `extensions.${extension.extensionId.toString()}`, true, false);
}
extension.artifacts.map(artifact => validateFile(artifact, 'artifact'));
};
const _validateExtensions = extensions => {
if (extensions) {
extensions.map(_validateExtension);
}
};
if (!version.mainFile) throw new (_versionInvalid().default)(`${message}, the mainFile is missing`);
if (!(0, _utils().isValidPath)(version.mainFile)) {
throw new (_versionInvalid().default)(`${message}, the mainFile ${version.mainFile} is invalid`);
}
if (!version.files || !version.files.length) throw new (_versionInvalid().default)(`${message}, the files are missing`);
let foundMainFile = false;
(0, _validateType().default)(message, version.files, 'files', 'array');
const filesPaths = [];
version.files.forEach(file => {
validateFile(file, 'file');
filesPaths.push(file.relativePath);
if (file.relativePath === version.mainFile) foundMainFile = true;
});
if (!foundMainFile) {
throw new (_versionInvalid().default)(`${message}, unable to find the mainFile ${version.mainFile} in the following files list: ${filesPaths.join(', ')}`);
}
const duplicateFiles = filesPaths.filter(file => filesPaths.filter(f => file.toLowerCase() === f.toLowerCase()).length > 1);
if (duplicateFiles.length) {
throw new (_versionInvalid().default)(`${message} the following files are duplicated ${duplicateFiles.join(', ')}`);
}
_validateEnv(version.compiler);
_validateEnv(version.tester);
_validatePackageDependencies(version.packageDependencies);
_validatePackageDependencies(version.devPackageDependencies);
_validatePackageDependencies(version.peerPackageDependencies);
_validateEnvPackages(version.compilerPackageDependencies, 'compilerPackageDependencies');
_validateEnvPackages(version.testerPackageDependencies, 'testerPackageDependencies');
_validateExtensions(version.extensions);
if (version.dists && version.dists.length) {
(0, _validateType().default)(message, version.dists, 'dist', 'array');
version.dists.forEach(file => {
validateFile(file, 'dist-file');
});
} else if (version.mainDistFile) {
throw new (_versionInvalid().default)(`${message} the mainDistFile cannot be set when the dists are empty`);
}
if (version.mainDistFile && !(0, _utils().isValidPath)(version.mainDistFile)) {
throw new (_versionInvalid().default)(`${message}, the mainDistFile ${version.mainDistFile} is invalid`);
}
_dependencies2().DEPENDENCIES_TYPES.forEach(dependenciesType => {
if (!(version[dependenciesType] instanceof _dependencies().Dependencies)) {
throw new (_versionInvalid().default)(`${message}, ${dependenciesType} must be an instance of Dependencies, got ${typeof version[dependenciesType]}`);
}
});
version.dependencies.validate();
version.devDependencies.validate();
if (!version.dependencies.isEmpty() && !version.flattenedDependencies.length) {
throw new (_versionInvalid().default)(`${message}, it has dependencies but its flattenedDependencies is empty`);
}
if (!version.devDependencies.isEmpty() && !version.flattenedDevDependencies.length) {
throw new (_versionInvalid().default)(`${message}, it has devDependencies but its flattenedDevDependencies is empty`);
}
const validateFlattenedDependencies = dependencies => {
(0, _validateType().default)(message, dependencies, 'dependencies', 'array');
dependencies.forEach(dependency => {
if (!(dependency instanceof _bitId().BitId)) {
throw new (_versionInvalid().default)(`${message}, a flattenedDependency expected to be BitId, got ${typeof dependency}`);
}
if (!dependency.hasVersion()) {
throw new (_versionInvalid().default)(`${message}, the flattenedDependency ${dependency.toString()} does not have a version`);
}
});
};
validateFlattenedDependencies(version.flattenedDependencies);
validateFlattenedDependencies(version.flattenedDevDependencies); // extensions can be duplicate with other dependencies type. e.g. "test" can have "compile" as a
// dependency and extensionDependency. we can't remove it from extDep, otherwise, the ext won't
// be running
const allDependenciesIds = version.getDependenciesIdsExcludeExtensions();
const depsDuplications = allDependenciesIds.findDuplicationsIgnoreVersion();
if (!_ramda().default.isEmpty(depsDuplications)) {
const duplicationStr = Object.keys(depsDuplications).map(id => `"${id}" shows as the following: ${depsDuplications[id].map(depId => depId.toString()).join(', ')} `).join('\n');
throw new (_generalError().default)(`some dependencies are duplicated, see details below.
if you added a dependency to "overrides" configuration with a plus sign, make sure to add it with a minus sign in the other dependency type
for example, { dependencies: { "bar/foo": "+" }, devDependencies: { "bar/foo": "-" } }
${duplicationStr}`); // todo: once decided how to address duplicate dependencies, remove the line above and uncomment the line below
// throw new VersionInvalid(`${message}, some dependencies are duplicated:\n${duplicationStr}`);
}
if (!version.log) throw new (_versionInvalid().default)(`${message}, the log object is missing`);
(0, _validateType().default)(message, version.log, 'log', 'object');
if (version.bindingPrefix) {
(0, _validateType().default)(message, version.bindingPrefix, 'bindingPrefix', 'string');
}
const npmSpecs = _packageJsonValidator().PJV.getSpecMap('npm');
const validatePackageJsonField = (fieldName, fieldValue) => {
if (!npmSpecs[fieldName]) {
// it's not a standard package.json field, can't validate
return null;
}
const validateResult = _packageJsonValidator().PJV.validateType(fieldName, npmSpecs[fieldName], fieldValue);
if (!validateResult.length) return null;
return validateResult.join(', ');
};
const validateOverrides = (fieldValue, fieldName) => {
const field = `overrides.${fieldName}`;
if (_constants().DEPENDENCIES_FIELDS.includes(fieldName)) {
(0, _validateType().default)(message, fieldValue, field, 'object');
Object.keys(fieldValue).forEach(key => {
(0, _validateType().default)(message, key, `property name of ${field}`, 'string');
(0, _validateType().default)(message, fieldValue[key], `version of "${field}.${key}"`, 'string');
});
} else if (!_consumerOverrides().nonPackageJsonFields.includes(fieldName)) {
const result = validatePackageJsonField(fieldName, fieldValue);
if (result) {
throw new (_versionInvalid().default)(`${message}, "${field}" is a package.json field but is not compliant with npm requirements. ${result}`);
}
}
};
Object.keys(version.overrides).forEach(field => {
if (_componentOverrides().componentOverridesForbiddenFields.includes(field)) {
throw new (_versionInvalid().default)(`${message}, the "overrides" has a forbidden key "${field}"`);
}
validateOverrides(version.overrides[field], field);
});
(0, _validateType().default)(message, version.packageJsonChangedProps, 'packageJsonChangedProps', 'object');
const forbiddenPackageJsonProps = _packageJsonFile().default.propsNonUserChangeable();
Object.keys(version.packageJsonChangedProps).forEach(prop => {
(0, _validateType().default)(message, prop, 'property name of packageJson', 'string');
if (forbiddenPackageJsonProps.includes(prop)) {
throw new (_versionInvalid().default)(`${message}, the packageJsonChangedProps should not override the prop ${prop}`);
}
const result = validatePackageJsonField(prop, version.packageJsonChangedProps[prop]);
if (result) {
throw new (_versionInvalid().default)(`${message}, the generated package.json field "${prop}" is not compliant with npm requirements. ${result}`);
}
});
}
;