It appends the .js
file extensions to the import statements
in the JavaScript files that are generated by TypeScript when it is necessary.
It aims to turn the JavasScript code output by the tsc TypeScript compiler into code that can be directly executed on the latest Node.js by performing the strict minimal amount of changes needed.
No bundling, just make it watch the outDir
of your TypeScript project and
it will automatically and transparently adjust the emitted JavaScript files.
Currently, as I explain in more details further down,
if you transpile to ESNext with "module": "ESNext"
in your tsconfig.json
,
Node.js will not be able to execute your Transpiled JavaScript code if you use relative import
statements or
some node_modules imports with sub-paths.
Just a very straightforward thing.
It appends the .js
file extensions to the top level import statements
in the JavaScript files that are generated by TypeScript.
This is quite useful, because, simply put:
-
If you want to use the
import
/export
syntax natively in Node.js you must set the property"type": "module"
in yourpackage.json
file. -
But as soon as you have set the
"type": "module"
property, Node's module resolution algorithm changes, and the new one now requires that you import your JavaScript files including their.js
extensions.I.e. writing
import foo from './foo'
won't work any longer, even if./foo.js
is a correctly defined JavaScript source file that exists, is in the right place, and exports a valid ES6 module.What you need to do instead is write
import foo from './foo.js'
.That works.
So what's the problem, you say?
-
The problem is that TypeScript, when it emits
import
statements, i.e. when you have set the"module": "ESNext"
property in yourtsconfig.json
, doesn't put the.js
extension in the transpiled JavaScript code that it generates - ever!It means that TypeScript code freshly transpiled to ESNext and using ESNext modules DOES NOT RUN ON Node.JS.
And there is no option to tweak in tsc to make it work.
Nothing.
The developers of TypeScript are closing all GitHub issues related to this problem and say that they won't move a finger.
The reasons why still elude me. If anyone could ELI5 TypeScript's position on this issue I'd be glad to hear their explanation.
It's just 3 f***ing characters to add!!
In conclusion you have to either give-up on using ESNext modules altogether,
or downlevel transpile your TypeScript to a version of JavaScript
that uses require
.
I don't want that, I want full ESNext. Including modules.
So I built this thing, it was a fun project and I've learnt a ton of stuff, and it's actually kind of useful.
What it does is replace all import
statements that lack a .js
file extension
with statements including the .js
extension, only if the corresponding .js
files
actually exist of course, and if it makes sense according to the ESM spec.
The algorithm goes something like:
- oh, here is an
import { flattenNodeTree } from './parsing/ASTUtil'
statement! - well, it does start with a
.
so it must be a relativeimport
... - ...aaaaaand it so happens that the file
./parsing/ASTUtil.js
exists!! - so let me replace
import { flattenNodeTree } from './parsing/ASTUtil'
withimport { flattenNodeTree } from './parsing/ASTUtil.js'
Voilà. Job done.
I'm using @babel/parser
to do the parsing so it should be rather robust
as I'm not just slashing in the code with approximative RegExp
s.
Just invoke the main script, whichever way you may have obtained it,
and pass it a path to a directory containing .js
files.
It will start watching and adding the .js
extensions where it thinks
it makes sense.
node node_modules/.bin/yab path/to/outDir
yarn yab path/to/outDir
# or for the cloned repo version
node dist/bin.js
You can have YAB watch the outDir
of your TypeScript
project without any issue.
I'm actually using the development version of YAB to watch the outDir
of its own
TypeScript project - I wasn't able to use modules in YAB until I go the basic features working,
it made for a large monolith up until this point.
It's a lighter alternative to a full-on webpack if you just need to transpile TypeScript and actually run it on Node.js without going through all the mess of bundling etc.
Needless to say, there are probably bugs, use at your own risk.
The "dev" script in package.json is handy: it starts tsc
and the test-runner (jest
)
in parallel and it first utilizes the stable version of yab in the dist
folder
to adjust the transpiled source of src
then starts watching a git-ignored js-unstable
dir,
which is the tsconfig.json
's "outDir".
- Currently YAB does not update the source-maps, I intend to fix it, but I do not think it should matter too much, as the only changes the tool performs are adding 2 chars at the end of some top lines