-
-
Notifications
You must be signed in to change notification settings - Fork 533
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ts-node
fails when ES Modules are in the dependency graph in Node.js 13+
#935
Comments
One way to reproduce this is to take a working application with |
related:
Now that Node 13 is out in the wild, looks like people are starting to run into this without any flags. Eventually someone will have unpaid free time to fix it, but I think it is easier to just pre-compile everything to JS before running Node. |
ts-node
isn't working now that ES Modules are in the dependency graph in Node.js 13+ts-node
fails when ES Modules are in the dependency graph in Node.js 13+
I'm trying to understand this scenario - how did this work before? I assume existing TypeScript running via Is this about existing projects breaking or about existing projects migrating from CJS-"import" to standard imports? |
Ah, I think I got it now: It's about running |
Not even by hooking in to the experimental load API? |
There's two independent holes in the API surface:
The second point is tricky because it's by design. We don't really want to allow userland code (from node's perspective) being able to switch out parts of the loading pipeline at runtime. It can lead to weird issues where the same global scope contains code expecting different module loaders and thanks to One idea brought up by @guybedford was to have an API that can create a new global scope ("context" or "realm") with all bells and whistles, including node standard library, |
I created a proof of concept for using the current version of the --experimental-loader feature to transpile typescript files right before node loads it and then give node the now ESNext Javascript code with the SyntheticModule VM feature. It's fairly hacky right now but it made for a fun 6 hour session: https://github.com/KristianFJones/TS-ES-Node |
^ Just for above there's a new loader hook in node nightly called Typescript Loaderimport fs from 'fs';
import path from 'path';
import babel from '@babel/core';
import urlUtils from 'url';
import babelPluginTransformTypescript from '@babel/plugin-transform-typescript';
function isURL(string) {
try {
new URL(string);
return true;
} catch {
return false;
}
}
const removePrivateTypes = (babel) => {
return {
visitor: {
ClassPrivateProperty(context) {
context.get('typeAnnotation')?.remove();
},
},
};
}
const BABEL_OPTS = {
plugins: [
babelPluginTransformTypescript,
removePrivateTypes,
],
parserOpts: {
plugins: [
'asyncGenerators',
'bigInt',
'classProperties',
'classPrivateProperties',
'importMeta',
'nullishCoalescingOperator',
'numericSeparator',
'objectRestSpread',
'optionalCatchBinding',
'optionalChaining',
'topLevelAwait',
'typescript',
],
},
sourceMaps: 'inline',
};
export async function getSource(urlString, context, getSourceDefault) {
if (isURL(urlString)) {
const url = new URL(urlString);
if (url.pathname.endsWith('.js') && !fs.existsSync(url)) {
url.pathname = url.pathname.replace(/\.js$/u, '.ts');
const contents = await fs.promises.readFile(url, 'utf8');
const { code: source } = await babel.transformAsync(contents, {
...BABEL_OPTS,
sourceFileName: path.basename(urlUtils.fileURLToPath(url)),
})
return { source };
}
}
return getSourceDefault(urlString, context, getSourceDefault);
}
export async function resolve(specifier, context, defaultResolve) {
try {
const defaultResolution = defaultResolve(specifier, context, defaultResolve);
try {
const url = new URL(defaultResolution.url);
url.pathname = url.pathname.replace(/\.ts$/, '.js');
return { url: url.href };
} catch {
return defaultResolution;
}
} catch {
return { url: new URL(specifier, context.parentURL).href };
}
} |
Hi, I created the |
Look's like I know what I'm doing this weekend. |
Been busy, here is a proof of concept using the |
Okay, made a few small changes and fixes. It's now able to handle external modules that have the TSLib import helper without requiring refactoring existing imports. The |
Removed the |
@KristianFJones If you're interested I'd love to land a PR in |
I'd love to give that a shot. I'll take a crack at it sometime this weekend. |
@KristianFJones I tried your fork and so far it works without issues, I also added --harmony-top-level-await to try along typescript 3.8 and it worked without issues. Hopefully it can be integrated into ts-node. Btw, I saw you state it requires node 14 (nightly), but I could use 13.7 without any issues (even TLA), anything lower than that and I start getting all kinds of errors. |
Please note that TLA may look like it's working at first glance but is known to still have some sharp edges. Both the base implementation in V8 and the integration in nodejs. See: nodejs/node#30370. So it's nice to play around with locally but things are kind of expected to break horribly at this point. It's not ready for real-world use yet. Just in case anybody is tempted to enable that flag somewhere. :) |
@ejose19 Interesting, I had been developing it with Node 13.6, didn't realize the transformSource hooks were added to 13.7. Unless you tried the one on my Account and not the new transformSource one I have on my |
It seems that
|
Well, looks like |
@KristianFJones if you can describe how we can help you here, I can ask my friends to make pull request |
can it be related to nodejs/node#32103 |
My hesitation with making a port/adaptation of TS-ESNode for |
No, I've written a full TypeScript discovery system to provide node style resolution within ESModule mode using my loader hook to automatically discover .ts,.tsx,.js,.jsx files when no existing file is provided to the hook. https://github.com/K-FOSS/TS-ESNode/blob/next/src/findFiles.ts |
The suggestion from @CaptainOfPhB actually works in some (my) cases. In the past, ts-node can work well without tsconfig.json, now it requires the mentioned settings in tsconfig.json for it to work in some projects. |
For some people who get to this point, if you add to your tsconfig.json, it allows the .js files to resolve.
file.ts
another.ts
|
It is amazing that 3 years after this was reported I am still facing this issue with no solutions tried would solve but hours spent and wasted. |
I still can't import my |
Same error, none of the above solutions worked.
|
I was encountering the same issue but following this guide and managed to solve my problem: https://www.typescriptlang.org/docs/handbook/esm-node.html However, I don't feel good about importing my project files with the .js extension |
can you show me how the code examples is? |
@menangaTech I was trying to use formadata-node, which in an ESM-only package. Here's a snippet that causes problems:
I fixed it by having
The important parts are the |
I was trying to setup this typescript for apollo-server and I was getting the same error, this is how my tsconfig.json file end up:
|
mark. |
Thanks this actually worked @erick2014 |
Please, can somebody explain for dummy how to use ts-node imports. Trivial setup:
tsconfig.json
code app/translate.ts
Error
Will really appreciate for help. |
Answer after answer on StackOverflow said to remove |
Mail.com / [email protected] / [email protected] |
Any update on this? |
Any update? |
You better stop using ts-node and start using tsx unless you really need to use the emit decorators metadata option of typescript if you're using something like TypeORM (you can still use tsx though). |
I get it that the error is because some dependency became an ES Module when previously was CommonJS. I have a funny situation now that a project works, but if I remove package-lock.json and reinstall, it stops working. Clearly some sub-dependency has changed into ESM. Is there an easy way to detect which one it is so I can pinpoint it's version and maybe let the authors know? |
@achlubek Almost always when I hit this message if you look very carefully at the error you receive it will allow you to track down which specific module is causing the problem. |
Yeah I managed to track it down to Chai v5 becoming ESM only. Too bad eh! |
Any recommendation about using tsx with emitDecorators? Im having troubles with that. |
tsx can't emit decorators, and I don't think it's possible (during development) |
I fixed this by running Once again, ES Modules are the dumbest thing to ever happen to the JS ecosystem |
I basically detailed the issue in this comment: #155 (comment)
It's a chicken-and-egg-like problem:
module: 'commonjs'
, then if any TS files import ES Modules (indirectly in their dependency graph), then Node throws an error because CommonJS modules can not import ES Modules.module: 'esnext'
, then the errors from the previous point go away, but now the.js
file that is loadingts-node
and calling something likerequire('typescript-entry-point.ts')
will have a similar issue, because the call torequire('typescript-entry-point.ts')
will try to load an ES Module..js
file into an ES Module, we can not convertrequire('typescript-entry-point.ts')
intoimport 'typescript-entry-point.ts'
because now ES Modules don't handle.ts
extensions (at least not out of the box, and it seems the old require hooks don't operate on these new identifiers)At the moment, I'm sort of stuck, because I have dependencies in my dependency tree that are ES Modules.
The only workaround I can think of is to compile everything to
.js
files (ES Modules) and avoid to use ts-node.I wonder if a combination of
allowJs
andignore
so that it compiles JS files would help. I haven't tried that yet.The text was updated successfully, but these errors were encountered: