You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Note: if you're here following a link from our docs about experimentalResolver, you know that our docs for this feature are currently quite limited. Feel free to experiment and ask questions in Discussions or on Discord. This epic tracks the development work to finish all the resolver features and eventually promote them out of experimental status.
Current status
Describes state of our main branch, may not be published to npm yet
All file extensions are supported, both CommonJS and ECMAScript modules
Use module: NodeNext, esm: true, and experimentalResolver: true. Everything works: cjs, cts, mjs, mts, with or without file extensions in import specifiers, in CommonJS or ESM files
Motivation
In TS, you import the emitted output path, and the compiler resolves this to the input source. They might have different extensions or be in different directories.
In ts-node, we should do the same.
Creating an epic to link together all the related tickets and tasks to make this happen.
I believe that TS creates a mapping from all source files to their emit paths. It can do this because it starts with a glob of all source files, then computes the output path for each in a loop.
Aside: Does this work with files not included in a files array? Does it trigger inclusion of those files in compilation? If yes, that would violate my understanding above.
How it'll work in ts-node
ts-node must do the opposite of TS: we start from a runtime require() or import request, then work backwards to a source TS file. We don't know if such a source file exists; we might be importing a non-compiled .js file.
Mappings to keep in mind:
package.jsonexports
package.jsonmain
node_modules search
Searching all matching file extensions
tsconfig.jsonoutDir and rootDir correspondence
tsconfig.jsonpaths
tsconfig.jsonbaseUrl
tsconfig.jsonrootDirs
Supported extensions are affected by:
resolveJsonModule
allowJs
jsx
TS >=4.5 supports mts, cts, mjs, cjs
moduleResolution or module?
break down this list by (a) those needed when resolving relative specifiers (b) those needed when resolving non-relative specifiers, such as foo
Questions
"Resolve to source path" vs "pretend emit exists on disk"
When resolving from ./dist/foo.js to ./src/foo.ts we have 2x options:
A) Tell node that the path is ./dist/foo.js and give it the compiled output of ./src/foo.ts
Resolve hook resolves to ./dist/foo.js
Load hook understands ./dist -> ./src mapping
Sourcemap must map from ./dist to ./src
B) Tell node that the path is ./src/foo.ts and give it the compiled output of ./src/foo.ts
Resolve hook resolves to ./src/foo.ts
Load hook does not need to understand any mappings
Sourcemap must map to same filename
ts-node today does (B) but does not do most of the advanced resolution we'd be implementing in this epic.
(B) is closer to deno and probably what people want. __filename and import.meta.url will refer to the actual TS file being executed. This is technically slightly different than pre-compilation execution. However, my gut tells me users will be happier with an intuitive __filename and won't mind the slight difference with pre-compilation.
Note: resolve hook is equivalent to _resolveFilename, load hook is equivalent to compile() hook
ignore with resolver
If src/index.ts is ignored and dist/index.js resolves to src/index.ts, should the resolver do that mapping?
Note: if you're here following a link from our docs about
experimentalResolver, you know that our docs for this feature are currently quite limited. Feel free to experiment and ask questions in Discussions or on Discord. This epic tracks the development work to finish all the resolver features and eventually promote them out of experimental status.Current status
Describes state of our
mainbranch, may not be published to npm yetmodule: NodeNext,esm: true, andexperimentalResolver: true. Everything works: cjs, cts, mjs, mts, with or without file extensions in import specifiers, in CommonJS or ESM filesMotivation
In TS, you import the emitted output path, and the compiler resolves this to the input source. They might have different extensions or be in different directories.
In ts-node, we should do the same.
Creating an epic to link together all the related tickets and tasks to make this happen.
Related tickets in no particular order:
Module._resolveFilenamehooks #1614How this works in TS
It's been a while since I read the TS code.
I believe that TS creates a mapping from all source files to their emit paths. It can do this because it starts with a glob of all source files, then computes the output path for each in a loop.
Aside: Does this work with files not included in a
filesarray? Does it trigger inclusion of those files in compilation? If yes, that would violate my understanding above.How it'll work in ts-node
ts-node must do the opposite of TS: we start from a runtime
require()orimportrequest, then work backwards to a source TS file. We don't know if such a source file exists; we might be importing a non-compiled.jsfile.Mappings to keep in mind:
package.jsonexportspackage.jsonmainnode_modulessearchtsconfig.jsonoutDirandrootDircorrespondencetsconfig.jsonpathstsconfig.jsonbaseUrltsconfig.jsonrootDirsSupported extensions are affected by:
resolveJsonModule
allowJs
jsx
TS >=4.5 supports mts, cts, mjs, cjs
moduleResolution or module?
break down this list by (a) those needed when resolving relative specifiers (b) those needed when resolving non-relative specifiers, such as
fooQuestions
"Resolve to source path" vs "pretend emit exists on disk"
When resolving from
./dist/foo.jsto./src/foo.tswe have 2x options:./dist/foo.jsand give it the compiled output of./src/foo.ts./dist/foo.js./dist->./srcmapping./distto./src./src/foo.tsand give it the compiled output of./src/foo.ts./src/foo.tsts-node today does (B) but does not do most of the advanced resolution we'd be implementing in this epic.
(B) is closer to deno and probably what people want.
__filenameandimport.meta.urlwill refer to the actual TS file being executed. This is technically slightly different than pre-compilation execution. However, my gut tells me users will be happier with an intuitive__filenameand won't mind the slight difference with pre-compilation.Note: resolve hook is equivalent to
_resolveFilename, load hook is equivalent tocompile()hookignorewith resolverIf
src/index.tsis ignored anddist/index.jsresolves tosrc/index.ts, should the resolver do that mapping?