Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #5 by properly encoding the name of scoped packages before requesting their package from npm #6

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,37 @@ function get(url, cb) {
});
}

function getCleanName(name){
// Any path components in the name need to be URI encoded, however the @
// symbol must not be encoded.
// Valid URL: https://registry.npmjs.org/@sindresorhus%2Fdf/
// Invalid URL: https://registry.npmjs.org/%40sindresorhus%2Fdf
return encodeURIComponent(name).replace(/^%40/, '@');
}

function packageIsScoped(name){
return name[0] === '@';
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really sure what this function is supposed to do?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. It's purpose is not clear, and tbh, I do not recall either. It is definitely required, though. I will re-discover why it was needed and add comments as appropriate.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lzilioli thank you for the updated documentation for this method.


module.exports = function (name, version, cb) {
var url = registryUrl(name.split('/')[0]) + name + '/';
var registry = registryUrl(name.split('/')[0]);
var url = registry + getCleanName(name) + '/';

if (typeof version !== 'string') {
cb = version;
version = '';
}

if (version && packageIsScoped(name)) {
throw new Error('Fetching a specific version of a scoped package is not allowed by npm.');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't even see this earlier. Thank you for adding a check here.

}

get(url + version, cb);
};

module.exports.field = function (name, field, cb) {
var url = registryUrl(name.split('/')[0]) +
'-/by-field/?key=%22' + name + '%22&field=' + field;
'-/by-field/?key=%22' + getCleanName(name) + '%22&field=' + field;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getCleanName(name) => encodeURIComponent(name).replace(/^%40/, '@')

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


get(url, function (err, res) {
if (err) {
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@
],
"dependencies": {
"got": "^3.2.0",
"registry-url": "^3.0.0"
"registry-url": "^3.0.3"
},
"devDependencies": {
"chai": "^3.0.0",
"mocha": "*"
}
}
71 changes: 45 additions & 26 deletions test.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,55 @@
'use strict';
var assert = require('assert');
var assert = require('chai').assert;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Output when using assert

[package-json (issue-5 *) Mon Jul 27 01:45:36]
$ npm test

> package-json@1.2.0 test /Users/lzilioli/Projects/otherrepos/package-json
> mocha



  public packages
    ✓ should get the package.json (271ms)
    ✓ should get the package.json for a specific version (114ms)
    ✓ should get the package.json main entry when no version is specified (84ms)
    ✓ get a single field (119ms)

  scoped packages
    1) should get the package.json
    2) should get the package.json for a specific version
    3) should get the package.json main entry when no version is specified
    ✓ get a single field (124ms)


  5 passing (2s)
  3 failing

Output when using chai.assert

$ npm test

> package-json@1.2.0 test /Users/lzilioli/Projects/otherrepos/package-json
> mocha



  public packages
    ✓ should get the package.json (108ms)
    ✓ should get the package.json for a specific version (57ms)
    ✓ should get the package.json main entry when no version is specified (70ms)
    ✓ get a single field (363ms)

  scoped packages
    1) should get the package.json
    2) should get the package.json for a specific version
    3) should get the package.json main entry when no version is specified
    ✓ get a single field (122ms)


  5 passing (2s)
  3 failing

  1) scoped packages should get the package.json:
     Uncaught TypeError: undefined is not a function
      at test.js:10:4
      at index.js:8:4

  2) scoped packages should get the package.json for a specific version:
     Uncaught TypeError: undefined is not a function
      at test.js:18:4
      at index.js:13:4
      at IncomingMessage.onend (_stream_readable.js:505:10)
      at _stream_readable.js:908:16

  3) scoped packages should get the package.json main entry when no version is specified:
     Uncaught TypeError: undefined is not a function
      at test.js:26:4
      at index.js:8:4



npm ERR! Test failed.  See above for more details.

The latter is significantly more descriptive and easy to debug.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I live the more descriptive form.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you have an outdated mocha. I get with an intentional failing assertion:

  ✓ should get the package.json (429ms)
  1) should get the package.json for a specific version
  ✓ should get the package.json main entry when no version is specified (219ms)
  ✓ get a single field (684ms)

  3 passing (2s)
  1 failing

  1)  should get the package.json for a specific version:

      Uncaught AssertionError: '0.1.0' === '1.1.0'
      + expected - actual

      -0.1.0
      +1.1.0

      at test.js:16:10
      at index.js:17:3
      at IncomingMessage.onend (_stream_readable.js:505:10)
      at _stream_readable.js:908:16

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On issue-5 branch with assert:

(~/.../test/package-json)-(git::issue-5)--> npm test

> package-json@1.2.0 test /home/user/hbetts/workspace/test/package-json
> mocha



  public packages
    ✓ should get the package.json (73ms)
    ✓ should get the package.json for a specific version
    ✓ should get the package.json main entry when no version is specified (46ms)
    1) get a single field

  scoped packages
    2) should get the package.json
    3) should get the package.json for a specific version
    4) should get the package.json main entry when no version is specified
    5) get a single field


  3 passing (266ms)
  5 failing

Checking outdated packages:

(~/.../test/package-json)-(git::issue-5)--> npm outdated
Package  Current  Wanted  Latest  Location
got        3.3.1   3.3.1   4.1.1  got

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sindresorhus is the outdated mocha suggestion to me in regards to lacking useful output on failed assertions? I am using whichever version is coming with npm install for the package. In my case, 2.2.5 (which is the latest mocha release).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I disable the specific version test for the scoped package, and log a message to the command line if a user tries to do so for now so that we can get the rest of this patch deployed? We could then enable specific version functionality for scoped packages in the future when the downstream issue with npm is resolved.

@destroyerofbuilds thanks for digging into that issue, and good call checking out the response headers

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I received a little feedback in IRC, but not resolution: http://logs.nodejs.org/npm/latest#19:04:58.872

I'm for disabling the offending test, and filing an issue to follow back around with the issue later.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@destroyerofbuilds I just pushed a commit to throw an error if a version is requested for a scoped package, and update the tests to check for the error in the scoped package case.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm for disabling the offending test, and filing an issue to follow back around with the issue later.

👍

@lzilioli Can you also update the readme to mention this limitation?

var packageJson = require('./');

it('should get the package.json', function (cb) {
packageJson('pageres', function (err, json) {
assert(!err, err);
assert.strictEqual(json.name, 'pageres');
cb();
});
});
function runTests(cfg){

it('should get the package.json for a specific version', function (cb) {
packageJson('pageres', '0.1.0', function (err, json) {
assert(!err, err);
assert.strictEqual(json.version, '0.1.0');
cb();
it('should get the package.json', function (cb) {
packageJson(cfg.name, function (err, json) {
assert(!err, err);
assert.strictEqual(json.name, cfg.name);
cb();
});
});
});

it('should get the package.json main entry when no version is specified', function (cb) {
packageJson('pageres', function (err, json) {
assert(!err, err);
assert(json._id);
cb();
it('should get the package.json for a specific version', function (cb) {
if(cfg.version === 'expect-throw') {
assert.throws(packageJson.bind(null, cfg.name, cfg.version));
cb();
} else {
packageJson(cfg.name, cfg.version, function (err, json) {
assert(!err, err);
assert.strictEqual(json.version, cfg.version);
cb();
});
}
});
});

it('get a single field', function (cb) {
packageJson.field('pageres', 'description', function (err, res) {
assert(!err, err);
assert(typeof res === 'string');
assert(/screenshots/.test(res));
cb();
it('should get the package.json main entry when no version is specified', function (cb) {
packageJson(cfg.name, function (err, json) {
assert(!err, err);
assert(json._id);
cb();
});
});

}

// test spec
var spec = {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Glad to see all test cases applied to both scoped, and un-scoped, packages.

'public packages': {
name: 'pageres',
version: '0.1.0',
descriptionRe: /screenshots/
},
'scoped packages': {
name: '@sindresorhus/df',
version: 'expect-throw',
descriptionRe: /Get free disk space info from/
}
};

// test runner
Object.keys(spec).forEach(function(key){
describe(key, runTests.bind(null, spec[key]));
});