From 577bbb0e4c50041f7c43d9035571be4a6a223418 Mon Sep 17 00:00:00 2001 From: test Date: Fri, 27 Apr 2018 11:08:39 -0500 Subject: [PATCH] feat(plugin-api): allow non-semver versioned dependencies (#1177) npm supports multiple "version" strings when declaring deps in your `package.json`. This PR is an attempt to support some of these other formats in a plugin specification, while retaining the smart version merging. If a semver range can be extracted, it will be used for version conflict resolution. If not, a warning will be displayed to the developer --- packages/@vue/cli/__tests__/Generator.spec.js | 107 ++++++++++++++++++ packages/@vue/cli/lib/util/mergeDeps.js | 17 ++- 2 files changed, 120 insertions(+), 4 deletions(-) diff --git a/packages/@vue/cli/__tests__/Generator.spec.js b/packages/@vue/cli/__tests__/Generator.spec.js index 9a8ef9f4c9..12f14b8f1c 100644 --- a/packages/@vue/cli/__tests__/Generator.spec.js +++ b/packages/@vue/cli/__tests__/Generator.spec.js @@ -114,6 +114,79 @@ test('api: extendPackage function', async () => { }) }) +test('api: extendPackage allow git, github, http, file version ranges', async () => { + const generator = new Generator('/', { plugins: [ + { + id: 'test', + apply: api => { + api.extendPackage({ + dependencies: { + foo: 'git+ssh://git@github.com:npm/npm.git#v1.0.27', + baz: 'git://github.com/npm/npm.git#v1.0.27', + bar: 'expressjs/express', + bad: 'mochajs/mocha#4727d357ea', + bac: 'http://asdf.com/asdf.tar.gz', + bae: 'file:../dyl', + 'my-lib': 'https://bitbucket.org/user/my-lib.git#semver:^1.0.0' + } + }) + } + } + ] }) + + await generator.generate() + + const pkg = JSON.parse(fs.readFileSync('/package.json', 'utf-8')) + expect(pkg).toEqual({ + dependencies: { + foo: 'git+ssh://git@github.com:npm/npm.git#v1.0.27', + baz: 'git://github.com/npm/npm.git#v1.0.27', + bar: 'expressjs/express', + bad: 'mochajs/mocha#4727d357ea', + bac: 'http://asdf.com/asdf.tar.gz', + bae: 'file:../dyl', + 'my-lib': 'https://bitbucket.org/user/my-lib.git#semver:^1.0.0' + } + }) +}) + +test('api: extendPackage merge nonstrictly semver deps', async () => { + const generator = new Generator('/', { plugins: [ + { + id: 'test', + apply: api => { + api.extendPackage({ + dependencies: { + 'my-lib': 'https://bitbucket.org/user/my-lib.git#semver:1.0.0', + bar: 'expressjs/express' + } + }) + } + }, + { + id: 'test2', + apply: api => { + api.extendPackage({ + dependencies: { + 'my-lib': 'https://bitbucket.org/user/my-lib.git#semver:1.2.0', + bar: 'expressjs/express' + } + }) + } + } + ] }) + + await generator.generate() + + const pkg = JSON.parse(fs.readFileSync('/package.json', 'utf-8')) + expect(pkg).toEqual({ + dependencies: { + 'my-lib': 'https://bitbucket.org/user/my-lib.git#semver:1.2.0', + bar: 'expressjs/express' + } + }) +}) + test('api: extendPackage merge dependencies', async () => { const generator = new Generator('/', { plugins: [ { @@ -208,6 +281,40 @@ test('api: extendPackage dependencies conflict', async () => { })).toBe(true) }) +test('api: extendPackage merge warn nonstrictly semver deps', async () => { + new Generator('/', { plugins: [ + { + id: 'test3', + apply: api => { + api.extendPackage({ + dependencies: { + bar: 'expressjs/express' + } + }) + } + }, + { + id: 'test4', + apply: api => { + api.extendPackage({ + dependencies: { + bar: 'expressjs/express#1234' + } + }) + } + } + ] }) + + expect(logs.warn.some(([msg]) => { + return ( + msg.match(/conflicting versions for project dependency "bar"/) && + msg.match(/expressjs\/express injected by generator "test3"/) && + msg.match(/expressjs\/express#1234 injected by generator "test4"/) && + msg.match(/Using version \(expressjs\/express\)/) + ) + })).toBe(true) +}) + test('api: render fs directory', async () => { const generator = new Generator('/', { plugins: [ { diff --git a/packages/@vue/cli/lib/util/mergeDeps.js b/packages/@vue/cli/lib/util/mergeDeps.js index 269510e126..ad99f7f3b1 100644 --- a/packages/@vue/cli/lib/util/mergeDeps.js +++ b/packages/@vue/cli/lib/util/mergeDeps.js @@ -7,8 +7,13 @@ module.exports = function resolveDeps (generatorId, to, from, sources) { const r1 = to[name] const r2 = from[name] const sourceGeneratorId = sources[name] + const isValidURI = r2.match(/^(?:file|git|git\+ssh|git\+http|git\+https|git\+file|https?):/) != null + const isValidGitHub = r2.match(/^[^/]+\/[^/]+/) != null - if (!semver.validRange(r2)) { + // if they are the same, do nothing. Helps when non semver type deps are used + if (r1 === r2) continue + + if (!isValidGitHub && !isValidURI && !semver.validRange(r2)) { warn( `invalid version range for dependency "${name}":\n\n` + `- ${r2} injected by generator "${generatorId}"` @@ -20,17 +25,19 @@ module.exports = function resolveDeps (generatorId, to, from, sources) { res[name] = r2 sources[name] = generatorId } else { - const r = tryGetNewerRange(r1, r2) + const r1semver = extractSemver(r1) + const r2semver = extractSemver(r2) + const r = tryGetNewerRange(r1semver, r2semver) const didGetNewer = !!r // if failed to infer newer version, use existing one because it's likely // built-in - res[name] = didGetNewer ? r : r1 + res[name] = didGetNewer ? injectSemver(r2, r) : r1 // if changed, update source if (res[name] === r2) { sources[name] = generatorId } // warn incompatible version requirements - if (!semver.intersects(r1, r2)) { + if (!semver.validRange(r1semver) || !semver.validRange(r2semver) || !semver.intersects(r1semver, r2semver)) { warn( `conflicting versions for project dependency "${name}":\n\n` + `- ${r1} injected by generator "${sourceGeneratorId}"\n` + @@ -45,6 +52,8 @@ module.exports = function resolveDeps (generatorId, to, from, sources) { const leadRE = /^(~|\^|>=?)/ const rangeToVersion = r => r.replace(leadRE, '').replace(/x/g, '0') +const extractSemver = r => r.replace(/^.+#semver:/, '') +const injectSemver = (r, v) => semver.validRange(r) ? v : r.replace(/#semver:.+$/, `#semver:${v}`) function tryGetNewerRange (r1, r2) { const v1 = rangeToVersion(r1)