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

Add Typescript typings + rework #37

Merged
merged 6 commits into from
May 8, 2021
Merged

Conversation

kanongil
Copy link
Contributor

@kanongil kanongil commented Apr 11, 2021

I wanted to add official typings to this module. In order to best do this, I added 2 breaking changes.

  1. Move default export to Mimos. Ie. the class will now be found at Mimos.Mimos(). This is because Typescript has issues with default exports, and matches what was done in Boom.
  2. Change path() to return null when no entry is found (instead of {}) – The {} return was a nonsensical API. Eg. declaring the return value with type MimosEntry | {}, makes it impossible to narrow it. With null it can be done with a simple if (returnedValue).

While I was doing the changes, I found opportunities to speed it up. From the DB setup, the initial load took 10s of ms on my machine. I made several changes to speed it up:

  1. Use Map() type instead of POJO for DB storage.
  2. Rework compilation using knowledge of source data to avoid expensive clone() and merge() of mime-db data.
  3. Use initially compiled DB as basis for overrides (instead of a full re-compile).

The behavior should match the current for common usage, with 2 exceptions for corner cases:

  1. The extensions property is assigned with the value from the parsed json. If someone were to modify this mime-db object later, it can change the returned extensions value. I don't believe this will be an issue in practice.
  2. The path() lookup might match another (valid) mime-type, if an override was provided for it, and multiple entries share an extension. This is a rare case, that already relies on V8-specific behaviour. It probably works better, since overrides now always take precedence.

For the ´type()` lookup, I made 2 speed-ups:

  1. Faster type extraction from provided mime type (removing any options), using indexOf() and slice().
  2. Attempt a pre-check with a quick lookup, before doing the expensive .trim().toLowerCase(). This is a heuristic based optimisation, which will make the lookup slightly slower for an upper-cased type lookup, but make it a lot faster for the simple common case.

Since mimos.type() is called on most hapi server responses, this should make hapi slightly faster.

This rewrite drastically improves both initial loading performance, as well as type lookup performance.

The includes 2 breaking changes in preparation for Typescript support.

 1. Move default export to `Mimos`.
 2. Change path() lookup to return `null` when no result.
This should be compatible with the current usage in @types/hapi__hapi.
@kanongil kanongil added breaking changes Change that can breaking existing code feature New functionality or improvement types TypeScript type definitions labels Apr 11, 2021
@devinivy
Copy link
Member

@kanongil thanks for this. I will review it more closely soon, but this all generally sounds good to me. While I agree with the breaking change regarding the result of path(), I wonder if we should temporarily hold off on this since it is part of hapi's public API via server.mime.

@kanongil
Copy link
Contributor Author

Hmm, I had not thought about the direct exposure in hapi. Technically, it would not be considered a breaking change from hapi POV, as it is just defined as whatever object mimos created, but I would prefer not to push it.

I guess I will go back to the MimosEntry | {} for now, and TS users will just have to live with it being a pain to use?

It would be handled something like this, for somewhat safe typing:

const entry = mimos.path(path);
if ((entry as MimosEntry).type) {
    const mime = entry as MimosEntry;
    
}

Copy link
Member

@Nargonath Nargonath left a comment

Choose a reason for hiding this comment

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

LGTM but it might be a good idea to release progressively with a non-breaking version first. Also shouldn't we create an issue on hapi or create a new major milestone to keep track of all the breaking changes we want to do in the next big version? I believe this is not the first breaking change we postpone and I wonder if we're not going to forget about them as time goes by.

lib/index.js Show resolved Hide resolved
@Marsup
Copy link
Contributor

Marsup commented Apr 24, 2021

Wouldn't we benefit from having a kind of citgm for hapi that we could trigger with a matrix of modules and see if at least it builds?

@devinivy
Copy link
Member

devinivy commented Apr 24, 2021

@Marsup yes, that would be amazing.

@Nargonath sounds good— I will be careful not to resolve this PR without ensuring that additional work is captured somewhere.

@kanongil
Copy link
Contributor Author

I have given this some further thought, and if we want to go with this change, I would be inclined to just update hapi with it without doing a breaking change. As I already said, this is technically not a breaking change for hapi, but more importantly, even if pushed as a breaking change, it can still cause issues for years since plugins might call into the api, expecting the old behavior. The returned value is a corner-case that could go easily pass 100% test coverage even after such an update.

So I guess the question is, if this instability is worth it. I'm leaning towards: NO. I would rather keep the existing API, and potentially take measures to simplify TS usage of the returned object.

Copy link
Contributor Author

@kanongil kanongil left a comment

Choose a reason for hiding this comment

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

I undid the path() breaking change.

To make TS usage simple, I export the MimosEntry class and expose it as such in the types. This enables a simple instanceof check on the the returned value, which will correctly narrow the type. Eg.:

const mime = mimos.path(path);
if (mime instanceof MimosEntry) {
    /* mime is ready for use */
    
}

test/index.ts Outdated Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
Copy link
Member

@devinivy devinivy left a comment

Choose a reason for hiding this comment

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

Looks good, thanks Gil! I've tested these changes locally against hapi (with some very minor changes within hapi), and all seems to be well there too. I also peeled off the breaking change to mimos.type() proposed here into #38.

@devinivy devinivy added this to the 6.0.0 milestone May 5, 2021
Copy link
Member

@Nargonath Nargonath left a comment

Choose a reason for hiding this comment

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

Found just a little error in the docs otherwise LGTM

API.md Outdated Show resolved Hide resolved
Co-authored-by: Jonas Pauthier <jonas.pauthier@gmail.com>
@devinivy devinivy merged commit b5b5974 into hapijs:master May 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking changes Change that can breaking existing code feature New functionality or improvement types TypeScript type definitions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants