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

[NpmUnpackedSize] Unpacked Size Badge #9954

Merged
merged 25 commits into from
Feb 19, 2024

Conversation

erayerdin
Copy link
Contributor

Closes #9952

Copy link
Contributor

github-actions bot commented Feb 7, 2024

Messages
📖 ✨ Thanks for your contribution to Shields, @erayerdin!

Generated by 🚫 dangerJS against 25c5207

@erayerdin erayerdin marked this pull request as ready for review February 7, 2024 12:48
@erayerdin
Copy link
Contributor Author

Some tests for NPM apart from what I've added fail oddly. I think it's due to requiring a token, maybe?

@chris48s chris48s changed the title [NPM] Unpacked Size Badge [NpmUnpackedSize] Unpacked Size Badge Feb 11, 2024
Copy link
Contributor

🚀 Updated review app: https://pr-9954-badges-shields.fly.dev

Comment on lines +61 to +62
message: unpackedSize ? prettyBytes(unpackedSize) : 'unknown',
color: unpackedSize ? 'blue' : 'lightgray',
Copy link
Member

Choose a reason for hiding this comment

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

Can you provide any detail on when this value would or would not be populated in the response?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

4.15.0 does not have dist.unpackedSize while 4.18.2 or latest has it.

I think the field is populated when someone publishes a package on NPM, so some older versions of the same package (especially when the package is old) does not have dist.unpackedSize.

@chris48s
Copy link
Member

chris48s commented Feb 11, 2024

OK. the tests that are failing are the badges that get stats from npm-stat.com Looks like that service may have gone away. https://npm-stat.com/

This is unrelated to this PR, so lets ignore that for now. Lets move that issue to #9964 . I've changed the PR title so we only run tests for the service added in this PR.


static route = {
base: 'npm/unpacked-size',
pattern: ':packageName/:version*',
Copy link
Member

Choose a reason for hiding this comment

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

The badge should accept a scoped or unscoped package name. Can you

  • Fix the route + code (route should be scope(@[^/]+)?/:packageName/:version* )
  • Add service tests for scoped package with and without version

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Completed. Resolve if okay.


t.create('Latest unpacked size')
.get('/express.json')
.expectBadge({ label: 'unpacked size', message: /.* kB/ })
Copy link
Member

Choose a reason for hiding this comment

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

Can we use the isFileSize test validator for the valid tests instead of writing a custom regex please

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wasn't aware it existed, I've checked other npm services and bundlephobia service for my code. Will do it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Completed. Resolve if okay.


async fetch({ packageName, version }) {
return this._requestJson({
schema: Joi.any(),
Copy link
Member

Choose a reason for hiding this comment

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

Instead of using Joi.any() here, can you write a schema that ensures the data we need exists in the response and is of the right type. There's some docs on input validation in https://github.com/badges/shields/blob/master/doc/input-validation.md . Not tested, but I guess either

Joi.object({
  dist: Joi.object({
    unpackedSize: nonNegativeInteger.required(),
  }).required()
}).required()

or

Joi.object({
  dist: Joi.object({
    unpackedSize: nonNegativeInteger
  }).required()
}).required()

will be what we want here, depending on how we want to handle the case where unpackedSize does not exist. One option would be to accept a missing value and handle it in the code (like you've done). the other option would be to reject the response at the validation stage. I guess that depends on the answer to #9954 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could not complete, refer to the review: #9954 (comment)

@chris48s chris48s added the service-badge New or updated service badge label Feb 11, 2024
@chris48s
Copy link
Member

Thanks for working on this btw. There's a few issues to iron out, but it is a good start 👍


t.create('Nonexistent unpacked size with version')
.get('/express/4.16.0.json')
.expectBadge({ label: 'unpacked size', message: 'unknown' })
Copy link
Contributor Author

@erayerdin erayerdin Feb 12, 2024

Choose a reason for hiding this comment

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

After adding schema to validate NPM registry response body, this test fails saying this:

5) NpmUnpackedSize
       [live] Nonexistent unpacked size with version
         
        [ GET /express/4.16.0.json ]:

      label mismatch
      + expected - actual

      -size
      +unpacked size

When I manually requested the local dev endpoint, I've seen the badge as such:

the badge with error

(could not paste the PNG of it as it's not available in the dev server)

I expected the label to be unpacked size instead of only size. It seems like, when the schema validation fails, it renders the label as size, which, I think, is a built-in mechanism.

I think when dist.unpackedSize is not defined, the label should still be unpacked size to imply that this is not any kind of other size such as minified or min+gzipped.

@chris48s, do you have any idea as to how to override the default badge label and message when dist.unpackedSize does not exist? I've tinkered a little bit with the codebase but I could not see any way (maybe I've missed it or haven't realized).

Copy link
Contributor Author

@erayerdin erayerdin Feb 13, 2024

Choose a reason for hiding this comment

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

Actually, I've checked it through BaseJsonService._requestJson method, which calls BaseService._validate. BaseService._validate has positional options parameter, in which, prettyErrorMessage has been set to "invalid response data".

All the way up the stack (from NpmUnpackedSize service), I cannot set prettyErrorMessage because it is internal to BaseJsonService and BaseService. Given that BaseJsonService._requestJson and BaseService._validate are internals (prepended by underscore), I naturally hesitate to change them.

Thus, there are two possible paths here:

  1. Modify BaseService._validate and BaseJsonService._requestJson methods so that I can reach and modify the message (and thus, the label) when dist.unpackedSize does not exist. Given that these methods are defined as internals and tons of services rely on their signature, this might/would cause enormous amount of failures on some tests in the best case, or unexpected behavior on runtime at worst.
  2. Accept the sweet fate and let the badge stay as size: invalid response data. I thought it wouldn't be a viable solution as (given that JS is used in web) it is important to differ unpacked size from minified or min+gzipped sizes but there's no sane room here. So, I can only modify the test to satisfy size: invalid response data badge.

@chris48s What's your idea?

Copy link
Member

Choose a reason for hiding this comment

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

Ah right - sorry. In my original post, the suggestions should have been

optionalNonNegativeInteger and nonNegativeInteger
not
nonNegativeInteger and nonNegativeInteger.required()

🤦

if we switch to using optionalNonNegativeInteger for the unpackedSize key, then you should be able to carry on doing what you were doing and render "unknown" if the unpackedSize key is missing.

The validation is still useful, because it ensures the response has a dist object and that unpackedSize is of the right type if it exists.

If you want to set the label as "unpacked size" even when an exception is thrown, setting

static defaultBadgeData = { label: 'unpacked size' }

will do the job.

Does that unstick you?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, did it. We definitely need documentation about these premade validators, missed it somehow. That's maybe the topic of another PR.

With that in mind, seems like this is completed.

Copy link
Member

Choose a reason for hiding this comment

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

The validators are included in the internal api docs here: https://contributing.shields.io/module-services_validators.html

example: '4.18.2',
}),
queryParam({
name: 'registry_uri',
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also added a registry_uri query param as they were existent on the other NPM-based services.


async handle(namedParams, queryParams) {
const { scope, packageName, tag, registryUrl } =
this.constructor.unpackParams(namedParams, queryParams)
Copy link
Member

Choose a reason for hiding this comment

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

I think you had this right first time. We now seem to be mixing the concepts of version and dist tag.
For some badges, we use a tag param, but that's not the same as a version. So for example

latest version of npm package: https://img.shields.io/npm/v/npm
latest version of npm package with dist-tag "next-8": https://img.shields.io/npm/v/npm/next-8

but its not the same as a version. What you're actually implementing here is a version param. i.e: the unpacked size of a specific version of this package. The thing we're accepting here isn't a dist tag.

I'd say just list the actual params in the signature of handle() rather than use the wrong helper.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Welp, I confused that. Sorry. Alright, let me get to reimplement version back.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Completed. Reverted back to using version instead of tags.

Copy link
Contributor

🚀 Updated review app: https://pr-9954-badges-shields.fly.dev

Copy link
Member

@chris48s chris48s left a comment

Choose a reason for hiding this comment

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

cool - latest changes LGTM 👍

@chris48s chris48s added this pull request to the merge queue Feb 19, 2024
Merged via the queue into badges:master with commit 503764e Feb 19, 2024
22 checks passed
@erayerdin erayerdin deleted the feat/npm-unpacked-size-badge branch February 19, 2024 10:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
service-badge New or updated service badge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

A badge for "unpacked size" in NPM
2 participants