Hi,
I have a question: is it possible to use ts-node/register followed by babel-core/register?
My goal is to have typescript configured to output es6, and then have babel do the es6 to es5 conversion. Thus, I would be able to use typescript interfaces where needed, but still integrate with other parts of the code using ES6 javascript.
Putting the two requires in my index.js does not raise any error, but also does not have the intended behaviour - I get different outputs if I just run node index.js, or if I manually run tsc first and then run node index.js - the difference being, IMHO, that the second case passes the js files outputed by the typescript compiler through babel, while the first case doesn't.
For reference:
require('babel-core/register');
require('ts-node/register');
require('./Tasks');
{
"compilerOptions": {
"target":"es6"
},
"files":
["Databases.ts"
,"Tasks.ts"
]
}
TypeScript can only load files with the .ts extension. Although it might be possible to hack around this by just appending .ts to all files and allowing the user to set the extension, I won't be doing that for now. Which leaves you with having to override the babel-core extensions to .ts instead. If you look at the babel docs (https://babeljs.io/docs/usage/require/), you'll see you should be able to set the extensions. However, I can not be sure whether this will work since babel may not play nicely with extensions - give it a go and feel free to ask more questions here.
Thanks for your answer!
Still, I think we're talking about different things - I don't want to pass .ts files to babel. I want to sequentialize - first pass them through ts-node/register (hence, typescript) and then, the (in-memory) output, pass it through babel.
I am now thinking this is only possible using gulp, not the register path, as that always seems to assume you ever only want to pass one decoder per file. For example rechoir makes this same assumption.
Would this be something worth adding, and additional option where you could do some post-processing (in this case, babel)?
Yes, that's what I'm talking about too. You might be confused by how registering works, since it doesn't change the file extension. So you still have .ts -> .ts -> .ts, it's transpiled in memory - there's just no change to the filename along the way. That's why it's not going to be picked up by Babel and I suggested you use the .ts extension.
I won't be adding support in ts-node for post-processing with Babel. The way it should be done is that both modules should manipulate require.extensions without overriding the previous one (wrap it instead) and then this will work. In fact, if you give it a go it _might_ already work - I just don't use Babel myself anymore to find out.
Ahh! Ok, sorry for my confusion! Got it, and I will try it, tks!
It's ok, I don't guarantee it works though. Theoretically, it should: https://github.com/babel/babel/blob/master/packages/babel-register/src/node.js#L104
It doesn't seem to work, at least on my limited testing... perhaps I could take a closer look to that babel code some day, I don't quite get how the old parameter is processed in the loader function - it seems not to be...
Still, I appreciate your help and now have a better understanding of how the tools should interact :)
@jcristovao I've solved this problem with a wrapper package, ts-babel-node.
@jcristovao I was just reviewing some require.extensions work and realised how Babel should update to support this. It might be worth making an issue with them to fix their side now, but if they use the pattern of overriding module._compile instead of overriding the whole hook, this would work flawlessly (see https://github.com/TypeStrong/ts-node/commit/44c389e73ae7c5c7f1c0068355ea9dc56a93b907#diff-f41e9d04a45c83f3b6f6e630f10117feR288 for an example). This would because the loader would be receiving the content from the _previous_ loader, whereas currently babel is doing the same thing ts-node used to which is load from the filesystem.
@blakeembrey I don't think it's quite as simple as you make it sound - take a look at what I had to do in order to shoehorn these libraries together.
@danielmoore I don't understand where you got that from, but if you read my comment above it says "if they followed the module._compile pattern this would work flawlessly". Maybe we can open an issue to fix that. So yes, it is that simple, but Babel still needs to be patched to make it work.
Edit: I see someone else made the issue already.
Right - that's what I did. But to get the ordering right (tsc → babel → node), I had to intercept your require extension, wrap it so I could wrap m._compile and be sandwiched in the middle. Then, getting source maps to follow through wasn't exactly trivial, either.
Take a look at the code and let me know if I'm doing something that could be simplified, but I don't see any way to do that.
I just did a test and got it working with minor changes to the Babel codebase. In babel-register, I did:
function loader(m, filename, old) {
var _compile = m._compile;
m._compile = function (code, filename) {
return _compile.call(m, compile(code, filename), filename);
};
old(m, filename);
}
result = babel.transform(code, ... // From `babel.transformFileSync`.
My babel-register.js, because babel-register explicitly disables babelrc:
require('babel-register')({
extensions: ['.ts'],
presets: ['es2015']
})
Finally, my tsconfig.json to verify Babel was being run (I just cheated since ES6 modules don't work on node.js):
{
"compilerOptions": {
"target": "es6",
"module": "es6",
"moduleResolution": "node"
}
}
Finally, to run this, you need to register Babel after ts-node (because compilation order):
node -r ts-node/register -r ./babel-register.js index.ts
Source maps should also _just work_, but it appears that Babel ignores the existing source map of the file when using transform. They might need to also enable a tranformSync option that works with source maps.
In summary, all the changes required are out of my control. I've done everything in my power to work with existing require loaders, but Babel does not play well. It always reads from the filesystem itself, which means (without the modifications above) ts-node can never be compiled first and pass it onto Babel.
@danielmoore Hopefully the above gives you more context, but you shouldn't need to "intercept" anything - if you follow the standard order (which is weird, has to be back to front because of the module._compile wrapper ordering - the first registered would be wrapped by the second, so on) you should be fine. The final fallback should always be to require.extensions['.js'] so that it'll read from the filesystem (node.js behaviour). That's how ts-node works - we don't do any file loading (except what's needed from TypeScript for type checking) and let node.js do the loading so, theoretically, anyone could also add a loader _before_ ts-node.
@blakeembrey, you wrote: "Edit: I see someone else made the issue already."
Can you provide a link to it. Can't find it...
@ccschneidr Sorry about that, I should have added it earlier - it might have been https://github.com/babel/babel/issues/4535. Also related to https://github.com/babel/babel/pull/3609#issuecomment-241304424.
fixed by "@babel/register"
require('ts-node/register');
require('tsconfig-paths/register');
require("@babel/register")({
cwd: __dirname,
extensions: ['.ts'],
presets: ["@babel/env"],
});
It works for me. This is how the shell command looks like:
TS_NODE_PROJECT=tsconfig_test.json BABEL_ENV=test mocha --require ts-node/register --require tsconfig-paths/register --require babel-core/register assets/js/test/unit_tests/tests/test_poseidon_navigator.ts
In the config file, you must specify the paths of the node_modules using wildcards. I had to produce babelized version of certain node_modules first. And also the following:
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
@MagicPoulp Could you give some details what you mean with "In the config file, you must specify the paths of the node_modules using wildcards"?
I just figured out a much simpler setup. Since Babel 7 supports TS, there is no need for ts-node. We can just use mocha -r babel-register-ts with .babelrc:
{
"presets": [
"@babel/preset-env",
"@babel/typescript"
]
}
Most helpful comment
I just figured out a much simpler setup. Since Babel 7 supports TS, there is no need for ts-node. We can just use
mocha -r babel-register-tswith .babelrc:https://github.com/deepsweet/babel-register-ts