Description
Intro
Since the previous discussion was successful in gathering feedback from the community, and I believe there is some consensus that this feature is something that the project wants to add, I want to summarize what are going to be the next steps and some technical solutions.
To be honest I'm very glad for the amount of feedback I received, it helped me to change my view on some aspects that I previously ignored.
Before I go into technical implementation, this is what the goals of this feature should be in my opinion:
Caution
These are long term goals and next steps, they might not reflect the current status.
- Make sure Node.js can support TypeScript with the stability guarantees that has always offered.
- Avoid breaking the ecosystem, this means creating a new "flavor" that only works on Node.js.
- Performance.
- Keeping it simple, we don't support everything, we want to user land tools to work in sync with Node, we provide the foundations to build on top.
- Type stripping is the way to go.
Step 1: Initial implementation
The initial implementation is the proof of concept that I have created to gather feedback and consensus from the project collaborators.
It's very far from being perfect but it establishes some of the points we want to move forward with.
Current limitations:
- No support forTypeScript features that require transformation (Enums, namespace, etc...).
- No
.js
extension for.ts
files. - No running TypeScript in
node_modules
. - No source maps, but not needed because we perform whitespacing (replace removed code with white space).
Important
Why no support for TypeScript features such as enums, namespaces, decorators, etc...?????
- In the initial implementation I decided to start with the smallest subset possible.
- Adding transformation means supporting source-maps so is more work.
- Supporting features means adding more instability to the TypeScript syntax we support, without having a way to stay up to date with new features. As a general rule of thumb, the more you support the more likely to have breaking changes. This is a great challenge to overcome because we need to support transformations and new features but respect Node stability guarantees
Step 2: Decoupling
- Amaro as standalone loader feat: create amaro loader amaro#47
There is already a precedent for something that Node.js support, that can be upgraded seperately, its NPM.
Node bundles a version of npm that can upgraded separately, we could do the same with our TypeScript transpiler.
We could create a package that we bundle but that can also be downloaded from NPM, keep a stable version in core, but if TypeScript releases new features that we don't support or breaking changes, or users want to use the new shiny experimental feature, they can upgrade it separately.
This ensures that users are not locked, but also provides support for a TypeScript version for the whole 3 years of the lifetime of Node.js release.
Getting started
Create a new package called amaro
that wraps swc
with the current implementation. This package, which is released on npm, offers the same api that is currently used by Node, and it can be upgraded separately. The first challenge would be setup the project, make sure it can be upgraded by running npm install amaro@vX.Y.Z
Increase features
-
Enable support for TypeScript features that require transformation (Enums, mamespaces), now that we are decoupled we can expand on the amount of feature we support.
Caution
Currently there is consensus that Node.js should NOT run TypeScript files inside `node_modules.
It is not supported to avoid package maintainers to release TS only package.
Thanks @joyeecheung and @legendecas for the idea 💡
Step 3: Make it fast
With the project up and running, we can now start thinking about performance.
We could vendor SWC in Rust, build our own wasm, or compile it to static libraries that we could use in core, c++ rust FFI, etc...
I'm not a performance expert, but I think it is possible to optimize the interaction between Node and SWC and make it performant, without impacting the Node build process.
This is the phase where we measure the performance and make it usable in production without performance penalties.
Step 4: Add more features
- Amaro as Loader feat: create amaro loader amaro#47
- Rewrite .ts => to .js supported by TS 5.7 Rewrite relative import extensions with flag microsoft/TypeScript#59767
Wont Fix
- I strongly believe we should not support
.js
extension for.ts
files.
The reason is that the compiler/bundler should be responsible to resolve the correct extension at compile time.
At runtime, the extension must be correct, it doesn't make sense to add overhead in production when it can be solved during development. Discussion happening here: Import specifiers in--experimental-strip-types
#214.
This is also supported by TypeScript from 5.7 Rewrite relative import extensions with flag microsoft/TypeScript#59767 - Support
tsconfig
directly
This would mean to run with typescript to support the latest flavors etc... Compilation and type checking should be done by user land tools during development.