diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c914b91750b..1c943a2edd21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [unreleased] +- [#1865](/~https://github.com/teambit/bit/issues/1865) allow adding `package.json` props via `overrides` - [#1837](/~https://github.com/teambit/bit/issues/1837) enable executing commands on remote components outside of bit-workspace - [#1774](/~https://github.com/teambit/bit/issues/1774) improve access errors and warn when sudo is used diff --git a/e2e/functionalities/workspace-config.e2e.3.js b/e2e/functionalities/workspace-config.e2e.3.js index e9874867a7ab..b50f91f1c16f 100644 --- a/e2e/functionalities/workspace-config.e2e.3.js +++ b/e2e/functionalities/workspace-config.e2e.3.js @@ -1297,13 +1297,162 @@ describe('workspace config', function () { expect(showBar.compiler).to.be.null; }); }); + describe('override package.json values', () => { + before(() => { + helper.setNewLocalAndRemoteScopes(); + helper.createComponentBarFoo(); + helper.addComponentBarFoo(); + const overrides = { + 'bar/*': { + bin: 'my-bin-file.js' + } + }; + helper.addOverridesToBitJson(overrides); + }); + it('bit show should show the overrides', () => { + const show = helper.showComponentParsed('bar/foo'); + expect(show.overrides) + .to.have.property('bin') + .equal('my-bin-file.js'); + }); + describe('tag, export and import the component', () => { + let authorScope; + before(() => { + helper.tagAllComponents(); + helper.exportAllComponents(); + authorScope = helper.cloneLocalScope(); + helper.reInitLocalScope(); + helper.addRemoteScope(); + helper.importComponent('bar/foo'); + }); + it('should write the values into package.json file', () => { + const packageJson = helper.readPackageJson(path.join(helper.localScopePath, 'components/bar/foo')); + expect(packageJson) + .to.have.property('bin') + .that.equals('my-bin-file.js'); + }); + it('should not show the component as modified', () => { + const status = helper.status(); + expect(status).to.have.string(statusWorkspaceIsCleanMsg); + }); + describe('changing the value in the package.json directly (not inside overrides)', () => { + before(() => { + const compDir = path.join(helper.localScopePath, 'components/bar/foo'); + const packageJson = helper.readPackageJson(compDir); + packageJson.bin = 'my-new-file.js'; + helper.writePackageJson(packageJson, compDir); + }); + it('should not show the component as modified', () => { + const status = helper.status(); + expect(status).to.not.have.string('modified components'); + }); + }); + describe('changing the value in the package.json inside overrides', () => { + before(() => { + const compDir = path.join(helper.localScopePath, 'components/bar/foo'); + const packageJson = helper.readPackageJson(compDir); + packageJson.bit.overrides.bin = 'my-new-file.js'; + helper.writePackageJson(packageJson, compDir); + }); + it('should show the component as modified', () => { + const status = helper.status(); + expect(status).to.have.string('modified components'); + }); + it('bit diff should show the field diff', () => { + const diff = helper.diff('bar/foo'); + expect(diff).to.have.string('my-bin-file.js'); + expect(diff).to.have.string('my-new-file.js'); + }); + describe('tagging, exporting and re-importing as author', () => { + before(() => { + helper.tagAllComponents(); + helper.exportAllComponents(); + helper.getClonedLocalScope(authorScope); + helper.importComponent('bar/foo'); + }); + it('should not show the component as modified', () => { + const status = helper.status(); + expect(status).to.not.have.string('modified components'); + }); + it('author bit.json should be rewritten to include a rule of the specific component', () => { + const bitJson = helper.readBitJson(); + expect(bitJson.overrides) + .to.have.property(`${helper.remoteScope}/bar/foo`) + .that.deep.equals({ bin: 'my-new-file.js' }); + }); + it('bit show should display the modified field and not the original one', () => { + const show = helper.showComponentParsed('bar/foo'); + expect(show.overrides) + .to.have.property('bin') + .that.equals('my-new-file.js'); + }); + }); + }); + }); + }); + describe('propagating from a specific rule to a more general rule when propagate field is true', () => { + let show; + before(() => { + helper.setNewLocalAndRemoteScopes(); + helper.createComponentBarFoo(); + helper.addComponentBarFoo(); + helper.addComponentBarFoo(); + const overrides = { + '*': { + scripts: { + build: 'babel build' + } + }, + 'bar/*': { + bin: 'my-bin-file.js', + scripts: { + test: 'mocha test', + lint: 'eslint lint' + }, + propagate: true + }, + 'bar/foo': { + scripts: { + test: 'jest test', + watch: 'babel watch' + }, + propagate: true + } + }; + helper.addOverridesToBitJson(overrides); + show = helper.showComponentParsed(); + }); + it('should not save the "propagate" field', () => { + expect(show.overrides).to.not.have.property('propagate'); + }); + it('should propagate to a more general rule and save string values that are not in the specific rule', () => { + expect(show.overrides) + .to.have.property('bin') + .that.equals('my-bin-file.js'); + }); + it('should propagate to a more general rule and merge objects that are in the specific rule', () => { + expect(show.overrides).to.have.property('scripts'); + expect(show.overrides.scripts) + .to.have.property('build') + .that.equals('babel build'); + expect(show.overrides.scripts) + .to.have.property('lint') + .that.equals('eslint lint'); + expect(show.overrides.scripts) + .to.have.property('watch') + .that.equals('babel watch'); + }); + it('should let the more specific rule wins when it contradict a more general rule', () => { + expect(show.overrides.scripts).to.have.property('test'); + expect(show.overrides.scripts.test).to.equals('jest test'); + expect(show.overrides.scripts.test).not.to.equals('mocha test'); + }); + }); }); describe('basic validations', () => { - before(() => { - helper.reInitLocalScope(); - }); describe('when overrides is not an object', () => { before(() => { + helper.reInitLocalScope(); const overrides = ['dependencies']; helper.addOverridesToBitJson(overrides); }); @@ -1314,6 +1463,7 @@ describe('workspace config', function () { }); describe('when overrides of a component is not an object', () => { before(() => { + helper.reInitLocalScope(); const overrides = { bar: 1234 }; @@ -1324,22 +1474,43 @@ describe('workspace config', function () { expect(output).to.have.string('expected overrides.bar to be object, got number'); }); }); - describe('when an unrecognized field is added into overrides of a component', () => { + describe('when a forbidden field is added into overrides of a component', () => { before(() => { + helper.reInitLocalScope(); const overrides = { bar: { - some_field: {} + name: 'foo' // the name field of package.json is not permitted to change } }; helper.addOverridesToBitJson(overrides); }); it('any bit command should throw an error', () => { const output = helper.runWithTryCatch('bit list'); - expect(output).to.have.string('found an unrecognized field "some_field" inside "overrides.bar" property'); + expect(output).to.have.string('found a forbidden field "name" inside "overrides.bar" property'); + }); + }); + describe('when a non-compliant package.json field is added into overrides of a component', () => { + before(() => { + helper.reInitLocalScope(); + helper.createComponentBarFoo(); + helper.addComponentBarFoo(); + const overrides = { + 'bar/*': { + private: 'foo' // according to npm specs it should be boolean + } + }; + helper.addOverridesToBitJson(overrides); + }); + it('bit tag should throw an error', () => { + const output = helper.runWithTryCatch('bit tag -a'); + expect(output).to.have.string( + 'unable to save Version object, "overrides.private" is a package.json field but is not compliant with npm requirements. Type for field private, was expected to be boolean, not string' + ); }); }); describe('when a dependency field is not an object', () => { before(() => { + helper.reInitLocalScope(); const overrides = { bar: { dependencies: 1234 @@ -1354,6 +1525,7 @@ describe('workspace config', function () { }); describe('when a dependency rule is not a string', () => { before(() => { + helper.reInitLocalScope(); const overrides = { foo: { dependencies: { diff --git a/package-lock.json b/package-lock.json index 41977c904656..fe402e80c6a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bit-bin", - "version": "14.2.1", + "version": "14.2.4-dev.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -10648,7 +10648,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, "requires": { "minimist": "~0.0.1", "wordwrap": "~0.0.2" @@ -10657,14 +10656,12 @@ "minimist": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" }, "wordwrap": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" } } }, @@ -10838,6 +10835,14 @@ "semver": "^5.1.0" } }, + "package-json-validator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/package-json-validator/-/package-json-validator-0.6.3.tgz", + "integrity": "sha512-juKiFboV4UKUvWQ+OSxstnyukhuluyuEoFmgZw1Rx21XzmwlgDWLcbl3qzjA3789IRORYhVFs7cmAO0YFGwHCg==", + "requires": { + "optimist": "~0.6.0" + } + }, "pad-right": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/pad-right/-/pad-right-0.2.2.tgz", diff --git a/package.json b/package.json index a0666b9f6a01..67a60fedafed 100644 --- a/package.json +++ b/package.json @@ -109,6 +109,7 @@ "ora": "^1.1.0", "p-event": "^4.1.0", "p-map-series": "^1.0.0", + "package-json-validator": "^0.6.3", "pad-right": "^0.2.2", "parse-gitignore": "^1.0.1", "porter-stemmer": "^0.9.1", diff --git a/src/consumer/component-ops/component-writer.js b/src/consumer/component-ops/component-writer.js index 13ad709733ed..33445edf98a4 100644 --- a/src/consumer/component-ops/component-writer.js +++ b/src/consumer/component-ops/component-writer.js @@ -177,6 +177,7 @@ export default class ComponentWriter { componentConfig.tester = this.component.tester ? this.component.tester.toBitJsonObject('.') : {}; packageJson.addOrUpdateProperty('bit', componentConfig.toPlainObject()); this._mergeChangedPackageJsonProps(packageJson); + this._mergePackageJsonPropsFromOverrides(packageJson); await this._populateEnvFilesIfNeeded(); this.component.dataToPersist.addFile(packageJson.toVinylFile()); if (distPackageJson) this.component.dataToPersist.addFile(distPackageJson.toVinylFile()); @@ -252,6 +253,17 @@ export default class ComponentWriter { } } + /** + * these changes were entered manually by a user via `overrides` key + */ + _mergePackageJsonPropsFromOverrides(packageJson: PackageJsonFile) { + const valuesToMerge = this.component.overrides.componentOverridesPackageJsonData; + packageJson.mergePackageJsonObject(valuesToMerge); + } + + /** + * these are changes done by a compiler + */ _mergeChangedPackageJsonProps(packageJson: PackageJsonFile) { if (!this.component.packageJsonChangedProps) return; const valuesToMerge = this._replaceDistPathTemplateWithCalculatedDistPath(packageJson); diff --git a/src/consumer/component-ops/components-object-diff.js b/src/consumer/component-ops/components-object-diff.js index 5b92979b8710..7e816a13c3f7 100644 --- a/src/consumer/component-ops/components-object-diff.js +++ b/src/consumer/component-ops/components-object-diff.js @@ -85,6 +85,7 @@ export function componentToPrintableForDiff(component: Component): Object { obj.overridesDependencies = parsePackages(overrides.dependencies); obj.overridesDevDependencies = parsePackages(overrides.devDependencies); obj.overridesPeerDependencies = parsePackages(overrides.peerDependencies); + obj.overridesPackageJsonProps = JSON.stringify(component.overrides.componentOverridesPackageJsonData); return obj; } diff --git a/src/consumer/config/component-config.js b/src/consumer/config/component-config.js index d5f53ac18ba6..2fc87f95706b 100644 --- a/src/consumer/config/component-config.js +++ b/src/consumer/config/component-config.js @@ -94,7 +94,9 @@ export default class ComponentConfig extends AbstractConfig { */ static mergeWithWorkspaceConfig(componentConfig: Object, consumerConfig: ?WorkspaceConfig): ComponentConfig { const plainConsumerConfig = consumerConfig ? consumerConfig.toPlainObject() : {}; - return ComponentConfig.fromPlainObject(R.merge(plainConsumerConfig, componentConfig)); + const consumerConfigWithoutConsumerSpecifics = filterObject(plainConsumerConfig, (val, key) => key !== 'overrides'); + const mergedObject = R.merge(consumerConfigWithoutConsumerSpecifics, componentConfig); + return ComponentConfig.fromPlainObject(mergedObject); } /** diff --git a/src/consumer/config/component-overrides.js b/src/consumer/config/component-overrides.js index 8ab91caaf6b0..f72e0e31694b 100644 --- a/src/consumer/config/component-overrides.js +++ b/src/consumer/config/component-overrides.js @@ -8,7 +8,7 @@ import { OVERRIDE_COMPONENT_PREFIX } from '../../constants'; import type { ConsumerOverridesOfComponent } from './consumer-overrides'; -import { dependenciesFields } from './consumer-overrides'; +import { dependenciesFields, overridesSystemFields, nonPackageJsonFields } from './consumer-overrides'; export type ComponentOverridesData = { dependencies?: Object, @@ -59,14 +59,13 @@ export default class ComponentOverrides { // $FlowFixMe return new ComponentOverrides(R.clone(overridesFromModel), {}); } - - static componentOverridesDataFields() { - return dependenciesFields; - } get componentOverridesData() { - const fields = ComponentOverrides.componentOverridesDataFields(); - const isDependencyField = (val, field) => fields.includes(field); - return R.pickBy(isDependencyField, this.overrides); + const isNotSystemField = (val, field) => !overridesSystemFields.includes(field); + return R.pickBy(isNotSystemField, this.overrides); + } + get componentOverridesPackageJsonData() { + const isPackageJsonField = (val, field) => !nonPackageJsonFields.includes(field); + return R.pickBy(isPackageJsonField, this.overrides); } getComponentDependenciesWithVersion(): Object { const allDeps = Object.assign( diff --git a/src/consumer/config/consumer-overrides.js b/src/consumer/config/consumer-overrides.js index e6534ffb645a..04a664c9f96a 100644 --- a/src/consumer/config/consumer-overrides.js +++ b/src/consumer/config/consumer-overrides.js @@ -19,7 +19,9 @@ export type ConsumerOverridesOfComponent = { export type ConsumerOverridesConfig = { [string]: ConsumerOverridesOfComponent }; export const dependenciesFields = ['dependencies', 'devDependencies', 'peerDependencies']; -const consumerOverridesPermittedFields = [...dependenciesFields, 'env']; +export const overridesForbiddenFields = ['name', 'main', 'version', 'bit']; +export const overridesSystemFields = ['propagate', 'env']; +export const nonPackageJsonFields = [...dependenciesFields, ...overridesSystemFields]; export default class ConsumerOverrides { overrides: ConsumerOverridesConfig; @@ -31,15 +33,10 @@ export default class ConsumerOverrides { return new ConsumerOverrides(overrides); } getOverrideComponentData(bitId: BitId): ?ConsumerOverridesOfComponent { - const getMatches = (): string[] => { - const exactMatch = this.findExactMatch(bitId); - const matchByGlobPattern = Object.keys(this.overrides).filter(idStr => this.isMatchByWildcard(bitId, idStr)); - const allMatches = matchByGlobPattern.sort(ConsumerOverrides.sortWildcards); - if (exactMatch) allMatches.unshift(exactMatch); - return allMatches; - }; - const matches = getMatches(); - if (!matches.length) return null; + const matches = this._getAllRulesMatchedById(bitId); + if (!matches.length) { + return null; + } const overrideValues = matches.map(match => this.overrides[match]); let stopPropagation = false; return overrideValues.reduce((acc, current) => { @@ -47,26 +44,42 @@ export default class ConsumerOverrides { if (!current.propagate) { stopPropagation = true; } - consumerOverridesPermittedFields.forEach((field) => { - if (!current[field]) return; - if (!acc[field]) acc[field] = {}; - if (field === 'env') { - ['compiler', 'tester'].forEach((envField) => { - // $FlowFixMe we made sure before that current.env is set - if (acc.env[envField] || !current.env[envField]) return; - acc.env[envField] = current.env[envField]; - }); - } else if (dependenciesFields.includes(field)) { - // $FlowFixMe - acc[field] = Object.assign(current[field], acc[field]); - } else { - throw new Error(`consumer-overrides, ${field} does not have a merge strategy`); - } - }); + this._updateSpecificOverridesWithGeneralOverrides(current, acc); return acc; }, {}); } - isMatchByWildcard(bitId: BitId, idWithPossibleWildcard: string): boolean { + _updateSpecificOverridesWithGeneralOverrides(generalOverrides: Object, specificOverrides: Object) { + const isObjectAndNotArray = val => typeof val === 'object' && !Array.isArray(val); + Object.keys(generalOverrides).forEach((field) => { + switch (field) { + case 'env': + if (!specificOverrides[field]) specificOverrides[field] = {}; + ['compiler', 'tester'].forEach((envField) => { + if (specificOverrides.env[envField] || !generalOverrides.env[envField]) return; + specificOverrides.env[envField] = generalOverrides.env[envField]; + }); + break; + case 'propagate': + // it's a system field, do nothing + break; + default: + if (isObjectAndNotArray(specificOverrides[field]) && isObjectAndNotArray(generalOverrides[field])) { + specificOverrides[field] = Object.assign(generalOverrides[field], specificOverrides[field]); + } else if (!specificOverrides[field]) { + specificOverrides[field] = generalOverrides[field]; + } + // when specificOverrides[field] is set and not an object, do not override it by the general one + } + }); + } + _getAllRulesMatchedById(bitId: BitId): string[] { + const exactMatch = this.findExactMatch(bitId); + const matchByGlobPattern = Object.keys(this.overrides).filter(idStr => this._isMatchByWildcard(bitId, idStr)); + const allMatches = matchByGlobPattern.sort(ConsumerOverrides.sortWildcards); + if (exactMatch) allMatches.unshift(exactMatch); + return allMatches; + } + _isMatchByWildcard(bitId: BitId, idWithPossibleWildcard: string): boolean { if (!hasWildcard(idWithPossibleWildcard)) return false; return isBitIdMatchByWildcards(bitId, idWithPossibleWildcard); } @@ -154,9 +167,9 @@ export default class ConsumerOverrides { function validateComponentOverride(id, override) { validateUserInputType(message, override, `overrides.${id}`, 'object'); Object.keys(override).forEach((field) => { - if (!consumerOverridesPermittedFields.includes(field)) { - throw new GeneralError(`${message} found an unrecognized field "${field}" inside "overrides.${id}" property. -only the following fields are allowed: ${consumerOverridesPermittedFields.join(', ')}.`); + if (overridesForbiddenFields.includes(field)) { + throw new GeneralError(`${message} found a forbidden field "${field}" inside "overrides.${id}" property. +the following fields are not allowed: ${overridesForbiddenFields.join(', ')}.`); } if (dependenciesFields.includes(field)) { validateDependencyField(field, override, id); diff --git a/src/scope/models/version.spec.js b/src/scope/models/version.spec.js index da1040d81d5b..c33c54c11c11 100644 --- a/src/scope/models/version.spec.js +++ b/src/scope/models/version.spec.js @@ -307,5 +307,27 @@ describe('Version', () => { version.packageJsonChangedProps = [1, 2, 3, 4]; expect(validateFunc).to.throw('expected packageJsonChangedProps to be object, got array'); }); + it('should throw when packageJsonChangedProps has a non-compliant npm value', () => { + version.packageJsonChangedProps = { bin: 1234 }; + expect(validateFunc).to.throw('the generated package.json field "bin" is not compliant with npm requirements'); + }); + it('should not throw when packageJsonChangedProps has a compliant npm value', () => { + version.packageJsonChangedProps = { bin: 'my-file.js' }; + expect(validateFunc).to.not.throw(); + }); + it('should throw when overrides has a package.json field that is non-compliant npm value', () => { + version.overrides = { bin: 1234 }; + expect(validateFunc).to.throw( + '"overrides.bin" is a package.json field but is not compliant with npm requirements' + ); + }); + it('should not throw when overrides has a package.json field that is compliant npm value', () => { + version.overrides = { bin: 'my-file.js' }; + expect(validateFunc).to.not.throw(); + }); + it('should show the original error from package-json-validator when overrides has a package.json field that is non-compliant npm value', () => { + version.overrides = { scripts: false }; + expect(validateFunc).to.throw('Type for field scripts, was expected to be object, not boolean'); + }); }); }); diff --git a/src/scope/version-validator.js b/src/scope/version-validator.js index baa52e5a30a2..df8f18ccf242 100644 --- a/src/scope/version-validator.js +++ b/src/scope/version-validator.js @@ -1,5 +1,6 @@ // @flow import R from 'ramda'; +import { PJV } from 'package-json-validator'; import packageNameValidate from 'validate-npm-package-name'; import validateType from '../utils/validate-type'; import { BitId, BitIds } from '../bit-id'; @@ -7,8 +8,12 @@ import VersionInvalid from './exceptions/version-invalid'; import { isValidPath } from '../utils'; import type Version from './models/version'; import { Dependencies } from '../consumer/component/dependencies'; -import ComponentOverrides from '../consumer/config/component-overrides'; import PackageJsonFile from '../consumer/component/package-json-file'; +import { + overridesForbiddenFields, + dependenciesFields, + nonPackageJsonFields +} from '../consumer/config/consumer-overrides'; /** * make sure a Version instance is correct. throw an exceptions if it is not. @@ -174,18 +179,36 @@ export default function validateVersionInstance(version: Version): void { if (version.bindingPrefix) { validateType(message, version.bindingPrefix, 'bindingPrefix', 'string'); } - const overridesAllowedKeys = ComponentOverrides.componentOverridesDataFields(); - const validateOverrides = (dependencies: Object, fieldName) => { + const npmSpecs = PJV.getSpecMap('npm'); + const validatePackageJsonField = (fieldName: string, fieldValue: any): ?string => { + if (!npmSpecs[fieldName]) { + // it's not a standard package.json field, can't validate + return null; + } + const validateResult = PJV.validateType(fieldName, npmSpecs[fieldName], fieldValue); + if (!validateResult.length) return null; + return validateResult.join(', '); + }; + const validateOverrides = (fieldValue: Object, fieldName: string) => { const field = `overrides.${fieldName}`; - validateType(message, dependencies, field, 'object'); - Object.keys(dependencies).forEach((key) => { - validateType(message, key, `property name of ${field}`, 'string'); - validateType(message, dependencies[key], `version of "${field}.${key}"`, 'string'); - }); + if (dependenciesFields.includes(fieldName)) { + validateType(message, fieldValue, field, 'object'); + Object.keys(fieldValue).forEach((key) => { + validateType(message, key, `property name of ${field}`, 'string'); + validateType(message, fieldValue[key], `version of "${field}.${key}"`, 'string'); + }); + } else if (!nonPackageJsonFields.includes(fieldName)) { + const result = validatePackageJsonField(fieldName, fieldValue); + if (result) { + throw new VersionInvalid( + `${message}, "${field}" is a package.json field but is not compliant with npm requirements. ${result}` + ); + } + } }; Object.keys(version.overrides).forEach((field) => { - if (!overridesAllowedKeys.includes(field)) { - throw new VersionInvalid(`${message}, the "overrides" has unidentified key "${field}"`); + if (overridesForbiddenFields.includes(field)) { + throw new VersionInvalid(`${message}, the "overrides" has a forbidden key "${field}"`); } // $FlowFixMe validateOverrides(version.overrides[field], field); @@ -197,5 +220,11 @@ export default function validateVersionInstance(version: Version): void { if (forbiddenPackageJsonProps.includes(prop)) { throw new VersionInvalid(`${message}, the packageJsonChangedProps should not override the prop ${prop}`); } + const result = validatePackageJsonField(prop, version.packageJsonChangedProps[prop]); + if (result) { + throw new VersionInvalid( + `${message}, the generated package.json field "${prop}" is not compliant with npm requirements. ${result}` + ); + } }); }