Enforce file extension in import #380
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
From the changeset:
History of extension-less imports:
require()
paths it will look in several places for the module, until it finds one:ex:
require('./foo')
checks for:./foo
./foo.js
./foo.json
./foo.node
./foo/package.json
withmain
field./foo/index.js
require()
in browsers with bundlers (Browserify and then Webpack)import
/export
, by using the same internal mechanisms asrequire()
, meaning that they preserved the resolution algorithm used byrequire()
.import './foo'
resolves like<a href="./foo"
). There is no mechanism for rewriting paths like./foo
->./foo.js
(except if the server provides redirects)parcel
,esbuild
,rollup-plugin-node-resolve
,typescript
) followed what webpack and browserify did, attempting to minimize compatibility issues when switching between bundlers.require
thanimport
.import
statements by settingmoduleResolution
tonode12
. When that option is set, TypeScript will no longer resolveimport './foo'
to./foo.js
or./foo.ts
, and it will throw an error (same as Node). The TS team decided that in order to import a file calledfoo.ts
, you must use a.js
import:import './foo.js'
, even though the filefoo.js
does not exist. TS will not let you useimport './foo.ts'
, even though./foo.ts
exists. The rationale is that if TS was to rewrite the import path during code emit from.ts
to.js
, it would be a functionality change to the JS code, which they avoid with a ten-foot pole. (I don't think this decision was the right one, but it is what it is).In other words, syntax like
import './foo'
is syntax that was never really valid/specified. It is a mishmash of the ES import syntax and the CommonJS module specifier syntax.require('./foo')
is valid andimport './foo.js'
is valid.To help move our projects towards supporting native-node ESM and native-browser ESM, this PR enforces the use of file extensions in all path imports. That means it will switch
require('./foo')
torequire('./foo.js')
even though that change is not needed. The goal is consistency across all imports/requires, even in projects that use CommonJS or a bundler that shims in CommonJS resolution into ESM.