You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ESM in webpack.config.ts isn't supported if ts-node outputs ESM #2458
"module": "esnext" in your tsconfig.json, or any ES
regardless of your type config in package.json
ts-node will transpile the configuration into JavaScript, preserving export default, but the file is still being executed as CJS.
ESM config files have just recently been supported (#2381, v4.5.0) but my guess is that ts-node or webpack is running the transpiled file outside the type: module directory.
What is the current behavior?
[webpack-cli] Failed to load './webpack.config.ts' config
[webpack-cli] ./webpack.config.ts:2
import path from 'path';
^^^^^^
SyntaxError: Cannot use import statement outside a module
changed the title [-]ESM in webpack.config.ts isn't supported with module: "esnext" in tsconfig[/-][+]ESM in webpack.config.ts isn't supported if TS outputs ESM[/+]on Feb 21, 2021
changed the title [-]ESM in webpack.config.ts isn't supported if TS outputs ESM[/-][+]ESM in webpack.config.ts isn't supported if ts-node outputs ESM[/+]on Feb 21, 2021
Ping for what? Are you suggesting that using node --loader ts-node/esm node_modules/.bin/webpack --config webpack.config.ts instead of webpack is the nothing-to-fix-here solution?
For using ts-node and ECMA modules you need pass loader to Node.js (i.e. --loader ts-node/esm) otherwise it will not work, I just don't know what we can do here, we can't add loader in runtime, no API from Node.js here
Yes, but it generates commonjs format of the webpack config file, not webpack’s output, which is not transpiled by ts-node. In my (only) test, the webpack output is identical byte for byte.
Yes, it is not related to webpack output, it is related to output ts-node, i.e.
webpack.config.ts -> ts-node -> webpack.config.ts with ES modules format in memory -> webpack-cli -> import('path/to/webpack.config.ts') in our code -> ts-node returns ES modules format -> run webpack with your options
webpack-cli -> import('path/to/webpack.config.ts') in our code
We support commonjs and ES format, so firstly we try to loader using require, if it fails with error.code === 'ERR_REQUIRE_ESM' (special code for this case) we try to load it using import(). But here problems, without --loader=ts-node/esmimport() will not work with ts-node - need runtime hooks (described here TypeStrong/ts-node#1007 and marked not fixable).
I think you will faced with this issue in future many times for other packages with configurations.
Reacted by Erik J Brown, Arthur Henrique and nyngwang
Unfortunately the typescript compiler insists on converting dynamic import() into require() when using "commonjs" and we can't override that. However, I've explained a couple workarounds here: TypeStrong/ts-node#1290
I'm not sure if webpack has special-case logic for a CommonJS config file to return a promise, though.
The whole point using that inline setting instead of tsconfig is to avoid those errors in the real code
Using eval/Function is a really poor runtime workaround. I suggest using the ENV as suggested here instead and using non-workaround code in production.
The whole point using that inline setting instead of tsconfig is to avoid those errors in the real code
The tsconfig tweak I proposed is equivalent to the environment variable in that it only affects ts-node. But I see what you're saying: in refined-github, you are also using ts-node elsewhere, so you need ts-node to emit commonjs for webpack and esnext for ava. Could be done with TS_NODE_PROJECT in the ava config if you really wanted to drop the cross-env dependency, but I can't say if it's worth it. EDIT: and now I see that's already been done
This fix may reduce confusion in these situations: TypeStrong/ts-node#1229
When you try to require() a .ts file that should be ESM, we'll throw the correct ERR_REQUIRE_ESM even when --loader isn't set. At least it avoids the misleading Cannot use import statement outside a module However, it means we no longer allow .ts in an ESM directory ("type": "module") to be erroneously executed as CJS.
Using eval/Function is a really poor runtime workaround.
It's certainly not my first suggestion, and likely not relevant if "module": "commonjs" is already working. There is another suggestion which avoids Function.
All of this is ultimately working around node and typescript feature gaps, so here's hoping they fill them soon.
FYI as of today for me to be able to import an ESM module (webpack plugin) from webpack.config.ts, I needed to add the following to the tsconfig.json
{// ..."ts-node": {// Tell ts-node CLI to install the --loader automatically, explained below"esm": true,"compilerOptions": {"module": "CommonJS","moduleResolution": "NodeNext",}},// ...
It's based on your responses above. "ts-node" itself works instead of TS_NODE_COMPILER_OPTIONS as already mentioned here, plus "esm": true replaces the need for passing --loader ts-node/esm to Node (see https://typestrong.org/ts-node/docs/imports).
Activity
fregante commentedon Feb 20, 2021
Temporary solution:
[-]ESM in webpack.config.ts isn't supported with module: "esnext" in tsconfig[/-][+]ESM in webpack.config.ts isn't supported if TS outputs ESM[/+][-]ESM in webpack.config.ts isn't supported if TS outputs ESM[/-][+]ESM in webpack.config.ts isn't supported if ts-node outputs ESM[/+]Meta: Drop Rollup from tests /2
alexander-akait commentedon Feb 24, 2021
Interesting, somebody wrap code from
tstoalexander-akait commentedon Feb 24, 2021
Related TypeStrong/ts-node#1007, we need allow to pass
register, so you can usets-node/esmalexander-akait commentedon Feb 24, 2021
I'll investigate it in detail tomorrow, but to be honest look like
ts-nodeis not ready for usingimport()alexander-akait commentedon Mar 16, 2021
@fregante
Sorry for delay, I think nothing to fix on our side, I am using (and everything works fine):
We need Node.js loader for
tsfiles, otherwise it will not work.From ts-node docs:
alexander-akait commentedon Mar 16, 2021
Here interesting case, we can
requirefiles with ES module syntax, very weird, but it is behavior fromts-nodealexander-akait commentedon Mar 22, 2021
@fregante friendly ping
fregante commentedon Mar 22, 2021
Ping for what? Are you suggesting that using
node --loader ts-node/esm node_modules/.bin/webpack --config webpack.config.tsinstead ofwebpackis the nothing-to-fix-here solution?alexander-akait commentedon Mar 22, 2021
For using
ts-nodeand ECMA modules you need passloaderto Node.js (i.e.--loader ts-node/esm) otherwise it will not work, I just don't know what we can do here, we can't add loader in runtime, no API from Node.js here3 remaining items
fregante commentedon Mar 31, 2021
Indeed, with so many moving parts I can understand that this is not strictly something that can be fixed locally.
Yes, but it generates commonjs format of the webpack config file, not webpack’s output, which is not transpiled by ts-node. In my (only) test, the webpack output is identical byte for byte.
alexander-akait commentedon Mar 31, 2021
Yes, it is not related to
webpackoutput, it is related to outputts-node, i.e.webpack.config.ts->ts-node->webpack.config.tswith ES modules format in memory ->webpack-cli->import('path/to/webpack.config.ts')in our code ->ts-nodereturns ES modules format -> runwebpackwith your optionsWe support commonjs and ES format, so firstly we try to loader using
require, if it fails witherror.code === 'ERR_REQUIRE_ESM'(special code for this case) we try to load it usingimport(). But here problems, without--loader=ts-node/esmimport()will not work withts-node- need runtime hooks (described here TypeStrong/ts-node#1007 and marked not fixable).I think you will faced with this issue in future many times for other packages with configurations.
cspotcode commentedon May 23, 2021
Came across this issue and wanted to mention a couple helpful things:
Alternative to setting
TS_NODE_COMPILER_OPTIONS='{"module": "commonjs"}', you can add this override to yourtsconfig.jsonfile:Unfortunately the typescript compiler insists on converting dynamic
import()intorequire()when using "commonjs" and we can't override that. However, I've explained a couple workarounds here: TypeStrong/ts-node#1290I'm not sure if webpack has special-case logic for a CommonJS config file to return a promise, though.
fregante commentedon May 24, 2021
eval/Functionis a really poor runtime workaround. I suggest using the ENV as suggested here instead and using non-workaround code in production.cspotcode commentedon May 24, 2021
The tsconfig tweak I proposed is equivalent to the environment variable in that it only affects
ts-node. But I see what you're saying: in refined-github, you are also usingts-nodeelsewhere, so you needts-nodeto emit commonjs for webpack and esnext for ava. Could be done withTS_NODE_PROJECTin the ava config if you really wanted to drop thecross-envdependency, but I can't say if it's worth it. EDIT: and now I see that's already been doneThis fix may reduce confusion in these situations: TypeStrong/ts-node#1229
When you try to
require()a.tsfile that should be ESM, we'll throw the correctERR_REQUIRE_ESMeven when--loaderisn't set. At least it avoids the misleadingCannot use import statement outside a moduleHowever, it means we no longer allow.tsin an ESM directory ("type": "module") to be erroneously executed as CJS.It's certainly not my first suggestion, and likely not relevant if
"module": "commonjs"is already working. There is another suggestion which avoidsFunction.All of this is ultimately working around
nodeandtypescriptfeature gaps, so here's hoping they fill them soon.MikkCZ commentedon Jun 16, 2022
FYI as of today for me to be able to import an ESM module (webpack plugin) from
webpack.config.ts, I needed to add the following to thetsconfig.jsonIt's based on your responses above.
"ts-node"itself works instead ofTS_NODE_COMPILER_OPTIONSas already mentioned here, plus"esm": truereplaces the need for passing--loader ts-node/esmto Node (see https://typestrong.org/ts-node/docs/imports).node: v16.14.1
ts-node: v10.8.1
typescript: 4.7.3
webpack: 5.73.0
webpack-cli: 4.10.0
Update to web-ext-plugin version 2.x.x
Update to web-ext-plugin version 2.x.x (#271)
digiperfect-tech commentedon Mar 9, 2023
I still get
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts"after adding this to tsconfig.jsonAny pointers please?
MikkCZ commentedon Mar 9, 2023
@digiperfect-janak here is my full
tsconfig.json, but I basically followed what was mentioned above my comment.Do you have
ts-nodeinstalled? Does any of the above-mentioned solutions withTS_NODE_COMPILER_OPTIONSwork for you?digiperfect-tech commentedon Mar 9, 2023
Thanks. I will try with the extended version of tsconfig you shared. I do have ts-node installed.
TS_NODE_COMPILER_OPTIONS='{"module": "commonjs"}' node_modules/webpack/bin/webpack.js --mode=productiongave the following error:
[webpack-cli] Failed to load '/home..../webpack.config.ts' configIf I am not mistaken, bin/webpack.js is what is supposed to be invoked?
iclegane commentedon Nov 3, 2023
in package.json:
and my tsconfig:
This solved my problem
wojtekmaj commentedon Nov 16, 2023
I think I came up with a bit more elegant solution. Simply prepend your commands referencing
webpackwithNODE_OPTIONS='--loader ts-node/esm'If you had
or something similar in your tsconfig.json, you should remove it.
stasguma commentedon Jan 30, 2024
the best solution for me was:
install
cross-envandtsxpackagespackage.jsonpay attention to the equal sign after
--import, because all the examples I saw were without it and it caused an errortsconfig.jsondoesn't matterNode: 20.11.0
TypeScript: 5.3.3
cross-env: 7.0.3
tsx: 4.7.0