index.js
import {a} from './a'
console.log(a)
a.js
export const a = 3
Using Node.js 13.2 node index.js
(node:76399) ExperimentalWarning: The ESM module loader is experimental.
internal/modules/esm/default_resolve.js:94
let url = moduleWrapResolve(specifier, parentURL);
^
Error: Cannot find module /Users/daniel/code/node-esm/a imported from /Users/daniel/code/node-esm/index.js
at Loader.resolve [as _resolve] (internal/modules/esm/default_resolve.js:94:13)
at Loader.resolve (internal/modules/esm/loader.js:74:33)
at Loader.getModuleJob (internal/modules/esm/loader.js:148:40)
at ModuleWrap.<anonymous> (internal/modules/esm/module_job.js:41:40)
at link (internal/modules/esm/module_job.js:40:36) {
code: 'ERR_MODULE_NOT_FOUND'
}
@gengjiawen file extension and directory resolution are no longer supported in the esm loader. If you put the full filename it will work
index.js
import {a} from './a.js'
console.log(a)
There are a number of reasons people on the Modules Team like this change including better performance and web compatibility.
There is support for the former algorithm with a flag --es-module-specifier-resolution=node.
There is still some discussion being had about the final behavior we'll land on with this.
I'm going to close this but please feel free to keep the conversation going.
There is two downside in this
ts generate js code without the extension, so if I change ts output to "module": "esnext", it won't work.
Also it's quite common to use without extension, like react or vue. I think this will bring much confusion.
IMO, what we want to see in the future should take precedence over current practices. Less magic and less divergence from browsers are benefits of not doing this.
Referencing proposal that doesn't involve "magic" or --es-module-specifier-resolution=node and solves this issue: https://github.com/nodejs/modules/issues/444
ts generate js code without the extension
IIRC TS does work fine with the extension, it just doesn't add extensions not present in the source. The following should be accepted by tsc:
// a.ts
import b from './b.js';
// b.ts
export default 42;
TSC should be smart enough to figure out that you meant b.ts even though the import is using the target filename.
What I mean is generated code by tsc

Right, if you use the extension in your typescript, typescript will generate code that has the extension. :)
When using typescript, adding the extension only works if you're working with js files. You can't import from './a.ts'
@jcjolley -- in your typescript source, when importing another typescript file, use the .js extension, and it will work
that is, when importing b.ts, the code in a.ts should look like this:
import * as b from "b.js"
the import path reflects what ends up in the compiled output, not the path to the typescript source
the import path reflects what ends up in the compiled output, not the path to the typescript source
See https://github.com/microsoft/TypeScript/issues/35589#issuecomment-577035970 and the comments preceding it.
Most helpful comment
There is two downside in this
Typescript support
ts generate js code without the extension, so if I change ts output to
"module": "esnext", it won't work.Common FE writing
Also it's quite common to use without extension, like react or vue. I think this will bring much confusion.