Just to be clear: this proposal is not to make --es-module-specifier-resolution=node a default
Backstory
Recently there have been a lengthy discussion I've started in order to make package.json of ES6 modules more simple. Probably the biggest takeaway from this is that without extensionless imports in some form, in order to create backward-compatible packages to support subpath imports like import first from 'lodash/first' they need to create long list of entrypoints in exports field of package.json which needs to be passed around everywhere.
This issue combines with obvious status quo that: many developers are used to, many existing applications already use, and many transcompilers (like typescript) application boilerplates (like create-react-app) generate extensionless imports in code: #323 (comment)
Probably the biggest "cons" against extensionless imports I've heard so far is that they are slow as they require checking few locations in filesystem in order to determine where the file is which is relevant if we want to use HTTP/2 streaming imports of non-transpiled JS files. But this seems an issue with --es-module-specifier-resolution=node which is only one solution to this problem.
Proposed solution(s)
Solution one:
If extensionless file is imported, assume .js extension.
import App from "./App" always imports "./App.js"
import App from "./App.js" always imports "./App.js"
import App from "./App.mjs" always imports "./App.mjs"
import App from "./App.cjs" always imports "./App.cjs"
This seems performant, accounts for status quo, and also seems to solve issue of complicated package.json for modules that support importing subpaths if exports is made stable.
For example this could be package.json of lodash package (please note that exports is not a part of this proposal, it is already a proposed and implemented thing)
{
"main": "index.js",
"exports": {
"require": "./",
"default": "./src/"
}
}
require('lodash/first') in old node loads CJS file lodash/first.js with old resolution rules
require('lodash/first') in new node loads CJS file lodash/first.js because of require in exports (or old resolution rules if you decide to not change this behavior)
import first from 'lodash/first' loads ES6 file lodash/src/first.js because of default in exports combined with the new resolution rule (default .js extension)
Solution two:
If extensionless file is imported, assume imported extension has the same extension as current file.
This means that:
- extensionless imports in
.js files wil import .js by default
- extensionless imports in
.mjs files wil import .mjs by default
- extensionless imports in
.jsx files wil import .jsx by default
- extensionless imports in
.cjs files wil import .cjs by default
etc. etd... Seems less opinionated but also more complicated
Just to be clear: this proposal is not to make
--es-module-specifier-resolution=nodea defaultBackstory
Recently there have been a lengthy discussion I've started in order to make
package.jsonof ES6 modules more simple. Probably the biggest takeaway from this is that without extensionless imports in some form, in order to create backward-compatible packages to support subpath imports likeimport first from 'lodash/first'they need to create long list of entrypoints inexportsfield ofpackage.jsonwhich needs to be passed around everywhere.This issue combines with obvious status quo that: many developers are used to, many existing applications already use, and many transcompilers (like typescript) application boilerplates (like create-react-app) generate extensionless imports in code: #323 (comment)
Probably the biggest "cons" against extensionless imports I've heard so far is that they are slow as they require checking few locations in filesystem in order to determine where the file is which is relevant if we want to use HTTP/2 streaming imports of non-transpiled JS files. But this seems an issue with
--es-module-specifier-resolution=nodewhich is only one solution to this problem.Proposed solution(s)
Solution one:
If extensionless file is imported, assume
.jsextension.import App from "./App"always imports "./App.js"import App from "./App.js"always imports "./App.js"import App from "./App.mjs"always imports "./App.mjs"import App from "./App.cjs"always imports "./App.cjs"This seems performant, accounts for status quo, and also seems to solve issue of complicated package.json for modules that support importing subpaths if
exportsis made stable.For example this could be package.json of
lodashpackage (please note thatexportsis not a part of this proposal, it is already a proposed and implemented thing)require('lodash/first')in old node loads CJS filelodash/first.jswith old resolution rulesrequire('lodash/first')in new node loads CJS filelodash/first.jsbecause ofrequireinexports(or old resolution rules if you decide to not change this behavior)import first from 'lodash/first'loads ES6 filelodash/src/first.jsbecause ofdefaultinexportscombined with the new resolution rule (default .js extension)Solution two:
If extensionless file is imported, assume imported extension has the same extension as current file.
This means that:
.jsfiles wil import.jsby default.mjsfiles wil import.mjsby default.jsxfiles wil import.jsxby default.cjsfiles wil import.cjsby defaultetc. etd... Seems less opinionated but also more complicated