Please use this ticket to provide feedback on our native ESM support, which is currently experimental. Your involvement is greatly appreciated to ensure the feature works on real-world projects.
Node's loader hooks are EXPERIMENTAL and subject to change. ts-node's ESM support is also experimental. They are likely to have breaking changes in the near future. You have been warned!
"module": "ESNext" or "ES2015" so that TypeScript emits import/export syntax."type": "module" in your package.json, which is required to tell node that .js files are ESM instead of CommonJS. To be compatible with editors, the compiler, and the TypeScript ecosystem, we cannot name our source files .mts nor .mjs.npm install ts-node or yarn add ts-nodeimport statements, or pass --experimental-specifier-resolution=node Idiomatic TypeScript should import foo.ts as import 'foo.js'; TypeScript understands this.
node --loader ts-node/esm ./my-script.ts
You must invoke node directly because there is no way for us to enable our loader hook at runtime. Be aware this imposes some limitations on our implementation.
tsconfig will be resolved relative to process.cwd(). Specify ts-node options in your tsconfig file. For details, see our README.
Environment variables and ts-node CLI flags are not parsed. We may enable one or both in the future.
During this experimental phase, all changes to ESM support, including breaking changes, will be released as minor or patch versions, NOT major versions. This conforms to semantic versioning's philosophy for version numbers lower than 1.0. Stable features will continue to be versioned as normal.
process.argv for config resolution?require('ts-node').esmImport(module, 'import-path')Below is the official proposal, explaining our implementation in detail.
I am asking node's modules team questions here: https://github.com/nodejs/modules/issues/351
I was reading the threads about ESM support in ts-node, e.g. #935.
The @K-FOSS/TS-ESNode implementation is unfortunately incomplete; it does not attempt to typecheck. (it uses transpileModule)
So I did some research. Below is a proposal for ESM support in ts-node, describing the required behavior in detail.
This doesn't feel like an urgent feature to me, but I like having an official proposal we can work on.
node --loader ts-node/esm ./entrypoint.ts
Cannot be invoked as ts-node because it requires node flags; hooks cannot be enabled at runtime. This is unavoidable.
For simplicity, --require ts-node/register can be eliminated, because ts-node/esm automatically does that.
Alternatively, we publish an experimental ts-node-esm entry-point which invokes a node subprocess.
Don't forget allowJs! Affects the treatment of .js files. (Not .mjs nor .cjs because the TS language service won't look at them)
Must implement ESM hooks to resolve extensionless imports to .ts files, resolve .js to .ts, classify .ts(x) and .jsx files as CJS or MJS, and compile .ts(x) and .jsx files.
resolve() hook:Match additional file extensions: .ts, .tsx, .jsx.
Resolve .ts, .tsx, and .jsx if the import specifier says .js. Obey preferTsExts when doing this.
_
[Good idea?] Always ask default resolver first. If it finds something, we should not interfere.
--experimental-specifier-resolution=node does not obey require.extensions, unfortunately, so we can't use that.
getFormat hook:If the resolved file is .ts, .tsx, or .jsx, behave as if extension was .js: use node's package.json discovery behavior to figure out if ESM or CJS.
This can be accomplished by appending .js to the URL path and delegating to built-in getFormat hook.
transformSource hook:Same as today's code transformer. Relies on projects to be configured correctly for import/export emit.
require() hookgetFormat logic to determine if node will treat file as CJS or ESM.require.resolve points to a .ts file, we need to make the determination.require() code transformimport() calls.require('ts-node').esmImport(module, 'import-path')?ts-node bin entry-pointts-node CLI does NOT need to support import()ing ESM.
WHY? Because ESM hooks are an experimental feature which must be enabled via node CLI flag.
Thus we will be loaded via --require, and Node is responsible for loading the entry-point, either triggering our hook or our require.extensions.
import() in CJSIf "module": "commonjs", compiler transforms import() into __importStar
No way to change this without a custom transformer, which IMO is too much complexity at this time.
Users should run their code as ESM.
If they can't do that, we can recommend the following workaround:
// This is in a CommonJS file:
const dynamicallyImportedEsmModule = await require('ts-node').importESM('./specifier-of-esm-module', module);
NOTE we have not implemented the following, although initially I thought we might. Instead, we assume tsconfig is configured for either ESM or CJS as needed
We could intelligently emit both "module": "esnext" and "module": "commonjs" depending on the classification of a file.
In transpile-only mode this is simple. Call transpileModule with different options.
When typechecking, we can pull SourceFile ASTs from the language service / incremental compiler.
We'll need a second compiler, one for each emit format. Or we can hack it by using transpileModule for all ESM output. transpileModule is incompatible with certain kinds of TS code, (can't do const enums) but it might work for a first-pass implementation.
TODO: turns out, users can tell the language service to include the .js file extension with automatically-written imports. So we do not need to automatically add them, though we do need to check if a .js import might point to a .ts or .tsx file.
The option is passed to the language service in a ts.UserPreferences object.
https://discordapp.com/channels/508357248330760243/640177429775777792/703301413337432114
I was trying to figure out if ts-node needs to automatically switch the "module" option between CommonJS and ESNext depending if we need to emit CommonJS or ESM. I concluded we do not want to do this. Here's an explanation anyway, in case I am proven wrong.
Today, ts-node respects the tsconfig's module option. Users are required to set it appropriately. If the user incorrectly sets module to ESNext and then tries to require() a TS file, they get an error because the emitted code has import statements.
Alternatively, we can automatically override the module option to be CommonJS when emitting for require() and ESNext when emitting for ESM. This allows a single tsconfig to be used for both ESM and CommonJS.
After thinking about this, it doesn't make sense. Users will choose either ESM or CommonJS via their package.json file. They won't do a mix of both. Also, this would get pretty messy since we'd be doing something that doesn't match tsc's output.
Nevertheless, if we wanted to implement this:
If the module option is already correct, we can use the languageService's getEmitOutput() like we do today. If not, we can grab a reference to the SourceFile and transform it using the same technique as transpileModule's implementation. This allow custom emit while avoiding an expensive parse.
TypeScript has an internal sourceFileAffectingCompilerOptions array. If any of those options differ, a SourceFile cannot be reused. However, some are only relevant if you care about diagnostics. For swapping out the module flag, I think SourceFile can always be reused.
We have released an experimental implementation of this in v8.10.1. Please test and share your feedback here.
Thanks @cspotcode for the release! Everything seems be working minus one snafu. Importing named exports don't seem to be working, but this may be a Node module quirk. For example in index.ts:
import { graphql } from 'graphql';
will cause a syntax error of:
SyntaxError: The requested module 'graphql' does not provide an export named 'graphql'
but this can be solved by using destructuring:
import Graphql from 'graphql';
const { graphql } = Graphql;
Any way to support importing named exports in ts files?
@chpeters I'd guess that would be because graphql is actually CommonJS and not an ES module. You can read more about it here: https://nodejs.org/api/esm.html#esm_interoperability_with_commonjs. Unfortunately it'll probably be messy for a while with TypeScript since the imports syntax is overloaded to represent both CommonJS and native ES modules.
Using mocha and TypeScript with ES modules I am facing an issue and I don't quite understand it.
Running this cmd as my test cmd :
node --experimental-modules --loader ts-node/esm.mjs ./node_modules/mocha/bin/mocha --extension ts
I get this error :
import './unit/authentication.js';
^^^^^^
SyntaxError: Cannot use import statement outside a module
What did I do wrong ?
PS: I have my tsconfig.json module attribute set to "ES2015", my package.json type attribute to "module", ts-node installed locally
Please send me a minimal reproduction and I'll be able to tell you.
On Fri, May 8, 2020, 12:01 PM Julien Collard notifications@github.com
wrote:
Using mocha and TypeScript with ES modules I am facing an issue and I
don't quite understand it.Running this cmd as my test cmd :
node --experimental-modules --loader ts-node/esm.mjs ./node_modules/mocha/bin/mocha --extension ts
I get this error :
import './unit/authentication.js';^^^^^^
SyntaxError: Cannot use import statement outside a moduleWhat did I do wrong ?
PS: I have my tsconfig.json module attribute set to "ES2015", my
package.json type attribute to "module", ts-node installed locally—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-625886264,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAC35OCOQXHWRG4OZI2UC63RQQUFZANCNFSM4MGJCWPA
.
This is my project architecture :
|_index.ts
test
|_tests.ts
|_unit
|_authentication.ts
package.json
tsconfig.json
My package.json :
{
"name": "my-project",
"version": "1.0.0",
"description": "My project",
"main": "lib/index",
"type": "module",
"files": [
"lib/**/*"
],
"directories": {
"test": "test"
},
"scripts": {
"build": "tsc",
"test": "node --experimental-modules --loader ts-node/esm.mjs ./node_modules/mocha/bin/mocha --extension ts"
},
"devDependencies": {
"@types/chai": "^4.2.11",
"@types/mocha": "^7.0.2",
"@types/node": "^13.13.5",
"chai": "^4.2.0",
"mocha": "^7.1.2",
"ts-node": "^8.10.1",
"typescript": "^3.8.3"
}
}
My tsconfig.json :
{
"compilerOptions": {
"target": "ES2015",
"module": "ES2015",
"lib": ["es6"],
"declaration": true,
"outDir": "lib",
"rootDir": "src",
"strict": true,
"noImplicitAny": true,
"moduleResolution": "node",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
},
"exclude": [
"test/"
]
}
My test/tests.ts :
import './unit/authentication.js'
Typescript is building my files right.
The npm run test cmd returns throw the error I wrote before.
Do you need more context ?
@NeilujD this is perfect, thanks.
It looks like, due to missing features in node's ESM support, mocha is using a hack to figure out whether a file should be loaded as ESM or CJS.
https://github.com/mochajs/mocha/blob/master/lib/esm-utils.js#L4-L23
ts-node's require() hook will need to be updated to match the error behavior of node's .js hook. When you try to require() a TS file that should be treated as ESM, we should throw an error.
At first I thought mocha could simply import() everything, since it automatically switches to CommonJS loading as needed. However, that would require our ESM hook to be installed in order to resolve and classify .ts files. They're forced to use require() to cater to legacy require() hooks.
I think I can hack this by delegating to node's built-in .js extension, passing a fake filename.
require.extensions['.js']({_compile(){}}, filename + 'DOESNOTEXIST.js')
At the cost of a failed fs call, this will cause the built-in require hook to do its package.json lookup and see if the file should be treated as CommonJS or ESM.
> require.extensions['.js'].toString()
'function(module, filename) {\n' +
" if (filename.endsWith('.js')) {\n" +
' const pkg = readPackageScope(filename);\n' +
" // Function require shouldn't be used in ES modules.\n" +
" if (pkg && pkg.data && pkg.data.type === 'module') {\n" +
' const parentPath = module.parent && module.parent.filename;\n' +
" const packageJsonPath = path.resolve(pkg.path, 'package.json');\n" +
' throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath);\n' +
' }\n' +
' }\n' +
" const content = fs.readFileSync(filename, 'utf8');\n" +
' module._compile(content, filename);\n' +
'}'
EDIT I've shared this hack with the node folks https://github.com/nodejs/modules/issues/351#issuecomment-625992425 to see if they have any interest in exposing this API natively.
Responding to https://github.com/TypeStrong/ts-node/issues/1007#issue-598417180
Cannot be invoked as ts-node because it requires node flags; hooks cannot be enabled at runtime. This is unavoidable.
Actually... it should be possible, by providing a very simple posix shell script wrapping the whole thing. That would be ideal when using shebangs, as it's not allowed to pass options to shebangs in Linux (although it's possible in Mac).
@castarco the problem is cross-platform support that matches the npm ecosystem without adding complexity to ts-node. Typically this is handled by the package manager: npm, yarn, pnpm, etc.
We set up our package.json "bin" field, pointing to a file with a shebang, and it takes care of creating a symlink, .cmd shim, and .ps1 shim.
@NeilujD The mocha issue you were seeing should be fixed by #1031 which has been merged to master.
If you're feeling adventurous, you can install ts-node directly from master.
npm i TypeStrong/ts-node#master --save
Or you can download and install the tarball artifact produced by CI.


npm install ts-node-packed.tgz --save
FWIW I tried it out and got two issues one undocumented issue:
TS2691: An import path cannot end with a '.ts' extension. Consider importing '../../src/index' instead). I used a @ts-ignore here, and then was able to import that module, sortof.TS7016: Could not find a declaration file for module 'tcp-port-used'. 'C:/Users/Matt/Documents/dev/http-server-group/node_modules/tcp-port-used/index.js' implicitly has an 'any' type.
Try 'npm install @types/tcp-port-used' if it exists or add a new declaration (.d.ts) file containing 'declare module 'tcp-port-used';' even though I have those packages sitting in my node_modules, and I don't get this error when I build with tsc.Anyways, just thought I would give some feedback since it was solicited. Keep up the great work @cspotcode!!
@zenflow thanks, much appreciated.
Including file extensions is tricky; you need to include them in the way that typescript wants, which is to include the .js extension, not the .ts extension. This comment explains precisely why TypeScript does things this way. It has to do with maintaining compatibility with the pre-compiled code scenario. https://github.com/nodejs/modules/issues/351#issuecomment-621257543
I've also been trying to get mocha to work with this and I've been following the rabbit hole of https://github.com/mochajs/mocha/issues/4267
master works for me, but to have a usable setup I also needed esModuleInterop enabled and I had to run tests with
node --experimental-modules --loader ts-node/esm.mjs node_modules/mocha/lib/cli/cli.js src/**/*.spec.ts
Calling into the mocha internals is a bit ugly, and probably breaks some features but doesn't require patching node_modules at least.
@thatsmydoing Thanks for sharing your experience; I'm sure it will help other people, too.
You should be able to omit --experimental-modules because it is implied by --loader.
We merged #1028 a few days ago, so you should be able to omit the .mjs extension if you want.
This simplifies things a bit:
node --loader ts-node/esm node_modules/mocha/lib/cli/cli 'src/**/*.spec.ts'
Unfortunately, node has a lot of work to do before this feels clean. There's no way for us to load our hooks at runtime, which would allow us to perform the equivalent of --loader on your behalf. There's also no good system for composing multiple loader hooks together. Right now, ts-node's hook is doing extra work that ideally should be handled by a robust hook composer.
Thanks for your time on this! Works great, except I seem to be losing the source map support.
For a two-line test.ts:
type Foo = string;
throw new Error("Oh no!");
(without "type": "module" in package.json)
ts-node test.ts
...
Error: Oh no!(test.ts:2:7) 👍
(with "type": "module" in package.json)
node --loader ts-node/esm.mjs ./test.ts
...
Error: Oh no!(test.ts:1:7) 😢
Is there any easy fix?
@akbr Good catch, thanks. It looks like files loaded as ESM have file:// URLs instead of paths.
We install source-map-support here:
https://github.com/TypeStrong/ts-node/blob/master/src/index.ts#L445-L451
It's a third-party library that handles sourcemaps automatically, rewriting stack traces. We give it access to a cache of TypeScript's compiler output so it can get the sourcemaps. But we're not handling file:// URLs correctly, so source-map-support is not able to get access to the sourcemaps.
Created #1060 to track this. If you feel like sending us a bugfix, that'd be awesome!
thanks for the this initiative.
I experiment esm and cjs feature
heres my library package.json
"type": "module",
"main": "./cjs/my-lib.js",
"module": "./my-lib.js",
"typings": "./my-lib.d.ts",
"exports": {
"require": "./cjs/my-lib.js",
"import": "./my-lib.js"
},
if i combine esm and cjs in my code to call the "my-lib" it always use the cjs
import { addNumber } from 'my-lib'
const lib = require('my-lib')
console.log(lib.addNumber(1,2) + addNumber(1,2))
my tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"moduleResolution": "node",
"module": "commonjs",
"target": "es2018",
"allowJs": true
}
}
by this configuration and example it is possible to always call or use the esm module or
when there require it uses the cjs then when there import it use the esm?
@aelbore Since your tsconfig.json file has module: "CommonJS", it will always require(…) the CJS files.
To import the ESM files, you need to set module: "ES2015", module: "ES2020" or module: "ESNext".
Also, you need to construct the require function in ESM modules yourself if you intend to use it:
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
Is there an easy answer to why __dirname is undefined? Easy to work around but feels unusual. Maybe it could be polyfilled as
import path from 'path';
const __dirname = path.dirname(new URL(import.meta.url).pathname);
As I saw here: https://techsparx.com/nodejs/esnext/dirname-es-modules.html
node --loader ts-node/esm.mjs ./ssr.ts
(node:117914) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
ReferenceError: __dirname is not defined
at new FileResourceLoader (file:///home/today/_/work/ssr.ts:11:43)
at file:///home/today/_/work/ssr.ts:26:24
at ModuleJob.run (internal/modules/esm/module_job.js:110:37)
at Loader.import (internal/modules/esm/loader.js:179:24)
Check node's docs which explain the differences between the ESM and CJS
contexts.
On Thu, Jun 11, 2020, 7:47 PM Gen Hames notifications@github.com wrote:
Is there an easy answer to why __dirname is undefined? Easy to work
around but feels unusual. Maybe it could be polyfilled asimport path from 'path';
const __dirname = path.dirname(new URL(import.meta.url).pathname);As I saw here: https://techsparx.com/nodejs/esnext/dirname-es-modules.html
node --loader ts-node/esm.mjs ./ssr.ts
(node:117914) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Usenode --trace-warnings ...to show where the warning was created)
ReferenceError: __dirname is not defined
at new FileResourceLoader (file:///home/today/_/work/ssr.ts:11:43)
at file:///home/today/_/work/ssr.ts:26:24
at ModuleJob.run (internal/modules/esm/module_job.js:110:37)
at Loader.import (internal/modules/esm/loader.js:179:24)—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-642985145,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAC35OEQUO7UIWHDS4ET43TRWFUHNANCNFSM4MGJCWPA
.
@cspotcode is there a way to use transpile-only mode? I'm trying to run this in a ts-node-dev way and closest I've got is running nodemon along with -x "node --loader ...", but it's still throwing in type errors instead of just ignoring them (like tsnd does)
Set it in your tsconfig file. The SchemaStore schema for tsconfig, which
is used for tabcompletion in modern editors, includes the ts-node options.
Environment variables should work, too
On Sat, Jun 20, 2020, 7:22 PM ejose19 notifications@github.com wrote:
@cspotcode https://github.com/cspotcode is there a way to use
transpile-only mode? I'm trying to run this in a ts-node-dev way and
closest I've got is running nodemon along with -x "node --loader ...", but
it's still throwing in type errors instead of just ignoring them (like tsnd
does)—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-647056399,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAC35OGBGLLUZ7KIJA454CDRXVAFBANCNFSM4MGJCWPA
.
@cspotcode It worked, thanks! trying this on a non-prod project and so far is has been good, excluding the annoyances of using destructured imports, but besides from that all is working (even TLA), will try to integrate jest as well.
Scripts if anyone is interested for faster testing:
"scripts": {
"start": "node --loader ts-node/esm --experimental-specifier-resolution=node --experimental-top-level-await --no-warnings src/server.ts",
"dev": "nodemon -q -e ts,js,json -x npm start"
}
@cspotcode , node-esm-resolve-implementation doesn't support '--experimental-specifier-resolution=node' flag specified trough NODE_OPTIONS env variable because it looks into process.execArgv. Issue can be fixed via '-r module-which-modifies-process-exec-argv.js", but it's really ugly hack.
@VladimirGrenaderov Thanks, can you file this as an issue to help track implementation of a fix?
@cspotcode looks a bit confusing:
code from my node_modules (latest version, 8.10.2) - line commented:

https://github.com/TypeStrong/ts-node/blame/master/raw/node-esm-resolve-implementation.js


Somebody change the code before publish?
Because https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz contains commented line too.
Make sure you're looking at the correct version tag in GitHub, matching the
npm version you're looking at.
On Sat, Jul 4, 2020, 2:36 PM Vladimir Grenaderov notifications@github.com
wrote:
@cspotcode https://github.com/cspotcode looks a bit confusing:
code from my node_modules (latest version, 8.10.2) - line commented:
[image: image]
https://user-images.githubusercontent.com/20106607/86518838-42219100-be3d-11ea-8406-1031ed5e382c.pnghttps://github.com/TypeStrong/ts-node/blame/master/raw/node-esm-resolve-implementation.js
[image: image]
https://user-images.githubusercontent.com/20106607/86518851-6a10f480-be3d-11ea-8f0f-bfe7ed97b474.png
[image: image]
https://user-images.githubusercontent.com/20106607/86518861-79903d80-be3d-11ea-9bdd-7a3278c76a31.pngSomebody change the code before publish?
Because https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz contains
commented line too.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-653798204,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAC35OEEFUYBF6TT2ZU3LU3RZ5ZDJANCNFSM4MGJCWPA
.
yarn info


npm view

Looks like tarball url is correct.
Tag v8.10.2 - https://github.com/TypeStrong/ts-node/blob/v8.10.2/raw/node-esm-resolve-implementation.js, line is not commented:

Make sure you're looking at the correct git tag.
On Sat, Jul 4, 2020, 2:49 PM Vladimir Grenaderov notifications@github.com
wrote:
yarn info
[image: image]
https://user-images.githubusercontent.com/20106607/86519048-aba29f00-be3f-11ea-8c2a-de905106aef6.png
[image: image]
https://user-images.githubusercontent.com/20106607/86519068-ddb40100-be3f-11ea-9ae0-1a8f2cf48b82.pngnpm view
[image: image]
https://user-images.githubusercontent.com/20106607/86519081-06d49180-be40-11ea-8690-9eedc61fdccb.pngLooks like tarball url is correct.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-653799222,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAC35OBUC362GGC4MVWPRHDRZ52RZANCNFSM4MGJCWPA
.
@cspotcode yep I catch it - dist-raw/node-esm-resolve-implementation.js & raw/node-esm-resolve-implementation.js are different. Thx, but what does it mean? Just experimental garbage?
As you asked, new issue - https://github.com/TypeStrong/ts-node/issues/1072.
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-631205443
The node --loader ts-node/esm node_modules/mocha/lib/cli/cli 'src/**/*.spec.ts' is not working in this case because the esm.mjs file has require for
const esm = require('./dist/esm')
but the folder dist doesn't exist when it is "installed" from TypeStrong/ts-node#master
A small hack is going to node_modules/ts-node and type npm install + npm run build-nopack
But if user doesn't specifiy --experimental-specifier-resolution=node the ts-node will start complaining about ERR_MODULE_NOT_FOUND. Because the normal resolution is not resolving missing .ts extension.
Eg.:
import {Something} from "../src/shared/TestFile";
Complains:
Error: ERR_MODULE_NOT_FOUND /src/shared/TestFile /tests/TestFile.spec.ts module
at finalizeResolution (/node_modules/ts-node/dist-raw/node-esm-resolve-implementation.js:360:9)
So the solution is having the specifier.
"test": "node --loader ts-node/esm --experimental-specifier-resolution=node node_modules/mocha/lib/cli/cli --config _config/.mocharc.cjs"
When do you plan next release of ts-node?
The dist folder should be created when you install from master using npm,
because npm should be running the build script. CI produces a packaged
tarball artifact which should also include the dist folder. Downloading
the archive that GitHub generates -- the one that only contains the
contents of master -- will likely not work.
On Sun, Jul 5, 2020, 9:32 AM sionzeecz notifications@github.com wrote:
@NeilujD https://github.com/NeilujD The mocha issue you were seeing
should be fixed by #1031 https://github.com/TypeStrong/ts-node/pull/1031
which has been merged to master.If you're feeling adventurous, you can install ts-node directly from
master.npm i TypeStrong/ts-node#master --save
Or you can download and install the tarball artifact produced by CI.
[image: image]
https://user-images.githubusercontent.com/376504/82399938-32ccbc80-9a24-11ea-93b7-52c457617a51.png
[image: image]
https://user-images.githubusercontent.com/376504/82399965-3c562480-9a24-11ea-9ec6-3d65616e69e6.pngnpm install ts-node-packed.tgz --save
The node --loader ts-node/esm node_modules/mocha/lib/cli/cli
'src/*/.spec.ts' is not working in this case because the esm.mjs file
has require forconst esm = require('./dist/esm')
but the folder dist doesn't exist when it is "installed" from
TypeStrong/ts-node#master—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-653889543,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAC35OCMGBMXGRMHABJDBL3R2B6GTANCNFSM4MGJCWPA
.
I have used yarn.
yarn add --dev TypeStrong/ts-node#master
seems it hasn't called the prepare event. https://github.com/yarnpkg/yarn/issues/1671
Sorry for false fire!
yarn add --dev git+https://github.com/TypeStrong/ts-node
Works as is expected.
@sionzeecz Thanks for sharing; I'm sure this will help others using yarn, too.
for the time being, could we include a note about adding additional type-roots?
since ts-node --files isn't an options here, people (including me up until 5min ago) might not know how to do it otherwhise.
something like
note: to include typeroots specified in tsconfig, add env var
TS_NODE_FILES=true
as a hashbang: #!/usr/bin/env TS_NODE_FILES=true node --loader ts-node/esm.mjs
note hasbang arguments arent available on some unix distros (including linux). it works on macOS for example.
besides that i't's generally been working very well and made coding ts+esm a much smoother experience
EDIT: whoops, i missed the note for it:
have you considered adding support for .mts/.ctsextentions? i don't know how that would be handled by standard ts-compiler and wether that could be added as a compiler-plugin if not.
this isn't really nescessary of course, i just wanted to add a note about it
I have. To elaborate on the explanation that you found:
The problem is it makes code incompatible with the TypeScript ecosystem. Try importing from a .mts or .cts file and see what the language service says.
The benefits of TypeScript largely come from the language service. Even if you may individually disagree with that statement, we can't be messing things up for people who do benefit greatly from the language service.
Additionally, runtime compiling in production doesn't make sense for obvious reasons. Again, some people may individually agree with this statement, which is fine, but we can't be encouraging code that's deliberately incompatible with precompilation.
EDIT: it'll be interesting to see if Deno causes any changes in the ecosystem, since they use the .ts file extension in their import specifiers, and they've had to write editor integrations to somehow modify behavior of the language service.
The problem is it makes code incompatible with the TypeScript ecosystem. Try importing from a
.mtsor.ctsfile and see what the language service says.
never tried it, but i thougth so. that why i added the note about a compiler-plugin. there is a plugin system, but it's not used much and i have no idea how it works. was just a thought
The benefits of TypeScript largely come from the language service. Even if you may individually disagree with that statement, we can't be messing things up for people who _do_ benefit greatly from the language service.
100% agree. my code style is basically vanilla-esm with type anotations and my default compile target is ESNEXT, so the language feature is almost the only reason why i use ts
The reason why i wondered is because of this vanilla-sentiment. it would allow for the emitted files to be even closer to the source. It's just something i like, to have that smooth transition and it makes compiler-side oddities and mistakes a breeze to track down. it's just satisfying to me how together with the typedeclarations that have the same vanilla-look, it merges together into prettymuch exactly what the source-code is on a visual level aswell.
EDIT: it'll be interesting to see if Deno causes any changes in the ecosystem, since they use the
.tsfile extension in their import specifiers, and they've had to write editor integrations to somehow modify behavior of the language service.
I haven't looked at deno in quite a while. i followed it's development early-on but i'm quite happy with node, so for now i don't see a personal need to use it. but it currently looks like it's here to stay, and some competition can cause benefitial changes on both sides.
I'm really looking forward to what developments will happen in nodes [vm.module] and [policies]. i think deno will have some influance on them.
The TypeScript compiler intentionally enforces that extensions in import statements include the .js extension, because this makes the import statement in your source code exactly match the emitted import statement. import {foo} from './foo.js'; will be emitted. At compile-time it's looking at foo.ts. At runtime it's looking at foo.js. The import statement is identical both places.
This doesn't appear to work with mixed module type dependencies. I have a dependency that has the following in its package.json:
"main": "./output-cjs/index.js",
"exports": {
"import": "./output-esm/index.js",
"require": "./output-cjs/index.js"
},
When I am in a project that references it and do
node --loader ts-node/esm.mjs --experimental-specifier-resolution=node tests/index.ts
I get an error:
The requested module '@zoltu/rlp-encoder' is expected to be of type CommonJS, which does not support named exports. CommonJS modules can be imported by importing the default export.
Why is it expecting the dependency package to be CJS? I am specifically trying to load everything as ESM modules. Both CJS and ESM are available, so why is failing to load whichever one it is trying to load?
Without a full reproduction, I'm guessing it might be an accident in the
way the package.json is written. It looks slightly different than the
examples from node's documentation.
https://nodejs.org/api/esm.html
We can dig in further if we get a full reproduction that proves the problem
occurs with ts-node and not with TSC.
On Sat, Jul 25, 2020, 8:45 AM Micah Zoltu notifications@github.com wrote:
This doesn't appear to work with mixed module type dependencies. I have a
dependency that has the following in its package.json:"main": "./output-cjs/index.js","exports": {
"import": "./output-esm/index.js",
"require": "./output-cjs/index.js"
},When I am in a project that references it and do
node --loader ts-node/esm.mjs --experimental-specifier-resolution=node tests/index.ts
I get an error:
The requested module '@zoltu/rlp-encoder' is expected to be of type CommonJS, which does not support named exports. CommonJS modules can be imported by importing the default export.
Why is it expecting the dependency package to be CJS? I am specifically
trying to load everything as ESM modules. Both CJS and ESM are available,
so why is failing to load whichever one it is trying to load?—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-663851595,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAC35OEJPMTPBJFID2M7TB3R5LHXFANCNFSM4MGJCWPA
.
Repro: https://github.com/MicahZoltu/eip-2718-tests/tree/ts-node-repro
This is the first project I've tried to use with ts-node & ESM, so I may be doing something wrong.
@MicahZoltu thanks, having a complete example makes debugging straightforward.
I tried the following with node v14.2.0 on Linux:
# At the shell prompt
$ npm install @zoltu/rlp-encoder # EDIT: installed v2.0.3
$ node
# in the node REPL
# Ask node what it wants to load for CommonJS. This part looks good.
> require.resolve('@zoltu-rlp-encoder')
..../repros/node_modules/@zoltu/rlp-encoder/output-cjs/index.js'
# Ask node to load it as ESM
> import('@zoltu/rlp-encoder')
Promise { <pending> }
> (node:5815) UnhandledPromiseRejectionWarning:..../repros/node_modules/@zoltu/rlp-encoder/output-esm/index.js:1
export function rlpEncode(item) {
^^^^^^
SyntaxError: Unexpected token 'export'
at Object.compileFunction (vm.js:344:18)
Based on the above, and without using ts-node at all, it looks like node is trying to load the ESM files as CommonJS.
This makes sense because the file extension is .js, so node will use package.json to figure out how to load the file. This tells node to load it as CommonJS.
So it looks like the problem is unrelated to ts-node and is caused by a faulty build process for @zoltu/rlp-encoder.
Hmm, thanks for looking into it, I'll dig a bit more. Looking at the script output you provided, it does appear to be loading the correct index file (it is using the exports property in package.json to figure out paths), but it then proceeds to complain that export is not a valid token in an ESM.... (which it definitely is) which is very bizarre.
The node docs call out this behavior explicitly, so it's not a node bug. https://nodejs.org/api/esm.html#esm_conditional_exports
The confusion from the docs was here: https://nodejs.org/api/esm.html#esm_dual_commonjs_es_module_packages
Node.js can now run ES module entry points, and a package can contain both CommonJS and ES module entry points (either via separate specifiers such as 'pkg' and 'pkg/es-module', or both at the same specifier via Conditional exports).
However, if I set type: 'module' it only works with import ... and if I don't set type: 'module' then it only works with require(...). I'll take this up with the NodeJS people though, since it sounds like the issue isn't with ts-node.
For any future readers wanting to follow along on my journey toward functional dual module packages: https://github.com/nodejs/node/issues/34515
Yeah, under the "Conditional Exports" docs they say:
"import" - matched when the package is loaded via import or import(). Can reference either an ES module or CommonJS file, as both import and import() can load either ES module or CommonJS sources. Always matched when the "require" condition is not matched.
node intends you to use the .cjs and .mjs file extensions to specify the module's type, which overrides whatever package.json is saying. I'm not sure what would happen if you include a mostly-empty package.json file in the ./output-cjs directory which specifies a different "type".
It's worth to mention that TypeScript compiler does not support Node package exports at the moment. There is bug report for that: https://github.com/microsoft/TypeScript/issues/33079
There are two distinct issues I would like to discuss with the current ESM loader implementation:
transformSource calls to the "next" (e.g. Node default) ESM loader, obstructing loader chaining..cjs and .mjs are not interoperable with TypeScript-transpiled sources, as the compiler does not support these file extensions.The loader hooks resolve and getFormat eventually defer execution to the "next" (e.g. default) loader hook. However, `transformSource' swallows the entire call without deferring out-of-scope source transformations (e.g. non-TypeScript extensions) to the "next" loader hook, stifling composability for loader chaining.
I mention this issue of composability since ESM loader chaining will be available when the pull request nodejs/node#33812 has landed in Node.js. The currently proposed ESM loader chaining API is documented here. It would be preferable that the ts-node ESM loader does _not_ have to be the last (i.e. rightmost) ESM loader, since it blindly swallows the call. If two distinct loaders never defer to the "next" loader, they are not easily composable in a chain; higher order code would be needed to compose them intelligently. There is an example ESM loader with good chaining composability available here - it only handles in-scope calls and defers out-of-scope calls.
In the present, only one ESM loader that can be registered with --experimental-loader/--loader, making loaders mutually exclusive (e.g. CoffeeScript and TypeScript). This can be resolved with a userland ESM loader chaining "polyfill" before the native chaining support is available (a shoddy example is available below). Once again, the same issue is prevalent - the ts-node ESM loader must be the "rightmost" loader in the chain, as it will not continue the chain.
I am interested in amending this behavior so that the loader chaining "just works", in almost any order.
A notable consequence of the current "catch-all" behavior: .cjs and .mjs files are not loadable and interoperable by TypeScript-transpiled ESMs (i.e. sources transpiled with "ESNEXT" target) unless they are either implicitly or explicitly ignored by the ts-node instance. It could be argued this is a pitfall of TypeScript's compiler itself and requires upstream support (see microsoft/Typescript#39840); regardless, an exception is thrown for seemingly out-of-scope compile calls (likely by file extension?). For example, non-valid TypeScript extensions generate the following error:
$ node --experimental-loader ts-node/esm.mjs index.mjs
(node:207696) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
C:\REDACTED\project\node_modules\typescript\lib\typescript.js:139175
var error = new Error("Could not find source file: '" + fileName + "'.");
^
Error: Could not find source file: 'C:/REDACTED/project/index.mjs'.
at getValidSourceFile (C:\REDACTED\project\node_modules\typescript\lib\typescript.js:139175:29)
at Object.getEmitOutput (C:\REDACTED\project\node_modules\typescript\lib\typescript.js:139560:30)
at getOutput (C:\REDACTED\project\node_modules\ts-node\src\index.ts:562:32)
at Object.compile (C:\REDACTED\project\node_modules\ts-node\src\index.ts:775:32)
at C:\REDACTED\project\node_modules\ts-node\src\esm.ts:96:38
at Generator.next (<anonymous>)
at C:\REDACTED\project\node_modules\ts-node\dist\esm.js:8:71
at new Promise (<anonymous>)
at __awaiter (C:\REDACTED\project\node_modules\ts-node\dist\esm.js:4:12)
at transformSource (C:\REDACTED\project\node_modules\ts-node\dist\esm.js:71:16)
(despite index.mjs existing and functional; it executes correctly without the --experimental-loader ts-node flag)
Notably, the same behavior is present with CommonJS's require.extensions when executing node -r ts-node/register index.cjs. ts-node seems to inject the TypeScript compiler in Module._extensions['.js'], but no native Module._extensions['.cjs'] is registered; thus a .cjs file defaults to the TypeScript compiler in Module._extensions['.js'], which throws the same error.
As a quick hack, installing a userland ESM loader.mjs can make them interoperable:
import Module from "module";
Module._extensions['.cjs'] = Module._extensions['.js'];
const tsNode = import("ts-node/esm.mjs");
let pendingResolve = false;
let tsEsmLoader;
export async function resolve(specifier, context, defaultResolve) {
if (!pendingResolve) {
pendingResolve = true;
tsEsmLoader = await tsNode;
}
return Reflect.apply(tsEsmLoader ? tsEsmLoader.resolve : defaultResolve, this, arguments);
}
export async function getFormat(url, context, defaultGetFormat) {
return Reflect.apply(tsEsmLoader ? tsEsmLoader.getFormat : defaultGetFormat, this, arguments);
}
export async function transformSource(source, context, defaultTransformSource) {
return Reflect.apply(tsEsmLoader && !context.url.endsWith(".mjs") && !context.url.endsWith(".cjs") ? tsEsmLoader.transformSource : defaultTransformSource, this, arguments);
}
I strongly feel that the recommended .cjs and .mjs extensions should not be mangled when registering the ts-node loader. This can be fixed by either upstream support from microsoft/TypeScript or by the loaders in this project. I am unsure of what ramifications there are to this decision, though. Since the introduction of ES modules has also introduced the new convention of .cjs and .mjs, I feel this is also worth mentioning on this ticket.
I am not too familiar with the internals or API side of the TypeScript transpiler, so some of these issues might already have some solution, decision, or workaround that I am simply unaware of. Let me know what you think.
@concision Thank you for taking an interest. Responding to both items:
it does not play nice with deferring transformSource calls to the "next" (e.g. Node default) ESM loader, obstructing loader chaining.
When I first implemented ts-node's ESM loader, node did not have any plans to support composing multiple loaders. I'm glad to see that they are now supporting this. We will certainly accept a pull request that allows ts-node to support composing with other ESM loaders. Are you able to send us one?
.cjs and .mjs are not interoperable with TypeScript-transpiled sources, as the compiler does not support these file extensions.
Is your usage of .cjs and .mjs incompatible with TypeScript's semantic analysis? If TypeScript's language service or CLI compiler are throwing a semantic diagnostic in these situations, then we will faithfully pass that semantic diagnostic to the user. Our --transpileOnly mode skips typechecking, so semantic diagnostics are not generated.
Are the errors you are seeing coming from TypeScript? If the same code is compiled by tsc, what are the errors? (if any)
This information will help us make a fix if appropriate.
We will certainly accept a pull request that allows ts-node to support composing with other ESM loaders. Are you able to send us one?
I can definitely author a pull request for this. I would like to work out what we should consider the expected behavior first, though.
In transformSource, are the only sources we should transform be based on the file extensions .js, .jsx, .ts, .tsx , based on context.url? TypeScript does not seem to recognize .cjs and .mjs for the time being.
If so, should extensions be captured only be enabled based on tsconfig.json settings, similar to this? These enable rules seem reasonable to me to apply to the ESM loader as well.
I have also realized that no ESM loader exists that offers an equivalent of the following CommonJS loaders:
ts-node/register/transpile-onlyts-node/register/filesPerhaps it is also worth adding more ESM-equivalent loaders?
Are the errors you are seeing coming from TypeScript? If the same code is compiled by tsc, what are the errors? (if any)
Thanks for mentioning the --transpileOnly flag might be affecting it. It turns out it is related to TypeScript's compiler trying to type check the files and not liking the extension. TypeScript's error message could definitely be a bit more verbose.
As evidenced by running the following with an empty index.cjs file:
# throws "Error: Could not find source file", despite the file exists
$ node -r ts-node/register index.cjs
# executes successfully
$ node -r ts-node/register/transpile-only index.cjs
# compiling with tsc
$ tsc --allowJs index.cjs
error TS6054: File 'index.cjs' has an unsupported extension. The only supported extensions are '.ts', '.tsx', '.d.ts', '.js', '.jsx'.
Found 1 error.
I suppose this ideally requires upstream support from the pull request microsoft/Typescript#39840 being merged.
If we only transform files with the extensions [.js, .jsx, .ts, .tsx] in transformSource, then .mjs files will be loaded correctly (the current loader behavior does not allow loading .mjs files, only .js with "type": "module"). However, this is currently not the case in the CommonJS equivalent - .cjs files will not load correctly when allowJs filtering is on, as the TypeScript compiler will throw an error.
This could be resolved by defining if (config.allowJs) require.extensions['.cjs'] = require.extensions['.js'] _before_ injecting require.extensions['.js']. However, this could prove to be an unintuitive and confusing behavior for an end user of ts-node, as some JavaScript sources will not be ran through the TypeScript compiler.
As it stands though, these discrepancies obstruct ts-node working with mixed codebases where .cjs and .mjs files are present.
What are your thoughts on the matter?
As a side note:
In addition to ESM loader chaining, they are considering moving ESM loaders to isolated worker threads with their own global context (see nodejs/node#31229). I am unsure of what ramifications this change would have on this project, or how to prepare for it. If anything, I am mentioning so that it is on this project's radar (as this is one of the few projects I have seen that even support --experimental-loader).
@concision
After looking at the code again, our transformSource hook already defers via defer()
https://github.com/TypeStrong/ts-node/blob/master/src/esm.ts#L91
Are you seeing different behavior? I believe you're hitting the bug described in the next paragraph. Fixing that bug will not require any changes to esm.ts; only to our ignored() implementation.
The rules for which files we do/don't transform should match the rules implemented for our CommonJS loader. We will transform .ts and .tsx. We will additionally transform .js and .jsx if the allowJs option is turned on. We will not transform any other extensions, and we will not transform anything that's identified by our ignored function. Today, the ignored function has a bug when combined with our ESM loader. Instead of conditionally excluding .js and .jsx extensions, it should be conditionally including file extensions based on the rules I just described.
An ESM equivalent of ts-node/register/transpile-only might be useful. Feel free to file a separate issue for this. We already support loading all flags from a tsconfig file, so I tend to recommend --script-mode or ts-node-script and allow all other flags to be specified in tsconfig.json.
We're not going to support code that tsc and the language service don't support. ts-node is meant to be equivalent to tsc && node. If someone is using tsc, then they're forced by TS to write compatible code. They can use the package.json "type" field to specify if a subdirectory is loaded as CJS or MJS. If/when tsc adds additional features, we'll support them.
I've heard about their plans to move loaders to an isolated worker. If that happens, it'll mean the ESM loader does not auto-install the CJS loader, since it won't have access to the main thread's require.extensions. Otherwise I think it'll keep working as expected. Someone could file separate issues to set up some sort of communication channel between the isolated worker and the main thread, so that a single TS instance could handle compilation for both CJS and ESM. This might also be convenient to make sure both use the same tsconfig settings.
@concision edited my post above, so pinging you in case you missed the edit.
ok frinds here comes the sun you need to check the file befor your load it we do the same inside rollup for example we assume ES6+ import by default if a file has Top Level Import but has require in it we assume still ES6+ so if he uses createRequire it is still ES6 as soon as no TopLevel Import is there we handle it as it would be CommonJS as it is Common JS :)
Oh by the way maybe mts and cts extensions would help? i saw here already
@cspotcode i am deno contributor i can tell you how it works inside deno it uses parcel internal to load and transpil the files. it bundels in memory on first run.
at present i am trying to prepare a language-server called rollup-lang-server that does the same but with rollups algo in the background as i am also rollup contributor i am familar with what it does and it works well as loader.
i also use rollup as webloader often
The Language Server will run rollup + typescript where rollup will do resolve and load and then hand over to typescript at this point rollup does know already what type of file that is via parsing it into a acron ast representation for analyze.
This will also open the gates for loader plugins for typescript
By design, we match node's behavior for choosing ESM/CJS, which determines based on file extension and package.json, and does not read the source text.
@cspotcode i know that typescript does not even handle .mjs or .cjs that is the core issue it will return Module not found for .mjs or .cjs
@cspotcode
You are correct - I am hitting the bug you have described. I glossed over the ignored functionality thinking it was intended to handle something akin to tsconfig.json's excludes.
I will be moving this discussion over to #1098.
I have opened a separate issue for the transpile-only hook over at #1101.
@cspotcode seems like the instructions in the OP are slightly off?
I needed to run
node --loader ts-node/esm.mjs ./my-script.ts
not
node --loader ts-node/esm ./my-script.ts
Could be, yeah. We had to make a fix in our package.json exports object to
enable omitting the extension; maybe it hasn't been published yet.
Right now there's one PR that is awaiting a code review, but as soon as
it's reviewed, I want to publish 9.0. Lots of things in 9.0:
https://github.com/TypeStrong/ts-node/compare/d989d4ce431e4f2f824f9ee1f9e183a9edbe39d0...master
On Thu, Aug 13, 2020, 12:25 AM Matt Lim notifications@github.com wrote:
@cspotcode https://github.com/cspotcode seems like the instructions in
the OP are slightly off?I needed to run
node --loader ts-node/esm.mjs ./my-script.ts
not
node --loader ts-node/esm ./my-script.ts
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-673247262,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAC35OH5JW4W4QECJBDJEETSANTJ5ANCNFSM4MGJCWPA
.
I'm trying to support ESM but the paths alias configured in tsconfig.json aren't being resolved in node-esm-resolve-implementation as relative or absolute path:
/home/guilima/www/financial-back/node_modules/ts-node/dist-raw/node-esm-resolve-implementation.js:680
throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base));
^
Error: ERR_MODULE_NOT_FOUND @middleware/errorHandler /home/guilima/www/financial-back/index.ts
tsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@root/*": ["*"],
"@business/*": ["src/business/*"],
"@data/*": ["src/data/*"],
"@enums/*": ["src/enums/*"],
"@middleware/*": ["src/middleware/*"],
"@routes/*": ["src/routes/*"],
"@schema/*": ["src/schema/*"],
"@services/*": ["src/services/*"],
"@utils/*": ["src/utils/*"],
"*": ["node_modules/*"]
},
"outDir": "dist",
"lib": ["ESNext"],
"module": "ESNext",
"target": "ESNext",
"moduleResolution": "Node",
"removeComments": true,
"esModuleInterop": true
},
"include": [
"index.ts"
]
}
Can it be a problem or something on my end?
Node does not understand paths mapping, and right now we are deferring to
node's resolver implementation, with only the minimal change necessary to
map .js->.ts extensions. What are you using today to support paths?
Tsconfig-paths?
Some relevant discussion can be found here:
https://github.com/dividab/tsconfig-paths/issues/122
In the future, ts-node might get into the business of more resolution
behaviors, for project references, paths, and baseUrl. But none of that
has been discussed yet.
On Sat, Aug 15, 2020, 10:14 AM Guilherme Carvalho Lima <
[email protected]> wrote:
I'm trying to support ESM but the paths alias configured in tsconfig.json
aren't being resolved in node-esm-resolve-implementation as relative or
absolute path:/home/guilima/www/financial-back/node_modules/ts-node/dist-raw/node-esm-resolve-implementation.js:680
throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base));
^
Error: ERR_MODULE_NOT_FOUND @middleware/errorHandler /home/guilima/www/financial-back/index.tstsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@root/": [""],
"@business/": ["src/business/"],
"@data/": ["src/data/"],
"@enums/": ["src/enums/"],
"@middleware/": ["src/middleware/"],
"@routes/": ["src/routes/"],
"@schema/": ["src/schema/"],
"@services/": ["src/services/"],
"@utils/": ["src/utils/"],
"": ["node_modules/"]
},
"outDir": "dist",
"lib": ["ESNext"],
"module": "ESNext",
"target": "ESNext",
"moduleResolution": "Node",
"removeComments": true,
"esModuleInterop": true
},
"include": [
"index.ts"
]
}Can it be a problem or something on my end?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-674403544,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAC35OA5RLJ4FCGJ4ZOMTXDSA2J4VANCNFSM4MGJCWPA
.
Despite all suggestions offered in this thread, I am stuck in a loop with this and maybe I am missing something.
I have so far:
type: module in package.jsonnode --loader ts-node/esm.mjs ./src/cli.tsand still hit the issue:
`(node:20072) ExperimentalWarning: The ESM module loader is experimental.
internal/modules/run_main.js:54
internalBinding('errors').triggerUncaughtException(
^
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for...........
at Loader.defaultGetFormat [as _getFormat] (internal/modules/esm/get_format.js:65:15)
at Loader.getFormat (internal/modules/esm/loader.js:113:42)
at Loader.getModuleJob (internal/modules/esm/loader.js:244:31)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async Loader.import (internal/modules/esm/loader.js:178:17) {
code: 'ERR_UNKNOWN_FILE_EXTENSION'
}`
@rags2riches-prog have you tried --experimental-specifier-resolution=node flag?
node --experimental-specifier-resolution=node --loader ts-node/esm.mjs ./src/cli.ts
Also double-check if TypeScript likes your code or not. You can use tsc for this. TypeScript requires you to import "foo.js" (js extension) even when you're importing a foo.ts file. I'm not sure if that's happening here, but it's worth double-checking.
@evg656e @cspotcode
Thanks for coming back to me guys, much appreciated.
Same old story here. I have changed the script to node --experimental-specifier-resolution=node --loader ts-node/esm.mjs ./src/cli.ts to no avail.
yarn run v1.22.4
$ node --experimental-specifier-resolution=node --loader ts-node/esm.mjs ./src/cli.ts
(node:18420) ExperimentalWarning: The ESM module loader is experimental.
(node:18420) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
internal/modules/cjs/loader.js:966
const err = new Error(message);
^
Error: Cannot find module './dist/esm'
Require stack:
C:\xxxxxxx\xxxxxx\xxxxxxxx\node_modules\ts-node\esm.mjs
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:966:15)
at Function.Module._load (internal/modules/cjs/loader.js:842:27)
at Module.require (internal/modules/cjs/loader.js:1026:19)
at require (internal/modules/cjs/helpers.js:72:18)
at file:///C:/xxxxxxxxxxxxxxx/node_modules/ts-node/esm.mjs:6:13
at ModuleJob.run (internal/modules/esm/module_job.js:137:37)
at async Loader.import (internal/modules/esm/loader.js:179:24)
at async internal/process/esm_loader.js:69:9 {
code: 'MODULE_NOT_FOUND',
requireStack: [
'C:\\xxxxxxx
]
}
error Command failed with exit code 1.
I am not trying to pass the hot potato over to you guys, I just need to understand where I can take a look at this internal/cjs/loader function that triggers the error because this specific module in the stack comes up over and over again. This is a node thing isn't it?
Nor do I think that a shotgun approach would help here as in "try this, try that"
Check your package.json if you are using latest version of ts-node. (the esm.mjs is not part of any released version).
package.json:
"ts-node": "git+https://github.com/TypeStrong/ts-node",
Then I'm using as run script:
TS_NODE_PROJECT='src/_server/tsconfig.json' node --loader ts-node/esm.mjs --experimental-specifier-resolution=node --experimental-top-level-await --no-warnings src/_server/index.ts
(the important ones are --loader and --experimental-specifier-resolution=node)
In my case I have written custom loader (not using esm.mjs) but it was working fine for me.
// Edit:
@cspotcode corrected me, the last ts-node should contains the esm.mjs.
So please check node_modules/ts-node/dist if there is esm.js file and node_modules/ts-node should contains esm.mjs file.
If you are using #master version please check if you have proper linking in your package.json (yarn handles it differently than npm). You will find out the esm.mjs is missing in the mentioned directory above if this is true.
If you are not using #master version then please check if you have latest version in your package.json.
PS: Running on WSL 2.0 && Ubuntu
@sionzeecz correction: esm.mjs is in the latest version.
@rags2riches-prog here is proof that it works, using the latest version of ts-node published to npm, with yarn, on Windows.
https://github.com/TypeStrong/ts-node-repros/tree/bfd23957-d44f-47c2-969b-7fe9b3ba8630
You can fork that branch and modify it so that it breaks in the way you're experiencing. Then we'll be able to verify on our end and propose a fix.
@rags2riches-prog here is proof that it works, using the latest version of ts-node published to npm, with yarn, on Windows.
https://github.com/TypeStrong/ts-node-repros/tree/bfd23957-d44f-47c2-969b-7fe9b3ba8630
You can fork that branch and modify it so that it breaks in the way you're experiencing. Then we'll be able to verify on our end and propose a fix.
will take a look at it tomorrow and revert. I have already the latest version of your package and please, I am not trying to attack your package in any shape or form...I am trying to understand here despite having a quite busy schedule at work.
Of course, I don't see it as an attack at all. I'm also busy, and I get questions from many other users, so this is the quickest way to diagnose your problem.
We get a lot of questions which are either very simple mistakes or problems completely unrelated to ts-node. Additionally, the questions often omit important information. Without knowing you personally, I have to assume the problem you're having is unrelated to ts-node. This is why I ask for people to send me code that I can actually run which shows the problem. Usually, in the process of attempting to send me this code, people end up figuring out their own problem. This is good, because it solves the problem for them and for me.
@cspotcode works as expected, meaning I missed something about the configuration. Thank you for your help.
v9.0.0 is out, including a few ESM improvements: https://github.com/TypeStrong/ts-node/releases/tag/v9.0.0
ESM support works great for me, thank you! Any ETA on caching support?
@sgtpep I'm glad to hear it's working well for you!
Please move caching discussion to the relevant tickets: #951, #908
With v9.0.0, I notice a 1-second delay when starting the script with the loader. I guess that's expected?
echo "console.log('Hello world')" > hello.js
cp hello.js hello.ts
node --loader ts-node/esm hello.js # instant
node --loader ts-node/esm hello.ts # takes one second
Remember you're asking the compiler to do typechecking, which might pull in
way more than just that one file. (It might parse every single @types, for
example) You can try transpileOnly mode and compare the speeds.
TS_NODE_DEBUG=true will log additional diagnostics, which may indicate
which area is slow.
On Sun, Aug 30, 2020, 2:59 AM Dan Dascalescu notifications@github.com
wrote:
With v9.0.0, I notice a 1-second delay when starting the script with the
loader.echo "console.log('Hello world')" > hello.js
cp hello.js hello.ts
node --loader ts-node/esm hello.js # instant
node --loader ts-node/esm hello.ts # takes one secondNode v14.8.0 on Ubuntu 20. Has anyone else noticed this slowness?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-683384578,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAC35OE5RJ6TUDFOZ4CRPRTSDH2FPANCNFSM4MGJCWPA
.
I am trying to run this with mocha through npx. What I run is the following:
npx --node-arg "--loader ts-node/esm.mjs" mocha --config ".."
I run it this way since as I understand you can only register the ESM loader by argument to Node, and you cannot do it inside of a required file when mocha starts up. But I get this error:
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension "" for node_modules/mocha/bin/mocha
Is it possible to have the default extension assumed 'js'?
@archfz this is a mocha bug, caused by a nodejs limitation. You can check mocha's issue tracker to find the appropriate solution. I don't have a link on-hand.
I believe they've already made the necessary changes, but you may need to follow specific instructions that are written either in mocha's README, on its website, or in the relevant issue.
I think this is of concern here as well. Suppose I have a binary that is infact js and executed with node (shebang) and I don't want to have an extension to it (that is the norm). Shouldn't in that case this loader work?
@archfz node is saying it does not understand how to classify extensionless files, correct? We defer to node to classify files that we don't care about. Extensionless files are not handled by ts-node, so we defer to node.
Here is the relevant code:
https://github.com/TypeStrong/ts-node/blob/master/src/esm.ts#L58-L80
Additionally, it is having trouble classifying the file node_modules/mocha/bin/mocha, correct? This is tricky because mocha's package.json says that it should be loaded as CommonJS, not ESM. Should node be using the ESM loader in this case?
Any word on node 12 support? Been trying to run with the --es-module-specifier-resolution=node --experimental-modules flags, instead of the --experimental-specifier-resolution=node that works with 13+. Here's the error I got:
TypeError [ERR_INVALID_RETURN_PROPERTY_VALUE]: Expected string to be returned for the "format" from the "loader resolve" function but got type undefined.
at Loader.resolve (internal/modules/esm/loader.js:88:13)
at Loader.getModuleJob (internal/modules/esm/loader.js:147:29)
at Loader.import (internal/modules/esm/loader.js:131:17)
@nguyensomniac The error you're getting doesn't seem related to the flags you're trying to use. It seems like a bug in node. Are you absolutely sure this error is being caused by ts-node's loader? Does the same error happen when you run ESM modules without ts-node on the same version of node 12?
Those node flags have to be parsed manually in our code. If you think ts-node is missing some logic for node 12, we will probably accept a pull request to fix it. My focus is on supporting node 13 and 14, since all of this is experimental anyway.
@nguyensomniac Could you specify what version of node 12 you are receiving the error with? I was testing and there seems to be mixed results on various node 12 versions.
On Docker image node:12.18.3, I had no problems using just --loader ts-node/esm.
However, on Docker image node:12.0.0, different flags were required (i.e. --experimental-modules --es-module-specifier-resolution=node --loader ts-node/esm) and there was an error (different from the one you specified, though).
I tried again with esm-usage-example in this repo using 12.18 (I was using 12.15 beforehand). I was able to make it work by adding an extension to the export at foo.ts and running the following command: node --loader ts-node/esm ./index
However, without the extension, I get an ERR_MODULE_NOT_FOUND with and without the --experimental-modules --es-module-specifier-resolution=node flags. Is that consistent with what you've found?
Yeah, node has a bug affecting extensionless files. I'm on mobile, but you
should be able to find where others have asked the same question in this
thread. I think we also have links to the relevant nodejs issues.
On Thu, Sep 10, 2020, 5:43 PM Lily Nguyen notifications@github.com wrote:
I tried again with esm-usage-example in this repo using 12.18 (I was
using 12.15 beforehand). I was able to make it work by adding an extension
to the export at foo.ts
https://github.com/TypeStrong/ts-node/blob/master/esm-usage-example/foo.ts
and running the following command: node --loader ts-node/esm ./indexHowever, without the extension, I get an ERR_MODULE_NOT_FOUND with and
without the --experimental-modules --es-module-specifier-resolution=node
flags. Is that consistent with what you've found?—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-690748074,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAC35OBA5QOALNQZQMIJXKTSFFCBJANCNFSM4MGJCWPA
.
@cspotcode Thanks for the prompt replies—I was able to fix my issue by aliasing --es-module-specifier-resolution to --experimental-specifier-resolution. I'll open a PR with more info.
Small bug: ts-node can't load a package if it defines the conditional imports directly at the root of the exports field.
Scratch that, i just didn't notice that it was imported twice, once missing the leading @
i thought it was odd since you are using a slight modification of the native esm loader.
@KilianKilmister yeah, it's a good thing to keep an eye on. If node every makes bugfixes to their ESM loader, then we'll need to integrate those fixes into our copy. That's why the indentation in our copy is intentionally wrong: to hopefully make merging upstream changes easier if/when they happen.
Pardon in advance if this isn't the right location for my comment/question 🙇 :
Is the following bullet point (from the pending development section) Implement require('ts-node').esmImport(module, 'import-path') related to tsconfig compilerOptions.paths? I think I'm encountering an expected bug, but wanted to confirm.
I was following the usage directions/examples and have my code working, with the exception of ES style imports using tsconfig paths.
More context on my situation:
// foo-bizzle.ts
import { getKnexInstance } from '@db/injector'; // @db is a ts path/alias
node --loader ts-node/esm scripts/foo-bizzle.ts
md5-7733a4d29df754b429b9c5fd322a872c
.../node_modules/ts-node/dist-raw/node-esm-resolve-implementation.js:650
throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base));
^
Error: ERR_MODULE_NOT_FOUND @db/injector.ts .../scripts/foo-bizzle.ts
at packageResolve (.../node_modules/ts-node/dist-raw/node-esm-resolve-implementation.js:650:9)
@chrisdchow
Is the following bullet point ...
Implement require('ts-node').esmImport(module, 'import-path')related to tsconfigcompilerOptions.paths?
Good question. No, it's not.
esmImport() is a way to preserve await import('foo') in a .ts file when you have "target": "CommonJS". TypeScript will normally transform await import('foo') into a require() call if you have "target": "CommonJS". This is normally what you want. But what if you want most imports to be converted to require(), but you need one dynamic await import() to be preserved? Node allows us to do await import() in a CommonJS file to load ESM modules, so this is occasionally necessary.
The hack is to write a helper function in another file, not being transformed by TypeScript, which does await import(). Then use this helper function in our CommonJS .ts file. esmImport() is this helper function.
compilerOptions.paths affects the way TS's typechecker resolves paths. You're only supposed to use it if the runtime environment does the same sort of path mapping. (webpack, rollup, etc) There are third-party libraries such as tsconfig-paths which can implement this non-standard path resolution for require(), but I don't think any of them support ESM.
@boyangwang use
import type { SomeType } from './someType'
There's a compiler option --importsNotUsedAsValues--, which you can set to
preserve type only imports, remove type only imports at compile time,error on type only imports that aren't using import type { X } from 'y'I believe your code should also throw the second error when running files pre-compiled with tsc -p <your-tsconfig-json>
I always set this option to error, as it significantly improves source-code readability, as everyone who sees it knows that something is just a virtual type/interface and not an actual object.
I would recommend setting the compiler option "importsNotUsedAsValues": "error" to anyone
type
Thank you! That's very helpful
I was in the mid of editing my question. For any future folks, here's my original question:
Does it support .d.ts file? I see 2 errors and couldn't make it work:
node-esm-resolve-implementation.js:319 throw new ERR_MODULE_NOT_FOUND
An import path cannot end with a '.d.ts' extension
@boyangwang
This old comment is relevant. It explains why you generally need to include a .js extension when importing .ts files.
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-633176674
Combined with @KilianKilmister's comment, it should give you everything you need to know. I agree with the compiler option recommendation.
compilerOptions.pathsaffects the way TS's typechecker resolves paths. You're only supposed to use it if the runtime environment does the same sort of path mapping. (webpack, rollup, etc) There are third-party libraries such astsconfig-pathswhich can implement this non-standard path resolution forrequire(), but I don't think any of them support ESM.
@cspotcode any chance the loader could support path aliasing?
Similar to this perhaps: https://github.com/ilearnio/module-alias/issues/59#issuecomment-500480450
@siric that's tricky because it makes us incompatible with tsc && node. The idea is, if you use ts-node, you should also be able to precompile with tsc and then run node. You can use ts-node in development, and precompile for production or publication.
With tsconfig-paths, it is an external library that can be installed with or without ts-node.
Loader can be enabled by configuration.
SET NODE_OPTIONS="--loader ts-node/esm --unhandled-rejections=strict --enable-source-maps --experimental-specifier-resolution=node"
node ./script.ts
md5-5e9d39d4fba399b6e6736d15e27ecc8a
`package.json`
md5-f76d2a1d27533371ec2eb76b78f76bc5
```sh
npm start
I've been facing an issue with asynchronous functions. I've been getting the following error from tslib.
The requested module 'tslib' is expected to be of type CommonJS, which does not support named exports
My example promises.ts
import { Logger } from './logger.js';
const log = new Logger();
// running npm run ts:promises causes error with the async
// removing async works fine. Error occurs with import { __awaiter }
async function fakeAsyncFunction() {
log.result(`This is test`);
}
fakeAsyncFunction();
After being compiled you will see below. The first line import { __awaiter } from "tslib"; apparently being the culprit of the issue.
import { __awaiter } from "tslib";
import { Logger } from './logger.js';
const log = new Logger();
// running npm run ts:promises causes error with the async
// removing async works fine. Error occurs with import { __awaiter }
function fakeAsyncFunction() {
return __awaiter(this, void 0, void 0, function* () {
log.result(`This is test`);
});
}
fakeAsyncFunction();
The weird thing is I can somewhat get around this by change this to _only_ having the async function in the file
async function fakeAsyncFunction() {
console.log(`This is test`);
}
fakeAsyncFunction();
After being compiled you will see that the is no tslib for the __awaiter
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
function fakeAsyncFunction() {
return __awaiter(this, void 0, void 0, function* () {
console.log(`This is test`);
});
}
fakeAsyncFunction();
If you want to view a repo to reproduce the issue you should be able to look at this
Running npm install and npm run ts:promises should get you to recreate the issue.
Any ideas on what is causing this issue?
Thanks in advance for any help.
@tengel92
The weird thing is I can somewhat get around this by change this to only having the async function in the file
You're removing all import/export syntax, making the file "not a module." When you do this, I assume TypeScript is not allowed to add import statements, so it's forced to inline the __awaiter helper instead of importing it. No importing means no import-related error.
The requested module 'tslib' is expected to be of type CommonJS, which does not support named exports
I'm pretty sure this error comes from node. In node, importing a CommonJS module from an ESM module means that the default export is the same as the CommonJS exports object. No named exports, only default.
I bet if you google around, you'll find issues where other people have already reported this incompatiblity between "tslib" and node's native ECMAScript modules support. Sounds like the correct fix is an update to tslib.
Here are some links which may or may not be helpful in your search:
https://nodejs.org/dist/latest-v14.x/docs/api/esm.html
https://unpkg.com/browse/[email protected]/ <-- looks like an ESM version is bundled, but the package.json is not updated
https://github.com/nodejs/node/issues/32137 <-- found this via google. May or may not be relevant
@cspotcode Thank you so much for the reply.
I did a bit more digging and I changed importHelpers from true to false in my tsconfig.json. When I reran tsc I noticed that it kept the native TypeScript __awaiter and didn't try to use tslib.
That did the trick and my async functions are working! Hopefully this will help someone if they get stuck on the same issue.
Seems the new node have made a breaking change. By updating to node 14.13.1 from 14.13.0 I'm getting error:
TypeError [ERR_INVALID_RETURN_PROPERTY_VALUE]: Expected string to be returned for the "format" from the "loader getFormat" function but got type object.
at Loader.getFormat (internal/modules/esm/loader.js:110:13)
at Loader.getModuleJob (internal/modules/esm/loader.js:230:20)
at ModuleWrap.<anonymous> (internal/modules/esm/module_job.js:53:21)
at async Promise.all (index 0)
at link (internal/modules/esm/module_job.js:58:9)
error Command failed with exit code 1.
@sionzeecz can confirm, getting the same eror after installing the update. I took a look at the [nodejs changelog] and it's probably one of these 3 entries that caused it (by the name, I'm guessing the last one the last one).
- [
0ac5fa703e] - module: update to [email protected] (Guy Bedford) #35501- [
5c879a97e6] - module: fix builtin reexport tracing (Guy Bedford) #35500- [
f23a0e250c] - module: refine module type mismatch error cases (Guy Bedford) #35426
@sionzeecz @KilianKilmister can you offer some more context about exactly when this error occurs? Which file is being loaded, what's the format of the file, what flags are being passed to node, what tsconfig is being used, etc.
This is where getFormat happens. https://github.com/TypeStrong/ts-node/blob/master/src/esm.ts#L58-L80
So far, it seems like node's getFormat implementation is returning an object, yet node is complaining that it will not accept an object.
It basically happens immediately when you try to use ts-node in esm mode.
node --loader ts-node/esm --experimental-specifier-resolution=node test.ts
where test.ts
import fs from "fs"
shell:
node --loader ts-node/esm --experimental-specifier-resolution=node test.ts
(node:11688) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
TypeError [ERR_INVALID_RETURN_PROPERTY_VALUE]: Expected string to be returned for the "format" from the "loader getFormat" function but got type object.
at Loader.getFormat (internal/modules/esm/loader.js:110:13)
at Loader.getModuleJob (internal/modules/esm/loader.js:230:20)
at ModuleWrap.<anonymous> (internal/modules/esm/module_job.js:53:21)
at async Promise.all (index 0)
at link (internal/modules/esm/module_job.js:58:9)
Seems it starts happening only in a case if my tsconfig.json contains this option:
"importsNotUsedAsValues": "error",
When I remove it, it works fine 👀
anyway here is small tsconfig.json:
{
"compilerOptions": {
"module": "ESNext",
"importsNotUsedAsValues": "error"
},
"ts-node": {
"transpileOnly": true
}
}
//Edit 1:
Ok, removing the option solves only the case above but not my projects.
Which basically makes sense, because the import is unused and removed.
You didn't mention --experimental-specifier-resolution=node in your initial post. Is the behavior consistent with and without that flag, or does it change?
importsNotUsedAsValues affects the elision of import statements. Without it, the import fs from "fs"; is removed because it's unused. With it, that statement is included. You can also force inclusion by using fs in your code. For example import fs from 'fs'; console.log(fs); So it sounds like the import statement is triggering the error.
What happens when you rename test.ts to test.cjs (or test.js if your package.json makes it ESM)? Does it only occur when the file is .ts?
Without the --experimental-specifier-resolution=node flag I'm still getting the same error.
The extension stuff:
I have set type to module in package.json,
when I rename the test.ts to cjs
shell:
node --loader ts-node/esm test.cjs
(node:15209) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
test/test.cjs:1
import fs from 'fs';
^^^^^^
SyntaxError: Cannot use import statement outside a module
at wrapSafe (internal/modules/cjs/loader.js:979:16)
at Module._compile (internal/modules/cjs/loader.js:1027:27)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
at Module.load (internal/modules/cjs/loader.js:928:32)
at Function.Module._load (internal/modules/cjs/loader.js:769:14)
at ModuleWrap.<anonymous> (internal/modules/esm/translators.js:180:29)
at ModuleJob.run (internal/modules/esm/module_job.js:146:23)
at Loader.import (internal/modules/esm/loader.js:165:24)
at Object.loadESM (internal/process/esm_loader.js:68:5)
npm ERR! Test failed. See above for more details.
or to .js
node --loader ts-node/esm test.js
(node:15397) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
TypeError [ERR_INVALID_RETURN_PROPERTY_VALUE]: Expected string to be returned for the "format" from the "loader getFormat" function but got type object.
at Loader.getFormat (internal/modules/esm/loader.js:110:13)
at Loader.getModuleJob (internal/modules/esm/loader.js:230:20)
at ModuleWrap.<anonymous> (internal/modules/esm/module_job.js:53:21)
at async Promise.all (index 0)
at link (internal/modules/esm/module_job.js:58:9)
npm ERR! Test failed. See above for more details.
Here are steps to reproduce it:
npm installnpm testYou will get the error :)
Tested with the following snippet (diplay of why you shouldn't use for...in to iterate an array, with a dummy import on top)
NODE_OPTIONS='--experimental-import-meta-resolve --experimental-vm-modules --unhandled-rejections=strict'NODE_OPTIONS='' with the same result, the above is my defaultTS_NODE_LOG_ERROR=true node --loader ts-node/esm --harmony-string-replaceall --harmony-logical-assignment --harmony-promise-anyimport { DefaultDeserializer } from 'v8'
const x = DefaultDeserializer
const string = 'Molestiae labore sunt et. Cum quisquam nam et labore voluptatem vel alias.'
const regex = /labore\s(\w+)\set/
const arr = string.match(regex)
console.log('-- IN --')
for (const key in arr) console.log('- ' + arr[key])
console.log('\n-- OF --')
for (const item of arr) console.log('- ' + item)
.js, .ts .mjs:log
TypeError [ERR_INVALID_RETURN_PROPERTY_VALUE]: Expected string to be returned for the "format" from the "loader getFormat" function but got type object.
at Loader.getFormat (internal/modules/esm/loader.js:110:13)
at Loader.getModuleJob (internal/modules/esm/loader.js:230:20)
at ModuleWrap.<anonymous> (internal/modules/esm/module_job.js:53:21)
at async Promise.all (index 0)
at link (internal/modules/esm/module_job.js:58:9)
.cjs: SyntaxError: Cannot use import statement outside a module (go figure).js, .ts, .cjs .mjs@cspotcode Node 15 is out. Any plans to fix it in near future?
I had to install node managers on all my servers to keep it on 14.13.0.
I have seen you have created a new issue for Hacktoberfest which is fine but it will take months before someone fixes this.
@sionzeecz yeah, by the end of this month. You say "it will take months before someone fixes this." Are you sure about that?
Remember the warning we gave everyone at the top of this thread:
Experimental warning
Node's loader hooks are EXPERIMENTAL and subject to change. ts-node's ESM support is also experimental. They are likely to have breaking changes in the near future. You have been warned!
Also keep in mind node's stability indicator for native ECMAScript modules:
Throughout the documentation are indications of a section's stability. Some APIs are so proven and so relied upon that they are unlikely to ever change at all. Others are brand new and experimental, or known to be hazardous.
Stability: 1 - Experimental. The feature is not subject to Semantic Versioning rules. Non-backward compatible changes or removal may occur in any future release. Use of the feature is not recommended in production environments.
Node's recommendation is that you avoid using native ECMAScript modules in production environments.
Hi @cspotcode et al., I've been watching this thread over the last few months, and have come to the point where I'm hoping for some help, or hoping to be helpful.
This follows on from the above discussion about node >14.13.0. Hopefully this barebones repository I've made is useful. with:
"type": "module" in the package.json"module": "esnext", "moduleResolution": "node", "esModuleInterop": false and "allowSyntheticDefaultImports": true in the tsconfig.json"exec": "node --trace-warnings --loader ts-node/esm src/server.ts" in nodemon.jsonTested on all versions of 14.x and 15.x and can unsurprisingly conclude the change was between 14.13.0 and 14.13.1.
Error log with
--trace-warnings
$ npm run start:dev [email protected] start:dev /Users/MyUser/git/ts-node-test [nodemon] 2.0.6 [nodemon] to restart at any time, enter `rs` [nodemon] watching path(s): src/**/* [nodemon] watching extensions: ts [nodemon] starting `node --trace-warnings --loader ts-node/esm src/server.ts` (node:13431) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time at emitExperimentalWarning (internal/util.js:183:11) at initializeLoader (internal/process/esm_loader.js:54:3) at Object.loadESM (internal/process/esm_loader.js:67:11) at runMainESM (internal/modules/run_main.js:43:31) at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:69:5) at internal/main/run_main_module.js:17:47 TypeError [ERR_INVALID_RETURN_PROPERTY_VALUE]: Expected string to be returned for the "format" from the "loader getFormat" function but got type object. at Loader.getFormat (internal/modules/esm/loader.js:110:13) at Loader.getModuleJob (internal/modules/esm/loader.js:230:20) at ModuleWrap.(internal/modules/esm/module_job.js:53:21) at async Promise.all (index 0) at link (internal/modules/esm/module_job.js:58:9) [nodemon] app crashed - waiting for file changes before starting...
Hopefully this may be helpful. Apologies if it isn't.
@g-bigboydev Thanks; always happy to have help. I'm pretty sure I've already identified the root cause; I'm tracking it in #1130.
Do you want to send a PR with a fix?
I wish I could! I think I may’ve written that in an easy to misinterpret way though (very late here; half asleep) - my meaning was that I hoped the codebase I’d prepared may be helpful
is there a way I could use ESM with ts-node while keeping node's require()?
whenever I do node --loader ts-node/esm my-script.ts and I'm also using dynamic require() then I get "ReferenceError: require is not defined"
:(
That's a node behavior. Node does not give you a default require function
in an ESM module. Their docs should have all the information you need to
create a require function when you need it.
On Sun, Oct 25, 2020, 1:29 AM Jorge Rivera notifications@github.com wrote:
is there a way I could use ESM with ts-node while keeping node's require()
?whenever I do node --loader ts-node/esm my-script.ts and I'm also using
dynamic require() then I get "ReferenceError: require is not defined":(
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-716097208,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAC35ODL6OZKL24EYQW6ITLSMOZSHANCNFSM4MGJCWPA
.
Hi.
I'm trying to make an Express app with Typescript.
Env :
Node : v15.0.1
ts-node : v9.0.0
package.json :
...
"type": "module",
"scripts": {
"start": "node --loader ts-node/esm server.ts"
}
tsconfig :
...
"module": "ESNext",
"allowJs": true,
"outDir": "dist",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"moduleResolution": "node",
"include": [
"server.ts",
"src/**/*"
]
server.ts :
import app from "./src/app";
// other import
export default async function start() {
try {
// stuff
app.listen(3000);
} catch (error) {
// stuff
}
}
void start();
src/app.ts :
import express from "express";
// other import
const app = express();
// stuff
app.use("*", (_req, res) => {
res.status(404).end();
});
export default app;
When I try to start the server, this error occurs :
Error: ERR_MODULE_NOT_FOUND path\to\project\src\app path\to\project\server.ts module
Am I missing something obvious ?
Turns out I fixed the issue when I changed the script for
"scripts": {
"start": "node --experimental-specifier-resolution=node --loader ts-node/esm server.ts",
}
Adding --experimental-specifier-resolution=node fix it.
A fix for native module resolution on node >= 14.13.1 was merged to master in #1136. npm can install directly from github instead of npmjs.com so you can start using the fix today.
Is there an ETA for that fix to be released?
Installing straight from GitHub does not work for me - it runs postinstall hooks that fail.
@felixfbecker interesting. If installing from git is ever broken, that is a bug and we can track it with a bug report. As far as I'm concerned, we should always allow git installations for situations exactly like this one.
Are you able to open a bug report with a transcript of the errors you're seeing?
Also, to unblock yourself, our CI (GitHub Actions) uploads a tarball which you can download and install in your projects via local path.
@felixfbecker git installs are working on my machine.
cspotcode@cbserver:/d/dev/temp$ npm install https://github.com/TypeStrong/ts-node
npm WARN [email protected] requires a peer of typescript@>=2.7 but none is installed. You must install peer dependencies yourself.
npm WARN temp No description
npm WARN temp No repository field.
npm WARN temp No license field.
+ [email protected]
added 8 packages from 40 contributors and audited 8 packages in 42.895s
found 0 vulnerabilities
cspotcode@cbserver:/d/dev/temp$
Can confirm that the version from GitHub works like expected on node v14.14.0 / v15.2.0
package.json
"type": "module",
"dependencies": {
"typescript": "^4.0.5",
},
"devDependencies": {
"ts-node": "git+https://github.com/TypeStrong/ts-node.git",
},
"scripts": {
"start": "node --experimental-specifier-resolution=node --loader ts-node/esm ./src/server.ts"
}
tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"allowJs": false,
"rootDir": "src",
"strict": true,
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"exclude": [
"test"
]
}
Meantime the same setup with the version from npm throws an error:
(node:59886) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
TypeError [ERR_INVALID_RETURN_PROPERTY_VALUE]: Expected string to be returned for the "format" from the "loader getFormat" function but got type object.
at Loader.getFormat (internal/modules/esm/loader.js:110:13)
at Loader.getModuleJob (internal/modules/esm/loader.js:230:20)
at ModuleWrap.<anonymous> (internal/modules/esm/module_job.js:53:21)
at async Promise.all (index 0)
at link (internal/modules/esm/module_job.js:58:9)
Hello and thanks for all the hard work done here!
I've found the following issue but I'm not sure if it's strictly related to ESM support or it also affects Commonjs.
When using raw node (>= 12), adding a query string to a dynamic import bypasses the module cache:
// my-module
console.log('hey')
export default {}
await import('./my-module?query=1') // logs 'hey'
await import('./my-module?query=2') // logs 'hey' again
However, when using ts-node the module is still cached:
await import('./my-module?query=1') // logs 'hey'
await import('./my-module?query=2') // nothing happens
This behavior is very useful for hot module reloading during development.
Is it possible to fix it or is caching required in ts-node?
Thanks.
@frandiox this should be possible in ts-node. Here are some pointers to help you investigate. I think if you add a few logging statements to our ESM loader, you should be able to figure out what's happening and why it's caching. I wonder if query parameters are being stripped at some point in our loader. Or, it could be a bug in node which only appears when using loader hooks, since node's loader hooks are still experimental.
Here is where we create the 3x loader hooks that we use: resolve, getFormat, and transformSource
https://github.com/TypeStrong/ts-node/blob/master/src/esm.ts#L22
In resolve, we are responsible for transforming the import specifier into a URL. We delegate to nodeResolveImplementation.defaultResolve, which has been copy-pasted out of node's own source code. If node fixed a bug recently, we may need to copy-paste the bugfix into ts-node.
I think if you add logging statements within our resolve hook, you should be able to see if the query parameters are being stripped from the URL. If they are, that seems like a good indication of a bug. Then we can check node's issue tracker, see if it was a node bug that was fixed, and copy-paste their updated implementation.
Hello! I wonder when are you planning to distribute it to npm. installing with github is fine in local, but failed in docker.
FROM node:14
RUN npm install https://github.com/TypeStrong/ts-node.git
Thanks!
Try downloading the prebuilt package from our GitHub Actions nightly
builds. It is uploaded as an artifact. npm can install from a local file,
so you can download the package into your project and tell npm to install
it.
On Thu, Nov 26, 2020, 10:55 AM Deokseong Kim notifications@github.com
wrote:
Hello. When are you planning to distribute it to npm? installing with
github is fine in local, but failed in docker.FROM node:14
RUN npm install https://github.com/TypeStrong/ts-node.git—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-734374070,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAC35OBVBAP6IHWZ4YDAIIDSRZ25JANCNFSM4MGJCWPA
.
Oh, right. I will use the artifacts. thank you for quick answer
There is minimal loader to avoid this error
NODE_OPTIONS='--experimental-specifier-resolution=node loader=./loader.mjs'
// support for extensionless files in "bin" scripts
export async function getFormat(url, context, defaultGetFormat) {
const format = await defaultGetFormat(url, context, defaultGetFormat);
if (!format.format) {
format.format = 'commonjs';
}
return format;
}
v9.1.0 is out with several ESM related updates and fixes.
https://github.com/TypeStrong/ts-node/releases/tag/v9.1.0
One big one: the bulk of our ESM resolver is copy-pasted from node's own source code, to ensure we behave as closely as possible to vanilla node. This copy-pasted code has been updated to the latest from node 15. Hopefully it ensures we continue to behave the same as the latest node, but it's possible that it broke something. Remember that node's ESM loader functionality is experimental. ts-node's ESM support relies on this, so it is also experimental, and you may encounter breaking changes even though this is a minor version bump.
Following these instructions, I am still having an issue when my script imports a node module that is using ESM. The module is not being recognized as being ESM. I've tried using several other packages with similar results.
My example repo is https://github.com/mondash/expo-ts-node-esm-script
This was run on Windows 10, but I'm having the same issue using Ubuntu 20.04 WSL2. Any guidance would be appreciated. Thanks a lot!
❯ yarn myScript
yarn run v1.22.10
$ node --loader ts-node/esm --experimental-specifier-resolution=node ./myScript.ts
(node:26436) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
file:///C:/sandbox/projects/expo-ts-node-esm-script/myScript.ts:2
import { StatusBar } from "expo-status-bar";
^^^^^^^^^
SyntaxError: Named export 'StatusBar' not found. The requested module 'expo-status-bar' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:
import pkg from 'expo-status-bar';
const { StatusBar } = pkg;
at ModuleJob._instantiate (internal/modules/esm/module_job.js:98:21)
at ModuleJob.run (internal/modules/esm/module_job.js:143:5)
at Loader.import (internal/modules/esm/loader.js:165:24)
at Object.loadESM (internal/process/esm_loader.js:68:5)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
@mondash this error explains what's going on and offers a solution:
SyntaxError: Named export 'StatusBar' not found. The requested module 'expo-status-bar' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:
expo-status-bar is a CommonJS module, which we know from looking at its package.json
https://unpkg.com/browse/[email protected]/package.json
Let me know if this fix works for you.
@cspotcode I know type: "module" is not in the package.json, but it looks like this _is_ using ES syntax. Doing the fix mentioned in the error output from my above comment gives me this error (since it's trying to load CJS).
yarn run v1.22.10
$ node --loader ts-node/esm --experimental-specifier-resolution=node ./myScript.ts
(node:8) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
(node:8) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
C:\sandbox\projects\expo-ts-node-esm-script\node_modules\expo-status-bar\build\StatusBar.js:1
export * from './StatusBar.types';
^^^^^^
SyntaxError: Unexpected token 'export'
at wrapSafe (internal/modules/cjs/loader.js:979:16)
at Module._compile (internal/modules/cjs/loader.js:1027:27)
at Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
at Object.require.extensions.<computed> [as .js] (C:\sandbox\projects\expo-ts-node-esm-script\node_modules\ts-node\src\index.ts:1045:43)
at Module.load (internal/modules/cjs/loader.js:928:32)
at Function.Module._load (internal/modules/cjs/loader.js:769:14)
at ModuleWrap.<anonymous> (internal/modules/esm/translators.js:192:29)
at ModuleJob.run (internal/modules/esm/module_job.js:146:23)
at Loader.import (internal/modules/esm/loader.js:165:24)
at Object.loadESM (internal/process/esm_loader.js:68:5)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
When the extension is .js, then node uses the nearest package.json to decide if the file should be loaded as ESM or CommonJS. This behavior is mandated by node. If package.json tells node to load something as CommonJS, and if that file has ESM syntax in it, then node will throw this error. This behavior is controlled by node and is not related to ts-node. https://nodejs.org/dist/latest-v15.x/docs/api/esm.html
I have a script that uses ESM and I added the Shebang to it. The local (same folder) tsconfig is using mode:commonJS whereas the main tsconfig (cwd) is not. So I want to invoke the script with the local tsconfig.
I was expecting that running ts-node would make it work given I have the shebang there but it doesn't.
Is seems like I have to invoke it with ts-node-script. Is that intentional?
Our readme explains the behavioral differences between ts-node-script and
ts-node.
Additionally, node's interface prevents the bin entrypoints from installing
the ESM loader, because node does not allow us to install the ESM loader at
runtime; only via CLI flag. So it sounds like you are not actually using
our ESM loader.
If you have more questions, please make a post in our Discussion forum.
You can see it linked from this repository's homepage. "Discussion" is a
new github feature we're using to make how-to information more discoverable.
On Fri, Dec 25, 2020, 12:49 AM ron23 notifications@github.com wrote:
I have a script that uses ESM and I added the Shebang to it. The local
(same folder) tsconfig is using mode:commonJS whereas the main tsconfig
(cwd) is not. So I want to invoke the script with the local tsconfig.
I was expecting that running ts-node would make it work given I have the
shebang there but it doesn't.
Is seems like I have to invoke it with ts-node-script. Is that intentional?—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-751178630,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAC35OBRQ5VTYTQC6EDKGOLSWQRXLANCNFSM4MGJCWPA
.
Just wanted to point out that the popular package lodash-es isn't working:
$ node --loader ts-node/esm --experimental-specifier-resolution=node server.ts
(node:19045) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
(node:19045) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
/path/to/project/node_modules/lodash-es/lodash.js:10
export { default as add } from './add.js';
^^^^^^
SyntaxError: Unexpected token 'export'
at wrapSafe (node:internal/modules/cjs/loader:1024:16)
at Module._compile (node:internal/modules/cjs/loader:1072:27)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1137:10)
at Module.load (node:internal/modules/cjs/loader:973:32)
at Function.Module._load (node:internal/modules/cjs/loader:813:14)
at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:199:29)
at ModuleJob.run (node:internal/modules/esm/module_job:152:23)
at Loader.import (node:internal/modules/esm/loader:166:24)
Is this an issue with the package.json that lodash-es uses?
Do you know if this breakage is our fault or lodash-es's fault? We'll need
to narrow this down.
On Sat, Dec 26, 2020, 10:46 PM Harsh Pandey notifications@github.com
wrote:
Just wanted to point out that the popular package lodash-es
https://www.npmjs.com/package/lodash-es isn't working:$ node --loader ts-node/esm --experimental-specifier-resolution=node server.ts
(node:19045) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Usenode --trace-warnings ...to show where the warning was created)
(node:19045) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
/path/to/project/node_modules/lodash-es/lodash.js:10
export { default as add } from './add.js';
^^^^^^SyntaxError: Unexpected token 'export'
at wrapSafe (node:internal/modules/cjs/loader:1024:16)
at Module._compile (node:internal/modules/cjs/loader:1072:27)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1137:10)
at Module.load (node:internal/modules/cjs/loader:973:32)
at Function.Module._load (node:internal/modules/cjs/loader:813:14)
at ModuleWrap.(node:internal/modules/esm/translators:199:29)
at ModuleJob.run (node:internal/modules/esm/module_job:152:23)
at Loader.import (node:internal/modules/esm/loader:166:24)Is this an issue with the package.json that lodash-es uses?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-751422356,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAC35OGKP66USWRTVZWBTXDSW2UZFANCNFSM4MGJCWPA
.
@hpx7 I did a quick test and it is working for me.
This is the script I ran:
#!/usr/bin/env bash
set -ex
echo '{"type": "module"}' | tee package.json
echo '{"compilerOptions": {"module": "esnext"}}' | tee tsconfig.json
echo 'import * as _ from "lodash-es"; _.memoize(console.log.bind(console))("hello world");' | tee index.ts
npm install ts-node typescript @types/lodash-es lodash-es
node --version
node --loader ts-node/esm --experimental-specifier-resolution=node ./index.ts
This is the output:
❯ ./run.sh
+ echo '{"type": "module"}'
+ tee package.json
{"type": "module"}
+ echo '{"compilerOptions": {"module": "esnext"}}'
+ tee tsconfig.json
{"compilerOptions": {"module": "esnext"}}
+ echo 'import * as _ from "lodash-es"; _.memoize(console.log.bind(console))("hello world");'
+ tee index.ts
import * as _ from "lodash-es"; _.memoize(console.log.bind(console))("hello world");
+ npm install ts-node typescript @types/lodash-es lodash-es
up to date, audited 13 packages in 2s
found 0 vulnerabilities
+ node --version
v15.4.0
+ node --loader ts-node/esm --experimental-specifier-resolution=node ./index.ts
(node:1603) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
hello world
Interesting, it works with import * as _ from "lodash-es" but the following throws an error:
import lodash from "lodash-es";
const { memoize } = lodash;
EDIT:
Here's my tsconfig.json:
{
"compilerOptions": {
"esModuleInterop": true,
"module": "esnext",
"strict": true,
"target": "esnext",
"moduleResolution": "node"
}
}
@hpx7 Let's move this conversation to the Discussion forum, because it seems like it's not a problem with our ESM loader, rather a question about node and ESM in general.
The Discussion forum is a recent addition, and I explained why we're using it over here: #1174
@hpx7 @cspotcode are we stuck on commonjs? Did you find any solution to use type=module in package.json with ts-node?
@damianobarbati Have you read the beginning of this thread? It has detailed documentation for using type=module with ts-node. We are not stuck on commonjs.
@cspotcode i think we need to open a new issue for this and in that start with a condenset intro of this issu else all get lost, maybe switch that into a github discussion and link it
Personally, I'd rather fix any inaccuracies in the issue's description, particularly the "Usage" and "Pending Development Work" sections. I have updated the issue's first post over time, the goal being to keep it accurate. Are there inaccuracies in those 2 sections, particularly ones leading to recent questions?
node is forcing this feature to be experimental and unstable, since their loader mechanism is still experimental and unstable. That's why I'm keeping the documentation here and not in our README.
EDIT: to clarify, the goal is for bleeding-edge adopters of the ESM loader to read that first "Usage" section and get all the information they need. They should not have to read the entire thread.
@cspotcode thanks, I almost managed to make it working. But... how are you dealing with this common case?
A commonjs module must be imported as a whole and then be destructured.
Typescript yells using not-imported-but-destructured classes, because it recognize them as values (suggesting to use typeof).

That looks like a more general issue, not something specifically related to ts-node. You would have to solve that problem even if you were using tsc && node. I can't dedicate time to answering non-ts-node questions. I recommend asking in the TypeScript Community Discord server.
Hello from just another ts-node user, wanted to share a small tip that helped me get ESM support working on my project today.
My project uses [email protected] and I tried to follow the instructions above under "Usage".
Eventually I was able to get tsc happy but with ts-node was facing this:
node --loader ts-node/esm ./src/index.ts
(node:95996) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
TypeError [ERR_INVALID_RETURN_PROPERTY_VALUE]: Expected string to be returned for the "format" from the "loader getFormat" function but got type object.
at Loader.getFormat (internal/modules/esm/loader.js:111:13)
at Loader.getModuleJob (internal/modules/esm/loader.js:231:20)
at ModuleWrap.<anonymous> (internal/modules/esm/module_job.js:58:21)
at async Promise.all (index 2)
at link (internal/modules/esm/module_job.js:63:9)
Luckily it turned out using [email protected] (EDIT: later 9.1.1) (thanks) via
"resolutions": {
"ts-node": "9.1.1"
}
fixed things. (This is using yarn).
node --loader ts-node/esm src/index.ts
(node:96471) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
🚀 GraphQL endpoint ready at http://localhost:4000/graphql
Hopefully this helps future ESM curious folks. Good luck, official ESM has been a long time coming and it seems to be more or less here 🥳
@onpaws Just to be sure I understand, are you saying that upgrading to 9.1 fixed the bug for you? We made big improvements to the ESM loader in 9.1.0. An npm 7 bug caused a broken npm 9.1.0 tarball, so we had to republish as 9.1.1 without any code changes.
the weird thing is that 9.1.0 was a completely broken release; npm 7 had a bug that created a broken tarball, so we had to republish as 9.1.1 without any code changes. (https://github.com/TypeStrong/ts-node/compare/v9.1.0..v9.1.1)
Can you double-check if you're using 9.1.0 or if it's 9.0.0? We introduced some big ESM changes in 9.1.0, so if downgrading to 9.0.0 fixed this, then that helps me narrow down the bug.
Also, can you confirm the exact version of node you're using? (node --version)
Here are our release notes enumerating all changes:
https://github.com/TypeStrong/ts-node/releases~~
Yep, all I'm saying is that upgrading from 9.0 which I think was depended on by ts-node-dev, to 9.1.1 using yarn's resolution field in package.json let me run my project.
$ node --version
v14.15.4
(Non sequitur, I'm not sure I fully understand the reasons but I noticed while [email protected] works great (thanks for your efforts on ESM suport @cspotcode), it seems ts-node-dev apparently depends on calling require() to do its thing. Problem is Node, at least this version, seems to choke when we ask it to require() ESM. TIL.
I'm intending to experiment with things a bit more but in case anyone else is interested, it seems tsc-watch appears to be a tentative drop-in replacement for ts-node-dev.)
Interesting, thanks for the speedy reply and for the information about ts-node-dev. I'll take a look at their code.
ESM loading is, indeed, an entirely different codepath and hooking system in node. (hence the annoying --loader flag which we can't install at runtime) So I suspect that they need to make some major changes to be compatible with ESM.
I commented on ts-node-dev to explain why I suspect it's incompatible with native ESM.
https://github.com/wclr/ts-node-dev/issues/212#issuecomment-761418434
when I import a class from b.ts into a.ts then the import has to include a .ts extension, for example
import {B} from './b.ts';
otherwise I get the error
Error: ERR_MODULE_NOT_FOUND
it works without .ts extension if I am only importing interfaces or types from b.ts
to make the compiler happy I then have to put // @ts-ignore above the import
is that normal behavior or did I do something wrong?
Not sure if this related to your specific issue, but one slightly counterintuitive thing I learned yesterday is that in an apparent effort to unify browser and Node.js semantics there's been a change in the way resolution works. Now, you have to import the full path, _including the .js extension_. Even when writing TypeScript.
Like @cspotcode said:
Include file extensions in your import statements, or pass --experimental-specifier-resolution=node Idiomatic TypeScript should import foo.ts as import 'foo.js'; TypeScript understands this.
e.g. index.ts:
before:
import blah from './myModule'
after:
import blah from './myModule.js'
If you know you're never going to target a browser, you could use --experimental-specifier-resolution=node but for my new projects I'm going to try and get in the habit of using .js to make things "more isometric by default" from now on.
I speculate this is because browsers historically request precise file paths; servers tend not to do filename munging by default.
By targeting the lowest common denominator modern JS can move towards becoming more cross platform ('isometric') in the future.
when i use --experimental-specifier-resolution=node I can omit the .js on imports for .js files
specifically what did not work for me if in b.ts I have
export class B {}
and in
a.ts
import {B} from './b'
in this case I have to
// @ts-ignore
import {B} from './b.ts'
The TypeScript compiler will never rewrite your import statements. If you import from "this looks weird"; then it will emit import from "this looks weird"; and the runtime (node) will have to understand it. In the common, pre-compiling use-case, the runtime needs to see foo.js (with a .js extension) so your import statements must be foo.js even though you are attempting to load code declared in foo.ts. The TypeScript compiler's typechecker understands the .js <-> .ts and will throw a type error if your imports have .ts because, in the common pre-compiling case, the runtime (node) requires your imports to have a .js extension.
Explained a bit more here:
https://github.com/nodejs/modules/issues/351#issuecomment-621257543
Just to close the loop on import statements, see also #783 .
From an outside perspective, it still sort of looks like the Typescript and ts-node projects are pointing fingers at each other and firmly saying "the other guy is wrong" -- both the linked issue and https://github.com/microsoft/TypeScript/issues/16577 have been closed as wontfix. I totally get the arguments on both sides, and I think I even mostly understand how we got here, but I'm not very happy with the result, which AFAICT is that it is not currently possible to write import specifiers that "just work" on every platform without adding something like a 3rd party tsc plugin.
@thw0rted Highlighting my comments here: https://github.com/TypeStrong/ts-node/issues/783#issuecomment-755064082
Given that this is the native ESM thread, it will help everyone for us to be more precise. Are you hoping for a change to the ESM loader's behavior? Or for something else?
For #783, I don't think we're saying TS is wrong, at least not recently. Rather, the issue hasn't received any attention because no one has sent us a pull request. Unfortunately, that is often the problem: despite an expressed desire for a feature, the community does not desire it enough to implement it. I'd like to think we're very encouraging of external contributions, (there are a few recent examples) but they don't happen often.
Another obvious factor is resources: I don't get paid for ts-node, unlike the TS team. Sometimes, when we try to keep ts-node simple, it is with this harsh reality in mind. But again, in the case of #783, we simply haven't received a pull request.
I'm happy to explain ts-node's codebase if it will help external contributors. Feel free to open a Discussion thread with any questions.
Additionally, it looks like #783 was closed by the person who submitted it; I don't think it's marked as wontfix.
@emiliosp this is what happens when tooling overtakes code 🤔
Thanks, @cspotcode , that helps. Certainly, I didn't intend to come off as complaining about the work you folks have done on ts-node. Volunteer-maintained projects are the lifeblood of open source, and it really sucks when people show up demanding unpaid work.
I've been following this issue both here and on the TS side for about a year now, but activity is intermittent and every few months I have to skim back over to refresh myself. If I understand correctly, it looks like the way forward is to somehow hook Node's require logic so that requiring a .ts extension will hand off to ts-node, including in the ESM case and sub-processes, but some part of that's either non-trivial or not currently supported (by Node's extension API?).
Do other compile-to-JS languages (I dunno, coffeescript?) have an analog of ts-node for on-the-fly compilation? (I realized as I wrote that sentence, I don't know what proper noun I'd use to fill in "ts-node is a _____ for loading Typescript files in nodeJS". Loader? Compiler plugin?) If those exist, do they have a solution for the ESM require problem that ts-node could use as a model? There are so many compile-to-JS languages out there, I guess I just assumed that surely ts-node can't be the first case where people want to do this. Maybe it is, though?
Transforming a string of .ts into .js is the easy part, relatively speaking. Resolving import specifiers to paths on disk, especially when those paths may be virtual, is the messy part. (compiler output is nonexistent on the filesystem when running in ts-node)
I think what people are hoping for is that our require() hook is updated to perform the same resolutions as our ESM hook already does. That is to say, our ESM hook is already doing the right thing today and should not change, but for the require() hook this is a major new bit of code.
Our ESM loader hook already implements a resolver which is responsible for taking import from "<string goes here>" and figuring out which file on the filesystem is being referred to. If you write import from "./foo.js" it will "resolve" that to foo.ts if it exists. This matches what tsc already does, but it happens within node's resolver. You import with the .js extension, and it resolves to the .ts file on disk. This seems counter-intuitive until you remember that, after compilation, the .js extension is correct, and TypeScript refuses to modify perfectly valid JS.
Unfortunately, modifying node's resolver is a very annoying undertaking. To do it for ESM, we had to copy-paste a lot of node's source code into ts-node to make sure we behave identically except for adding .js -> .ts resolution. And node's ESM and CJS resolvers are different things. For ESM, it's part of the hooking API. For CommonJS, it requires monkey-patching require('module')._resolveFilename
To sum up, I think the ESM loader is already doing the right thing. And modifying node's CJS resolver to match is non-trivial but probably what people are hoping to implement.
Apologies if this is not useful feedback, I've tried this in our project following the setup in comment 1 and everything works fine 🌟
@pvarga-dni thank you, I'm glad to hear it!
If you know you're never going to target a browser, you could use
--experimental-specifier-resolution=node
Passing that option also solved this error for me, caused by import { foo } from '../':
.../node_modules/ts-node/dist-raw/node-esm-resolve-implementation.js:370
const err = new ERR_UNSUPPORTED_DIR_IMPORT(path, fileURLToPath(base));
__dirname and __filename (among others?) are both undeclared/undefined when trying to use ts-node and esmodules as described herein, although it works fine changing my imports to require and then running ts-node --transpile-only FILE.ts? ReferenceError: __dirname is not defined
That is correct behavior, as per the docs: https://nodejs.org/api/esm.html
On Sun, Feb 28, 2021, 7:43 AM tyler1205 notifications@github.com wrote:
__dirname and __filename (among others?) are both undeclared/undefined
when trying to use ts-node and esmodules as described herein, although it
works fine changing my imports to require and then running ts-node
--transpile-only FILE.ts? ReferenceError: __dirname is not defined—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-787446540,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAC35OCC6SVFS7JVZUHS3HLTBI3A5ANCNFSM4MGJCWPA
.
I'm getting ERR_MODULE_NOT_FOUND when I have recursive imports, i.e.:
file1.ts:
import file2 from './file2'
...
file2.ts:
import file1 from './file1`
...
In my code logic I don't have recursive calls (just one call in stack, making sure both files were used), and in real web application it works fine. But by executing node --loader ts-node/esm file1.ts I'm getting this error...
UPD. Whoops, nope. It seems it doesn't work at all, even if I'm getting rid of this recursive hack.
Please share the full error. I can already see one potential problem in your code, but before I offer a diagnosis, I want to have the full context.
$ node --loader ts-node/esm src/db/json/createPosts.ts
(node:26096) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
C:\Users\Jerry\projects\jerrygreen.me\node_modules\ts-node\dist-raw\node-esm-resolve-implementation.js:765
throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base));
^
Error: ERR_MODULE_NOT_FOUND ~ C:\Users\Jerry\projects\jerrygreen.me\src\db\json\createPosts.ts
at packageResolve (C:\Users\Jerry\projects\jerrygreen.me\node_modules\ts-node\dist-raw\node-esm-resolve-implementation.js:765:9)
at moduleResolve (C:\Users\Jerry\projects\jerrygreen.me\node_modules\ts-node\dist-raw\node-esm-resolve-implementation.js:806:18)
at Object.defaultResolve (C:\Users\Jerry\projects\jerrygreen.me\node_modules\ts-node\dist-raw\node-esm-resolve-implementation.js:920:11)
at C:\Users\Jerry\projects\jerrygreen.me\node_modules\ts-node\src\esm.ts:55:38
at Generator.next (<anonymous>)
at C:\Users\Jerry\projects\jerrygreen.me\node_modules\ts-node\dist\esm.js:8:71
at new Promise (<anonymous>)
at __awaiter (C:\Users\Jerry\projects\jerrygreen.me\node_modules\ts-node\dist\esm.js:4:12)
at resolve (C:\Users\Jerry\projects\jerrygreen.me\node_modules\ts-node\dist\esm.js:31:16)
at Loader.resolve (internal/modules/esm/loader.js:86:40)
I have further imports in this file:
import fs from 'fs'
import getPosts from '~/db/gh/getPosts' // <-- this one is problematic
import { Article } from '~/interfaces/blog'
If I get rid of getPosts, then it works fine. But honestly getPosts is pretty big to find out what is breaking it by just by commenting out code parts. I made sure it's not recursive imports (it was my assumption but I got rid of them, and the error is still there...). Can't say for sure exact reason yet, sorry. If you can tell anything just from this stacktrace, it would be cool
I saw one person said he had to add .ts extension, - I tried it, but it didn't help.
Check out the file extension requirements from the instructions above:

Here is another explanation of this requirement, which actually comes from TypeScript, not ts-node:
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-761685408
Also check out https://github.com/TypeStrong/ts-node/discussions/1263; it may be relevant but we won't be able to offer advice without seeing your tsconfig
yarn add ts-node it is!--experimental-specifier-resolution=nodeRegarding my tsconfig.json, - I indeed used some custom paths, thank you for sharing, so I also added -r ts-node/register -r tsconfig-paths/register, i.e. my command looks like a final boss right now:
node --loader ts-node/esm --experimental-specifier-resolution=node -r ts-node/register -r tsconfig-paths/register src/db/json/createPosts.ts
And yes, I didn't forget to yarn add -D tsconfig-paths. But despite all that I'm still getting the same exact ERR_MODULE_NOT_FOUND error with the same exact message.
Here's my tsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"~/*": ["./src/*"],
"#/*": ["./public/*"]
},
"allowJs": true,
"alwaysStrict": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"jsx": "preserve",
"lib": ["dom", "es2017"],
"module": "esnext",
"moduleResolution": "node",
"noEmit": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"target": "esnext"
},
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
Does tsconfig-paths/register support native ESM? They would need to implement their own --loader. I'm pretty sure they don't.
Using "paths" is, in general, not advised. You'll just be causing yourself extra effort. Better to keep it simple and relative paths. I understand why people want to use "paths" but I still advise against it.
As you know, node's ESM loader feature is currently experimental. This means that it will have breaking changes in the future, it currently has bugs, and it has limitations that will need to be fixed. (which may cause those breaking changes) You pretty much need a --loader to tell node how to do custom path resolution. So you're compounding the risk.
Check out https://nodejs.org/api/packages.html#packages_subpath_patterns It might do the trick, but I honestly don't know if it's capable of the kind of mapping you want to do.
This is the "correct" way to do path mappings: configure your runtime -- whether it's webpack, node, deno, browserify -- to do the mappings. Then configure your tsconfig to match. This comes back to our canned answer from TypeScript Discord: "paths are not magic."
That is my very prescriptive advice. Of course you're free to do whatever you want, but I hope this is helpful.
paths are not magic
Using "paths" is, in general, not advised [because it adds complexity and overall it's some fucking magic]
paths are not magic
Am I missing something here?
I actually like "paths are not magic" statement. And I don't like your "paths aren't advised... extra effort" statement... This way I may say: ts-node isn't advised. It adds complexity, extra efforts. Also typescript is not advised. Adds complexity, too. Programming is not advised in overall: too complex. Yeah? I completely agree! But I still want that.
This is the "correct" way to do path mappings: configure your runtime -- whether it's webpack, node, deno, browserify -- to do the mappings. Then configure your tsconfig to match
You may not worry about the rest configuration of mine: those paths already work just fine in my web app. Webpack setup is fine. I just wanted to execute some script with cli-command, and wanted to use ts-node for that. Thank you for your detailed feedback though, - I'm actually surprised I'm still getting this bug, despite following all your recommendations. I guess I'll re-check this feature sometime later when it's more robust
You're saying paths are magic whereas I'm saying they're not. I think that's the confusion.
Here's some more reading where the TypeScript team explains the purpose of these path-mapping flags:
https://www.typescriptlang.org/docs/handbook/module-resolution.html#additional-module-resolution-flags
If you have more questions, I recommend talking to people on the TypeScript Community Discord.
This comment in the README
There are two options when using import statements: compile them to CommonJS or use node's native ESM support.
seems incredibly unintuitive to me. I have long been under the impression that the entire purpose of ts-node was to be able to invoke a .ts file without having to compile it to JS. If I am to compile a .ts file to a .js file, then I would just run node on that JS file rather than using ts-node, no? I thought that ts-node at runtime would take a .ts file transpile it inline to run?
ts-node compiles in-memory. In other words, it uses the typescript API to convert a string of typescript into a string of javascript, passing it to node to execute, without writing it to the filesystem. This conversion from typescript to javascript is still "compilation" and is still driven by the compiler options you have set. For example, the "target" option determines whether or not the ?. operator is downleveled or not, which is required for older node versions.
Given this information, do you have a suggestion for how we should rewrite the README? Pull requests are welcome.
I'm trying to use this workaround to dynamically import an ESM file (for a unit test) in a project that otherwise generates CJS:
If they can't do that, we can recommend the following workaround:
// This is in a CommonJS file: const dynamicallyImportedEsmModule = await require('ts-node').importESM('./specifier-of-esm-module', module);
However I get an en error that importESM is not a function. I also can find no reference to it in this repo (expect this issue). Am I missing something or is this not implemented?
I'm using ts-node 9.1.1
@realityking yeah, that was an idea that I ultimate decided not to implement. Really it's a limitation of typescript. It's out of scope for ts-node to be implementing workarounds for typescript issues, since it's more features to maintain and might cause confusion in our issue tracker.
Fortunately, it should be easy to implement yourself, and you can even publish it as a reusable npm module if you want. (perhaps one already exists?) Put this in a file that is not transformed by ts-node / typescript.
// import-esm.js
exports.importEsm = function(specifier, module) {
return import(import.meta.resolve(specifier, module.filename);
}
I have not tested this, so you may need to do some troubleshooting, but this code snippet should be clear enough. Essentially, import() will be transformed by TypeScript into require(), so we must avoid that by placing the import() in another file that does not get compiled, import-esm.js. Then we can use the helper function anywhere we want.
@realityking I've shared this recipe here: https://github.com/TypeStrong/ts-node/discussions/1290
If you have follow-up questions, please post them there, because it will help us update the example and the explanation to help you and anyone else who hits this issue.
I got some feedback, and I am open to implementing the suggested changes I have in mind for a few of the issues.
That being said, I could be wrong, and would love to some correction in that case.
ts-node CLI arguments to node --loader
When using node --loader is there a way to pass arguments to ts-node without using tsconfig.json?
$ node --loader ts-node/esm index.ts
Using tsconfig.json is not an option because of how extend works.
We're supplying a tsconfig.json as a part of a dependency.
A project is supposed to use or extend that, but the ts-node part of it does not get included.
I know that ts-node tries to use the same approach as tsc when looking for it the tsconfig.json or that you can supply it with via TS_NODE_PROJECT.
It would be beneficial to turn it around and instead supply those as arguments as mentioned above.
As I understand, this could very well be a limitation of node --loader that you can't pass arguments to the ts-node/esm loader, in which case, I am open to helping adding a PR for a different way that you might be comfortable with.
If arguments is not a possibility, could we use a different ENV variable to add ts-node configs separate from TS_NODE_PROJECT, but make tsconfig.json settings take precedence/override if necessary.
A somewhat inferior alternative would be a that TS_NODE_PROJECT instead takes its own config file that contains a relative path to tsconfig.json instead.
An example:
{
"ts-node": {
"transpileOnly": true
},
"tsconfig": "../../tsconfig.json"
}
Don't worry about the relative path, it's going to get sorted by the build script.
I am open to help with a PR for this, or any other alternative approaches you had in mind, but mostly hoping to be wrong and have an existing way to resole this.
@seivan
When using node --loader is there a way to pass arguments to ts-node without using tsconfig.json?
The recommended approach is using tsconfig.json and/or environment variables. You have 2x main options: write a new tsconfig and tell ts-node to load it via TS_NODE_PROJECT, or specify everything via environment variables.
Node does not let us programmatically install an ESM loader at runtime the way we are able to install our CommonJS loader at runtime. Personally I think this is a flaw in node's API surface and they should add it. Once they do, users won't need to worry about the --loader flag because ts-node will be able to install the ESM loader at runtime, the same way we do for CommonJS. It will work the same both for ts-node and for node -r ts-node/register.
We deliberately avoid spawning a subprocess, because it added complexity and caused confusion in the past.
You could investigate adding "extends": support to our "ts-node" loading, so that one tsconfig extending another will merge the "ts-node" options specified in both, the same way that "compilerOptions" are merged.
You also have the option of copy-pasting the necessary config values into a new file instead of trying to make "extends" work. Sometimes that's the most pragmatic approach.
See also: #1204, which proposes a simpler, more intuitive way to pass flags to ts-node via env vars.
@seivan
See also: #1204, which proposes a simpler, more intuitive way to pass flags to ts-node via env vars.
Yeah so I assume this is what you meant earlier with or specify everything via environment variables
That would be useful on its own in the future given the flaw in node's API surface.
You could investigate adding
"extends":support to our"ts-node"loading, so that one tsconfig extending another will merge the"ts-node"options specified in both, the same way that"compilerOptions"are merged.
Yep, I was looking into that with tsc -p tsconfig.json to see what got incuded, and ts-node was not. But since the values aren't for tsc itself but ts-node it shouldn't be an issue to simply look at the extends portion of the file and source it.
Depending on how ts-node reads the config, if it's actually looking at the source file one could simply refer to the file defined under extend to look for the ts-node key unless one already exists in the loaded file in which case it takes precedence.
I don't mind doing a PR for this if you're comfortable with that approach?
Edit: I noticed an existing ProcessEnv interface that has the current TS_NODE_PROJECT also takes TS_NODE_TRANSPILE_ONLY
I assume you meant that one?
Yeah so I assume this is what you meant earlier with or specify everything via environment variables
I was actually referring to the various environment variables we already support, including TS_NODE_PROJECT, TS_NODE_COMPILER_OPTIONS, and others.
Yep, I was looking into that with tsc -p tsconfig.json to see what got incuded, and ts-node was not.
If you were using tsc --showConfig, you might like to know we have an unreleased ts-node --showConfig flag which includes "ts-node". You'll have to install ts-node from our github main branch instead of from npmjs.com (npm can install directly from git / github) See #1100
Feel free to tackle a PR for #1204; that would be helpful.
@cspotcode Ok, seems I misunderstood.
Just to clarify, you currently support some if not all of the CLI args as separate env variables, and #1204 is the idea of tackling them all in a single env variable using some form of argv parser implementation to pick them up, correct?
That looks like a much cleaner approach and I can get behind that, but for now I tackled
You could investigate adding "extends": support to our "ts-node" loading, so that one tsconfig extending another will merge the "ts-node" options specified in both, the same way that "compilerOptions" are merged.
Made a PR in https://github.com/TypeStrong/ts-node/pull/1292
It doesn't do any error handling, but not sure how much that would add. Either your file doesn't exists, or it does.
Regarding #1204 I could eventually look on that as well, do you know what takes precedence?
With that, we would also have tsconfig as well as API options.
Do they merge or does one have higher priority?
@seivan sounds good; let's move conversation to #1292 and #1204 to avoid spamming everyone here. Would you mind copy-pasting your questions above into the relevant issues as comments, and I'll respond there?
Thanks for sending a PR; I'll do my best to review it soon but things are busy for me so it may take me a little while.
@seivan, regarding extending tsconfig, might what you pointed out be the reason why transpileOnly didn't work for me if placed in the parent tsconfig.json?
Let's try to keep these conversations in the relevant tickets. If it's not
related to the ESM loader, it shouldn't be here. This thread is already
long enough as it is.
On Thu, Apr 8, 2021 at 6:16 PM Dan Dascalescu @.*>
wrote:
@seivan https://github.com/seivan, regarding extending tsconfig
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-812850743,
might what you pointed out be the reason why transpileOnly didn't work
for me if placed in the parent tsconfig.json
https://github.com/TypeStrong/ts-node/discussions/1276#discussioncomment-582804
?—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/TypeStrong/ts-node/issues/1007#issuecomment-816266164,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAC35OBRVSOX5UNH37EHYDLTHYTMXANCNFSM4MGJCWPA
.
Can I set the loader with NODE_OPTIONS?
Upd: yes. I just didn't know how to use spaces in Windows cmd. The right way is:
set "NODE_OPTIONS=--no-warnings --loader ts-node/esm"
This way both node lib/main.js and node main.ts work. (lib is my tsconfig outDir)
Is there a way to somehow specify --files option with node --loader ts-node/esm notation?
Does it support worker_threads?
@niyarlatotep ts-node is a JIT transpiler that will load and transpile TS in runtime to run on your NodeJS. So ts-node or the ESM loader should not impact any NodeJS core API or functionality. That said if you are on NodeJS 10+ it should support the Worker threads API https://nodejs.org/api/worker_threads.html
@evenfrost check out https://typestrong.org/ts-node/docs/configuration#ts-node-options-via-tsconfigjson-recommended
@niyarlatotep check out https://typestrong.org/ts-node/docs/recipes/other
The website is still a work-in-progress, but I believe those pages have the answers you need.
@niyarlatotep sorry for the previous answer, I just realized I misinterpreted it by reading @cspotcode answer. Thanks @cspotcode!
@cspotcode @felipeplets thanks!
Most helpful comment
v9.0.0 is out, including a few ESM improvements: https://github.com/TypeStrong/ts-node/releases/tag/v9.0.0