Skip to content

Commit

Permalink
feat(plugin-api): allow non-semver versioned dependencies (vuejs#1177)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
test committed Apr 27, 2018
1 parent a0a7dc6 commit 577bbb0
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 4 deletions.
107 changes: 107 additions & 0 deletions packages/@vue/cli/__tests__/Generator.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
{
Expand Down Expand Up @@ -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: [
{
Expand Down
17 changes: 13 additions & 4 deletions packages/@vue/cli/lib/util/mergeDeps.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}"`
Expand All @@ -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` +
Expand All @@ -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)
Expand Down

0 comments on commit 577bbb0

Please sign in to comment.