-
Notifications
You must be signed in to change notification settings - Fork 29.2k
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
module: expose getPackageJSON
utility
#55229
base: main
Are you sure you want to change the base?
module: expose getPackageJSON
utility
#55229
Conversation
Review requested:
|
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #55229 +/- ##
==========================================
+ Coverage 88.23% 88.40% +0.17%
==========================================
Files 651 652 +1
Lines 183863 186639 +2776
Branches 35824 36069 +245
==========================================
+ Hits 162235 165005 +2770
+ Misses 14932 14899 -33
- Partials 6696 6735 +39
|
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
Co-authored-by: Aviv Keller <[email protected]>
|
||
return { | ||
data: { | ||
__proto__: null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having null-prototype object returned to the object feels weird
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. I did it for consistency between everything
(not everything
is used internally and needs the null prototype).
> Stability: 1.1 - Active Development | ||
* `startPath` {string} Where to start looking | ||
* `everything` {boolean} Whether to return the full contents of the found package.json |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure it is a good idea to introduce yet-another way of loading a JSON file, wouldn't it be simpler to return the path and let the user deal with reading/parsing the file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I considered that (there's another PR I opened and closed that exposed findPackageJSON
, which did exactly that). We're already reading the pjson and caching it, so IMO that would force a de-op as well as force the user to manually do what we're already doing. So, why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think returning the parsed data means every time we consider parsing another field in the package.json, it needs to be surfaced into this API, even though that field may not even be useful for tools, but we'll end up paying the serialization/deserialization cost even though internally only selected places need selected fields. IMO even just returning the string would be better than this. Users need this for the lookup algorithm, they tend to have other fields to parse anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May I recommend a different naming and API surface?
module.findPackageJson(path, { includeContents: true })
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, @joyeecheung I'm not sure I follow in your message. I think you've misunderstood the behaviour here: internally, we do not use the full version—we do use getNearestParentPackageJSON
but do not set everything
to true
. When everything
is not true
, only the select fields we use internally are parsed. That has not changed in this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May I recommend a different naming and API surface?
module.findPackageJson(path, { includeContents: true })
@anonrig if you recall, when we were working on this a few days ago, that did not make sense due to how the C++ worked, which is what caused me to abandon the findPackageJSON
+ getPackageJSON
route in the first place.
Regarding "find" + includeContents
, this seems counter-intuitive to me: I expect something called "find" to only locate, not retrieve. I could perhaps see the inverse
module.getPackageJSON(path, { includeContents: false })
I do not like either option though because the shape of the return is changed significantly in a foot-gun way:
getPackageJSON(path, false) // '…/package.json'
getPackageJSON(path, true) // { data: {…}, path: '…/package.json' }
(I simplified the 2nd arg for brevity, not necessarily to say we shouldn't use an object)
I suppose it could always return an object for consistency, but that seems like a clumsy compromise
getPackageJSON(path, false) // { path: '…/package.json' }
getPackageJSON(path, true) // { data: {…}, path: '…/package.json' }
If we were to do this, I think exposing 2 different utils (findPackageJSON
+ getPackageJSON
) would be better.
...(main != null && { main }), | ||
...(type != null && { type }), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you change these lines? Meaning anything after the highlighted lines included 63-64.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because as it was before, empty fields are forced to be present but with no value:
{
"name": "foo",
"exports": "./index.js"
}
{
name: 'foo'
main: undefined,
exports: './index.js',
}
The only result is hassle and gotchas: ex 'main' in pkg
→ true
, which is a lie ("main" is not in the package.json).
And in tests, it forces you to add extra properties that don't matter:
assert.deepStrictEqual(pkg, {
name: 'foo'
exports: './index.js',
});
Pedantically fails because main: undefined
is not explicitly specified.
*/ | ||
function getNearestParentPackageJSON(checkPath) { | ||
const result = modulesBinding.getNearestParentPackageJSON(checkPath); | ||
function getNearestParentPackageJSON(startPath, everything = false) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of everything, can we change this to includeContents
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And preferably make this an object rather than a plain argument. getPackageJSON(path, true)
does not help the developer, unless they look into the documentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
includeContents
does not make sense: it will include some of the contents regardless.
does not help the developer, unless they look into the documentation.
IntelliSense :)
I think an object makes sense when there are (or potentially will be) multiple configuration options. Here it seems verbose.
typings/internalBinding/modules.d.ts
Outdated
PackageConfig['name'], | ||
PackageConfig['main'], | ||
PackageConfig['type'], | ||
RecognisedPackageConfig['name'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These changes seems to be unnecessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree: PackageConfig
sounds like the superset of all config options. That's not what it actually is. The potential for confusion is now much more because there are now both options (subset and superset). This rename removes the ambiguity to preclude confusion.
typings/internalBinding/modules.d.ts
Outdated
export type PackageConfig = { | ||
pjsonPath: string | ||
export type RecognisedPackageConfig = { | ||
pjsonPath: URL['pathname'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would resolve to string
, and variable name also includes path
suffix. I recommend just using string
pjsonPath: URL['pathname'] | |
pjsonPath: string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We talked about this on our screenshare a few days ago, and I showed why what I did is better: Whilst ultimate, yes, it does resolve to string
, not any string is acceptable, so the extra human-friendly context provides valuable info; also, IntelliSense displays it as URL['pathname']
.
If we really wanted to make it correct, we could create a template literal type to properly validate via typescript.
typings/internalBinding/modules.d.ts
Outdated
export type FullPackageConfig = RecognisedPackageConfig & { | ||
[key: string]: unknown, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would make every value of FullPackageConfig unknown
since unknown has higher precedence than any literal type, and I don't think we need this type in here, since it is a return value and depends on the contents of the package.json. Since we are not accessing them in node.js core, we don't need to add a type for them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would make every value of FullPackageConfig
unknown
That's not correct: TS Playground
I don't think we need this type in here […] since we are not accessing them in node.js core
I disagree: Failing to do this causes it to lie. A lying tool is worse than no tool. Even if we're not consuming them, the extra info could still be valuable, and a lie could cause an unaware dev to make a mistake. There is no drawback to doing it correctly, so we should do it correctly.
typings/internalBinding/modules.d.ts
Outdated
string | undefined, // exports | ||
string | undefined, // imports | ||
string | undefined, // raw json available for experimental policy | ||
RecognisedPackageConfig['pjsonPath'], // pjson file path |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change seems unrelated. experimental policy
doesn't exist anymore, so we might just replace this line with raw json
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so we might just replace this line with raw json.
I'm not sure what you're talking about. The type dec was merely wrong—this element was already pjsonPath
.
> Stability: 1.1 - Active Development | ||
* `startPath` {string} Where to start looking | ||
* `everything` {boolean} Whether to return the full contents of the found package.json |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May I recommend a different naming and API surface?
module.findPackageJson(path, { includeContents: true })
41163ca
to
d9bdf23
Compare
Finally got around to exposing one of the utilities we've long-discussed providing to users.
Currently, users (particularly library authors like yarn, who I believe originally requested this) have to re-implement this functionality.
I ran into this issue when building a codemod that needs to consume
pjson.types
/pjson.exports[…].types
JakobJingleheimer/correct-ts-specifiers#6
References:
exports
field loaders#43