diff --git a/__tests__/commands/install/__snapshots__/integration.js.snap b/__tests__/commands/install/__snapshots__/integration.js.snap new file mode 100644 index 0000000000..cc7c35ce4a --- /dev/null +++ b/__tests__/commands/install/__snapshots__/integration.js.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`install-file-as-default-no-semver 1`] = ` +"{ + \\"author\\": \\"AJ ONeal (http://coolaj86.info)\\", + \\"name\\": \\"foo\\", + \\"description\\": \\"A test module with no \`main\`, \`lib\`, or \`dependencies\` specified\\", + \\"version\\": \\"1.0.0\\", + \\"repository\\": { + \\"type\\": \\"git\\", + \\"url\\": \\"git://github.com/coolaj86/node-pakman.git\\" + }, + \\"engines\\": { + \\"node\\": \\">= v0.2\\" + } +} +" +`; diff --git a/__tests__/commands/install/integration.js b/__tests__/commands/install/integration.js index ca5824a5d2..78ae21df34 100644 --- a/__tests__/commands/install/integration.js +++ b/__tests__/commands/install/integration.js @@ -385,7 +385,7 @@ test.concurrent('root install with optional deps', (): Promise => { }); test.concurrent('install file: protocol with relative paths', (): Promise => { - return runInstall({noLockfile: true}, 'install-file-relative', async config => { + return runInstall({}, 'install-file-relative', async config => { expect(await fs.readFile(path.join(config.cwd, 'node_modules', 'root-a', 'index.js'))).toEqual('foobar;\n'); }); }); @@ -459,17 +459,30 @@ test.concurrent('install file: link file dependencies', async (): Promise }); test.concurrent('install file: protocol', (): Promise => { - return runInstall({noLockfile: true}, 'install-file', async config => { + return runInstall({lockfile: false}, 'install-file', async config => { expect(await fs.readFile(path.join(config.cwd, 'node_modules', 'foo', 'index.js'))).toEqual('foobar;\n'); }); }); test.concurrent('install with file: protocol as default', (): Promise => { - return runInstall({noLockfile: true}, 'install-file-as-default', async config => { + return runInstall({}, 'install-file-as-default', async config => { expect(await fs.readFile(path.join(config.cwd, 'node_modules', 'foo', 'index.js'))).toEqual('foobar;\n'); }); }); +test.concurrent("don't install with file: protocol as default if target is a file", (): Promise => { + // $FlowFixMe + return expect(runInstall({lockfile: false}, 'install-file-as-default-no-file')).rejects.toBeDefined(); +}); + +test.concurrent("don't install with file: protocol as default if target is valid semver", (): Promise => { + return runInstall({}, 'install-file-as-default-no-semver', async config => { + expect(await fs.readFile(path.join(config.cwd, 'node_modules', 'foo', 'package.json'))).toMatchSnapshot( + 'install-file-as-default-no-semver', + ); + }); +}); + // When local packages are installed, dependencies with different forms of the same relative path // should be deduped e.g. 'file:b' and 'file:./b' test.concurrent('install file: dedupe dependencies 1', (): Promise => { @@ -505,7 +518,7 @@ test.concurrent('install file: dedupe dependencies 3', (): Promise => { }); test.concurrent('install everything when flat is enabled', (): Promise => { - return runInstall({noLockfile: true, flat: true}, 'install-file', async config => { + return runInstall({lockfile: false, flat: true}, 'install-file', async config => { expect(await fs.readFile(path.join(config.cwd, 'node_modules', 'foo', 'index.js'))).toEqual('foobar;\n'); }); }); @@ -587,7 +600,7 @@ test.concurrent('install should run install scripts in the order of dependencies }); test.concurrent('install with comments in manifest', (): Promise => { - return runInstall({noLockfile: true}, 'install-with-comments', async config => { + return runInstall({lockfile: false}, 'install-with-comments', async config => { expect(await fs.readFile(path.join(config.cwd, 'node_modules', 'foo', 'index.js'))).toEqual('foobar;\n'); }); }); @@ -759,7 +772,7 @@ test.concurrent('offline mirror can be disabled locally', (): Promise => { // sync test because we need to get all the requests to confirm their validity test('install a scoped module from authed private registry', (): Promise => { - return runInstall({noLockfile: true}, 'install-from-authed-private-registry', async config => { + return runInstall({}, 'install-from-authed-private-registry', async config => { const authedRequests = request.__getAuthedRequests(); expect(authedRequests[0].url).toEqual('https://registry.yarnpkg.com/@types%2flodash'); @@ -774,7 +787,7 @@ test('install a scoped module from authed private registry', (): Promise = }); test('install a scoped module from authed private registry with a missing trailing slash', (): Promise => { - return runInstall({noLockfile: true}, 'install-from-authed-private-registry-no-slash', async config => { + return runInstall({}, 'install-from-authed-private-registry-no-slash', async config => { const authedRequests = request.__getAuthedRequests(); expect(authedRequests[0].url).toEqual('https://registry.yarnpkg.com/@types%2flodash'); diff --git a/__tests__/fixtures/install/install-file-as-default-no-file/bar b/__tests__/fixtures/install/install-file-as-default-no-file/bar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/__tests__/fixtures/install/install-file-as-default-no-file/package.json b/__tests__/fixtures/install/install-file-as-default-no-file/package.json new file mode 100644 index 0000000000..448cf0c1b3 --- /dev/null +++ b/__tests__/fixtures/install/install-file-as-default-no-file/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "foo": "bar" + } +} diff --git a/__tests__/fixtures/install/install-file-as-default-no-semver/1/index.js b/__tests__/fixtures/install/install-file-as-default-no-semver/1/index.js new file mode 100644 index 0000000000..6e6da2546d --- /dev/null +++ b/__tests__/fixtures/install/install-file-as-default-no-semver/1/index.js @@ -0,0 +1 @@ +foobar; diff --git a/__tests__/fixtures/install/install-file-as-default-no-semver/1/package.json b/__tests__/fixtures/install/install-file-as-default-no-semver/1/package.json new file mode 100644 index 0000000000..bfe81c479a --- /dev/null +++ b/__tests__/fixtures/install/install-file-as-default-no-semver/1/package.json @@ -0,0 +1,5 @@ +{ + "name": "1", + "version": "0.0.0", + "main": "index.js" +} diff --git a/__tests__/fixtures/install/install-file-as-default-no-semver/package.json b/__tests__/fixtures/install/install-file-as-default-no-semver/package.json new file mode 100644 index 0000000000..094a73c088 --- /dev/null +++ b/__tests__/fixtures/install/install-file-as-default-no-semver/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "foo": "1" + } +} diff --git a/__tests__/fixtures/install/install-file-as-default-no-semver/yarn.lock b/__tests__/fixtures/install/install-file-as-default-no-semver/yarn.lock new file mode 100644 index 0000000000..83dbd093dd --- /dev/null +++ b/__tests__/fixtures/install/install-file-as-default-no-semver/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 +"foo@file:bar": + version "0.0.0" diff --git a/__tests__/fixtures/request-cache/GET/registry.yarnpkg.com/foo.bin b/__tests__/fixtures/request-cache/GET/registry.yarnpkg.com/foo.bin new file mode 100644 index 0000000000..889fc27be3 Binary files /dev/null and b/__tests__/fixtures/request-cache/GET/registry.yarnpkg.com/foo.bin differ diff --git a/__tests__/fixtures/request-cache/GET/registry.yarnpkg.com/foo/-/foo-1.0.0.tgz.bin b/__tests__/fixtures/request-cache/GET/registry.yarnpkg.com/foo/-/foo-1.0.0.tgz.bin new file mode 100644 index 0000000000..d51d465c36 Binary files /dev/null and b/__tests__/fixtures/request-cache/GET/registry.yarnpkg.com/foo/-/foo-1.0.0.tgz.bin differ diff --git a/src/package-request.js b/src/package-request.js index ee9ac6412e..6a40f25f7e 100644 --- a/src/package-request.js +++ b/src/package-request.js @@ -134,14 +134,20 @@ export default class PackageRequest { async normalizeRange(pattern: string): Promise { if (pattern.indexOf(':') > -1 || pattern.indexOf('@') > -1 || getExoticResolver(pattern)) { - return Promise.resolve(pattern); + return pattern; } - if (await fs.exists(path.join(this.config.cwd, pattern))) { - return Promise.resolve(`file:${pattern}`); + if (!semver.validRange(pattern)) { + try { + if ((await fs.stat(path.join(this.config.cwd, pattern))).isDirectory()) { + return `file:${pattern}`; + } + } catch (err) { + // pass + } } - return Promise.resolve(pattern); + return pattern; } async normalize(pattern: string): any {