-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Surprising (or incorrect) priorities with package.json exports fields? #46334
Comments
My assumption was that a top-level |
Nope. Is the referencing file cjs mode or is esm mode? An |
|
I missed that the |
I was importing from a module ( |
I think this is fair, but it highlights 3 things to me
|
In any case, it's a bug that this one didn't work, right? "exports": {
".": {
"import": {
"node": "./index.mjs",
"default": "./dist/vue.runtime.esm-bundler.js"
+ "types": "./dist/vue.d.ts"
},
"require": "./index.js",
},
} |
|
Oof that's really confusing. Shouldn't |
The mistake is thinking they're prioritized - they're not. They're either on or off, and the first condition (in object insertion order) that is on is selected. |
I don't think this is required for TypeScript to do, Node's official guidance is:
However the point about
Doesn't really apply to TypeScript when looking for types as the "universal implementation" is not neccessarily what is desired. I think the easiest way for TypeScript to behave in an expected way would be to do two passes on conditions, first do a pass for i.e. The logic would be like: const typeFile = lookupExportMap(["import", "types"]);
if (exists(typeFile)) {
// use it
return (...);
}
// If an explicit type mapping doesn't exist look for a module file
const moduleFile = lookupExportMap(["import", "default"]);
if (!exists(moduleFile)) {
throw new Error("No such module");
}
if (moduleFile.endsWith(".js")) {
const dTs = replaceExtension(moduleFile, ".d.ts");
if (exists(dTs)) {
// Use the d.ts file
return (...);
}
// Else try parsing moduleFile for JSDOC
return (...);
}
// And similar for .cjs/.mjs
// and whatever other handling for .json etc |
Export maps, very explicitly and by their design, follow the priority order listed in the conditional export object. That... Can be confusing at times; but not respecting that in TS would only lead to more confusion. |
The above suggestion does not change this, it just queries twice with two sets of conditions, for each of the queries it still does things in order as per usual.
I mean either case is confusing, however I think allowing it to work is still perfectly explainable, TypeScript tries
There is also the fact the fact |
Except we kinda do. First, we'll look for .d.ts files next to the js (or extensionless!) file we find, so in that way, every js resolution is a valid potential resolution, second we do load js files directly when allow js is set, so we need to follow js resolution rules, and lastly, every condition needs to be set at the same time to support compound (ie, nested) conditions. |
Likely there should be a warning if anything comes after a "default" condition. |
Right I see what you mean.
Yeah this would solve the problem, is there an intention to add this into Node or should this be part of say text editors, or even other tooling? |
We’re looking into adding it to tsc and surfacing it in VS Code. |
Should this impact the apps not using Coz, I have an application that breaks after upgrading to 4.5. Here is a sample repo to reproduce the issue. https://github.com/thetutlage/Typescript-4-5-regression Happy to provide more info if required :) |
Our lovely TypeScript is finding every possible way to break things on every release. I cannot find any documentation on "export maps" behavior, except these issues microsoft/TypeScript#46860 microsoft/TypeScript#46334
@thetutlage that’s #46770, which I believe is a bug—didn’t realize it was affecting non- |
Actually #46770 has a couple different things going on so it’s hard to say what’s what. The issue you reproduced is caused by this: TypeScript/src/compiler/moduleNameResolver.ts Line 343 in 68bf5a5
Type reference directives are always being resolved with TypeScript/src/compiler/moduleNameResolver.ts Line 1993 in 68bf5a5
|
^ fixed by #47007 |
Also I see |
Yes, node12/nodenext has no special handling of index files. |
When trying to use `fast-check` with TypeScript where `moduleResolution` is set to `Node12` or `NodeNext` we get the following error: > Could not find a declaration file for module 'fast-check'. '/home/makeen/q2web/Dev/openapi-codegen/node_modules/fast-check/lib/esm/fast-check.js' implicitly has an 'any' type. > Try `npm i --save-dev @types/fast-check` if it exists or add a new declaration (.d.ts) file containing `declare module 'fast-check';`ts(7016) This change fixes the issue. I think this might be related: microsoft/TypeScript#46334
When trying to use `fast-check` with TypeScript where `moduleResolution` is set to `Node12` or `NodeNext` we get the following error: > Could not find a declaration file for module 'fast-check'. '/home/makeen/q2web/Dev/openapi-codegen/node_modules/fast-check/lib/esm/fast-check.js' implicitly has an 'any' type. > Try `npm i --save-dev @types/fast-check` if it exists or add a new declaration (.d.ts) file containing `declare module 'fast-check';`ts(7016) This change fixes the issue. I think this might be related: microsoft/TypeScript#46334
It sounds like the end result of the conversation abve is that if a package has a top level If so, what advice will you give to those who consume packages like this? Declaring Also: I don't know if it was brought up previously, but this behavior has a sort of impedance-mismatch with existing frontend bundlers -- |
First of all, note that if To answer your other questions: In
Nothing specifically for this problem, but tsconfig
Please 🙏
My take is that it’s basically a coincidence that TypeScript worked reasonably well with bundlers for years without complaints. Part of the promise of bundlers was that you can develop your frontend projects like Node, using dependencies from npm, and so bundlers copied Node’s module resolution strategy, so Also, I think this issue can be closed? |
Excellent answer as always, thanks Andrew. One or two follow-ups though:
This sounds like a sentence that should appear somewhere on a "for library authors" page in the TS handbook. Does it?
This isn't a hypothetical. When Webpack started respecting |
Otherwise projects might complain, here's Vue: > src/App.vue:5:23 - error TS7016: Could not find a declaration file for module '@axiomhq/js'. '/Users/arne/Developer/axiomhq/axiom_ts_issue/node_modules/@axiomhq/js/dist/esm/index.js' implicitly has an 'any' type. > There are types at '/Users/arne/Developer/axiomhq/axiom_ts_issue/node_modules/@axiomhq/js/dist/types/index.d.ts', but this result could not be resolved when respecting package.json "exports". The '@axiomhq/js' library may need to update its package.json or typings. See also microsoft/TypeScript#46334
I'm trying a scenario of
module: nodenext
with Vue.js. I hit a few issues with resolution of declaration files.Here's the current Vue.js declarations
package.json
:However, referencing this in a project results in the following error:
This kind of makes sense - I think you could argue that this isn't configured right for
moduleResolution: node12
or later.I was able to get this working by adding
"exports": { ".": { "import": { "node": "./index.mjs", "default": "./dist/vue.runtime.esm-bundler.js" }, "require": "./index.js", + "types": "./dist/vue.d.ts" }, }
But the following DID NOT work.
"exports": { ".": { "import": { "node": "./index.mjs", "default": "./dist/vue.runtime.esm-bundler.js" + "types": "./dist/vue.d.ts" }, "require": "./index.js", }, }
That part seems like a bug, right?
The text was updated successfully, but these errors were encountered: