I use Angular + TypeScript + Electron and I try to add tests to my application. I installed mocha and added the following line to package.json:
"test": "mocha './**/*.spec.ts'",
I have a app.component.spec.ts file in my source directory but executing npm run test fails with the following exception:
> mocha './**/*.spec.ts'
src\app\app.component.spec.ts:1
import { TestBed, waitForAsync } from '#angular/core/testing';
^^^^^^
SyntaxError: Cannot use import statement outside a module
at wrapSafe (internal/modules/cjs/loader.js:931:16)
at Module._compile (internal/modules/cjs/loader.js:979:27)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1035:10)
at Module.load (internal/modules/cjs/loader.js:879:32)
Can anyone explain me the context, and how I could fix this issue?
P.S. In similar posts the module in tsconfig.json needed to be set to commonjs which I did, but that didn't solve the issue:
{
"compileOnSave": false,
"compilerOptions": {
"module": "commonjs",
...
}
Related
EDIT
Changing module to Node16 inside compilerOptions fixed it.
more info https://github.com/microsoft/TypeScript/issues/50647
I have a pretty simple typescript+esm project with a CJS dependency, so my code looks like this:
index.ts
import myFunc from "./myfile.cjs"
(...)
I also have the compilerOptions (inside tsconfig.json) allowSyntheticDefaultImportsand esModuleInterop set
But when I try to run it using ts-node (node --loader ts-node/esm --experimental-specifier-resolution=node src/index.ts) it fails with a syntax error saying that my cjs file, myfile.cjs has an unexpected token export (export {};)
It is clear to me that something (ts-node probably) is trying to convert this into an ESM module, but it just creates a syntax error and breaks the code. Obviously, I'm not adding it there manually.
Command used: node --loader ts-node/esm --experimental-specifier-resolution=node src/index.ts
Complete error log:
export {};
^^^^^^
SyntaxError: Unexpected token 'export'
at Object.compileFunction (node:vm:352:18)
at wrapSafe (node:internal/modules/cjs/loader:1027:15)
at Module._compile (node:internal/modules/cjs/loader:1063:27)
at Module.m._compile (/Users/cjg/Git/ast/node_modules/ts-node/src/index.ts:1618:23)
at Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
at Object.require.extensions.<computed> [as .js] (/Users/cjg/Git/ast/node_modules/ts-node/src/index.ts:1621:12)
at Module.load (node:internal/modules/cjs/loader:975:32)
at Function.Module._load (node:internal/modules/cjs/loader:822:12)
at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:170:29)
at ModuleJob.run (node:internal/modules/esm/module_job:198:25)
tsconfig.json
{
"compilerOptions": {
"moduleResolution": "node",
"baseUrl": "./src",
"module": "es2020",
"outDir": "./dist",
"allowJs": true,
"target": "es2020",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
},
"include": [
"./src/**/*"
]
}
ts-node version: 10.9.1
node version: 17.9.0
tsc version: 4.7.4
Does anyone have a clue?
Thanks in advance!
There are several threads on this when generating code for a ".ts" file, even for a commonjs target. From what I can tell, typescript developers think the generated export {}; is a feature in those files and provide the rationale (which many disagree with).
From your description, and I tested the same, they're doing the same in very specifically (.cjs) files.
The solution for now is to remove the line in your build. Maybe using gulp-replace?
On electron, the node module vosk needs to access some shared objects located in node_modules/vosk/lib/.
The issue I am having right now is that, when I do require('vosk') in my main.js and try to execute my AppImage file, I get:
A JavaScript error occurred in the main process
Uncaught Exception:
Error: Dynamic Linking Error: /tmp/.mount_CantooClaxGf/resources/app.asar/node_modules/vosk/lib/linux-x86_64/libvosk.so: Cannot open the shared object: It's not a folder
at new DynamicLibrary (/tmp/.mount_CantooClaxGf/resources/app.asar/node_modules/ffi-napi/lib/dynamic_library.js:75:11)
at Object.Library (/tmp/.mount_CantooClaxGf/resources/app.asar/node_modules/ffi-napi/lib/library.js:47:10)
at Object.<anonymous> (/tmp/.mount_CantooClaxGf/resources/app.asar/node_modules/vosk/index.js:24:21)
at Module._compile (internal/modules/cjs/loader.js:1145:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1166:10)
at Module.load (internal/modules/cjs/loader.js:981:32)
at Module._load (internal/modules/cjs/loader.js:881:14)
at Function.Module._load (electron/js2c/asar.js:769:28)
at Module.require (internal/modules/cjs/loader.js:1023:19)
at require (internal/modules/cjs/helpers.js:77:18)
I tried to add vosk to the files in the build:
"build": {
"files": [
"dist/**/*",
"src/assets/icons/*",
"electron.js",
"package.json",
"assets/models/*",
"node_modules/vosk/lib/*"
],
I can now see the files in the app.asar.unpacked/node_modules/vosk/lib/ folder, but when executing the app, I'm still having the same error.
I found this answer mentioning a hack, but it didn't solve my issue and I still have the exact same error.
How am I supposed to package the shared objects in a way that vosk will find them?
I could solve the issue with this config for electron, putting all the dependencies of vosk in the extraResources field:
"build": {
"extraResources": [
"node_modules/at-least-node/**/*",
"node_modules/builder-util-runtime/**/*",
"node_modules/debug/**/*",
"node_modules/ffi-napi/**/*",
"node_modules/fs-extra/**/*",
"node_modules/get-symbol-from-current-process-h/**/*",
"node_modules/get-uv-event-loop-napi-h/**/*",
"node_modules/graceful-fs/**/*",
"node_modules/jsonfile/**/*",
"node_modules/ms/**/*",
"node_modules/node-addon-api/**/*",
"node_modules/node-gyp-build/**/*",
"node_modules/ref-napi/**/*",
"node_modules/ref-struct-di/**/*",
"node_modules/sax/**/*",
"node_modules/universalify/**/*",
"assets/models/**/*"
],
"files": [
"dist/**/*",
"src/assets/icons/*",
"electron.js",
"package.json"
],
I also needed this lib.
It's now working as expected
I solved by updating my electron-builder configuration in package.json to be
{
"build": {
"asar": true,
"asarUnpack": [
"node_modules"
],
}
}
Then ensuring the unpacked path was used instead:
var dirPath = __dirname.includes('.asar') ? __dirname.replace('.asar', '.asar.unpacked') : __dirname;
node_modules/
my_module/
lib/
index.ts
src/
index.ts
I made a custom module my_module and wanted to use it as an external library. The codes are like below:
[src/index.ts]
import { hello } from 'my_module'
console.log(hello)
[node_modules/my_module/index.ts]
export const hello = "Hello"
My tsconfig.json says "module": "commonjs", "target": "es6", so I thought import keywords would work nicely in Typescript files.
As I expected, the import keyword in src/index.ts works well, but the export keyword in node_modules/my_module/index.ts is an unexpected token. How can I get this problem solved?
export const hello = "hello"
^^^^^^
SyntaxError: Unexpected token export
at new Script (vm.js:83:7)
at createScript (vm.js:267:10)
at Object.runInThisContext (vm.js:319:10)
at Module._compile (internal/modules/cjs/loader.js:685:28)
at Module._extensions..js (internal/modules/cjs/loader.js:733:10)
at Object.require.extensions.(anonymous function) [as .ts] (/usr/local/lib/node_modules/ts-node/src/index.ts:431:14)
at Module.load (internal/modules/cjs/loader.js:620:32)
at tryModuleLoad (internal/modules/cjs/loader.js:560:12)
at Function.Module._load (internal/modules/cjs/loader.js:552:3)
at Module.require (internal/modules/cjs/loader.js:658:17)
Unless the project was configured to transpile modules from node_modules (this is generally discouraged because this is inefficient and unneeded), it's expected that external module will be evaluated as is, and import keyword is not allowed in CommonJS modules.
my_module should be compiled before publishing, i.e. tsc should be executed, and dist should contain transpiled *.js and *.d.ts typings.
main in
my_module package.json should specify entry point, "main": "dist/index.js".
The package can optionally contain .npmrc file to exclude src from published files, but this is not necessary.
I am trying to port a react app to electron using electron-react-boilerplate but I'm using semantic-ui which suggested being set up with gulp. electron-react-boilerplate uses webpack to handle all of its packaging and I can't get webpack +gulp to work so everything will package in the electron app.
I'm trying this link that explains how to pipe webpack configs through gulp tasks but I'm getting an "unexpected token import" error from the webpack config.
.babelrc
{
"presets": ["es2015", "stage-0", "react"],
"plugins": ["add-module-exports"],
"env": {
"production": {
"presets": ["react-optimize"],
"plugins": ["babel-plugin-dev-expression"]
},
"development": {
"plugins": ["tcomb"],
"presets": ["react-hmre"]
},
"test": {
"plugins": [
["webpack-loaders", { "config": "webpack.config.test.js", "verbose": false }]
]
}
}
}
simple gulpfile.js straight from the link above:
var gulp = require('gulp');
var webpack = require('webpack-stream');
gulp.task('default', function() {
return gulp.src('src/entry.js')
.pipe(webpack())
.pipe(gulp.dest('dist/'));
});
the webpack dev file is here,
note some of the names are changed between this boilerplate version and my project but otherwise it's the same.
the error:
Configurator>gulp
[09:52:40] Using gulpfile C:\git\Configurator\gulpfile.js
[09:52:40] Starting 'default'...
[09:52:40] 'default' errored after 8.28 ms
[09:52:40] C:\git\Configurator\webpack.config.development.js:7
import webpack from 'webpack';
^^^^^^
SyntaxError: Unexpected token import
at Object.exports.runInThisContext (vm.js:53:16)
at Module._compile (module.js:513:28)
at Object.Module._extensions..js (module.js:550:10)
at Module.load (module.js:458:32)
at tryModuleLoad (module.js:417:12)
at Function.Module._load (module.js:409:3)
at Module.require (module.js:468:17)
at require (internal/module.js:20:19)
at Gulp.<anonymous> (C:\git\Configurator\gulpfile.js:7:18)
at module.exports (C:\git\Configurator\node_modules\orchestrator\lib\runTask.js:34:7)
the internet tells me that getting rid of the import errors should be as simple as using the 'es2015' preset in .babelrc but it's there and it's not helping.
I can get the dev server to work in the electron app with the semantic-ui stuff after the initial gulp build for semantic, but for some reason it doesn't build into the electron package when I try to package an installer to deploy this thing.
When I run the electron app dev server through webpack it works fine though, except I get two errors:
cannot set property exports of undefined
and
locals[0] does not appear to be a module object with hot module replacement API enabled
the latter stacktrace goes back to some semantic imports in one of my react files.
I'm totally at a loss as to how to make all this stuff work together.
Is it possible to override which tsconfig.json ts-node uses when called from mocha?
My main tsconfig.json contains "module": "es2015", but I want to use "module": "commonjs" for ts-node only.
I tried this
mocha --compilers ts:ts-node/register,tsx:ts-node/register \
--compilerOptions '{"module":"commonjs"}' \
--require ts-node/register test/**/*.spec.ts*
but it did not work:
SyntaxError: Unexpected token import
at exports.runInThisContext (vm.js:53:16)
at Module._compile (module.js:387:25)
at Module.m._compile (/usr/lib/node_modules/ts-node/src/index.ts:406:23)
at Module._extensions..js (module.js:422:10)
at Object.require.extensions.(anonymous function) [as .tsx] (/usr/lib/node_modules/ts-node/src/index.ts:409:12)
at Module.load (module.js:357:32)
at Function.Module._load (module.js:314:12)
at Module.require (module.js:367:17)
at require (internal/module.js:16:19)
at /usr/lib/node_modules/mocha/lib/mocha.js:222:27
at Array.forEach (native)
at Mocha.loadFiles (/usr/lib/node_modules/mocha/lib/mocha.js:219:14)
at Mocha.run (/usr/lib/node_modules/mocha/lib/mocha.js:487:10)
at Object.<anonymous> (/usr/lib/node_modules/mocha/bin/_mocha:458:18)
at Module._compile (module.js:413:34)
at Object.Module._extensions..js (module.js:422:10)
at Module.load (module.js:357:32)
at Function.Module._load (module.js:314:12)
at Function.Module.runMain (module.js:447:10)
at startup (node.js:146:18)
at node.js:404:3
You need to set the configuration through the TS_NODE_COMPILER_OPTIONS environment variable
Example code on an unix machine:
TS_NODE_COMPILER_OPTIONS='{"module":"commonjs"}' \
mocha --require ts-node/register 'test/**/*.spec.{ts,tsx}'
Explanation extracted from the repository documentation
CLI and Programmatic Options
Environment variable denoted in parentheses.
-T, --transpile-only Use TypeScript's faster transpileModule (TS_NODE_TRANSPILE_ONLY, default: false)
-I, --ignore [pattern] Override the path patterns to skip compilation (TS_NODE_IGNORE, default: /node_modules/)
-P, --project [path] Path to TypeScript JSON project file (TS_NODE_PROJECT)
-C, --compiler [name] Specify a custom TypeScript compiler (TS_NODE_COMPILER, default: typescript)
-D, --ignore-diagnostics [code] Ignore TypeScript warnings by diagnostic code (TS_NODE_IGNORE_DIAGNOSTICS)
-O, --compiler-options [opts] JSON object to merge with compiler options (TS_NODE_COMPILER_OPTIONS)
--files Load files from tsconfig.json on startup (TS_NODE_FILES, default: false)
--pretty Use pretty diagnostic formatter (TS_NODE_PRETTY, default: false)
--skip-project Skip project config resolution and loading (TS_NODE_SKIP_PROJECT, default: false)
--skip-ignore Skip ignore checks (TS_NODE_SKIP_IGNORE, default: false)
--log-error Logs errors of types instead of exit the process (TS_NODE_LOG_ERROR, default: false)
--prefer-ts-exts Re-order file extensions so that TypeScript imports are preferred (TS_NODE_PREFER_TS_EXTS, default: false)
TypeScript allows you to override a configuration file. Rather than hard-code JSON in an environment variable as mentioned in the other solutions, specify the overridden configuration path in the environment. The TS_NODE_PROJECT environment variable can be used for this.
TS_NODE_PROJECT='./tsconfig.commonjs.json'
So if your main config is:
tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
}
}
You can create another configuration that overrides the module setting.
tsconfig.commonjs.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs"
}
}
When you run mocha, specify the overridden configuration to use:
"test": "TS_NODE_PROJECT='./tsconfig.commonjs.json' mocha -r ts-node/register test/**/*.spec.ts*"
This makes it very easy to further customize your tsconfig just for mocha testing. You can even run ts-node (outside of mocha) directly specifying that path:
ts-node -P tsconfig.commonjs.json -r myFile.ts
--compilerOptions wont' work.
What you need to do is customize how you register ts-node. My case was a little bit different from yours, I wanted it to use test/tsconfig.json, which contained settings needed by my test code. If I just used --require ts-node/register, it was using a default configuration that did not contain the settings needed to run my tests.
What I did was:
Create a file test/tshook.js. It contains:
require("ts-node").register({
project: "test/tsconfig.json",
});
I edited my test/mocha.opts to have:
--require test/tshook.js
test/**/*.ts
This should will pass the desired setting to ts-node:
require("ts-node").register({
compilerOptions: {
module: "commonjs",
},
});
In package.json - scripts section:
"test": "TS_NODE_PROJECT=src mocha"
picks up my tsconfig.json in the src directory of my project, overriding the default tsconfig.json.
OP can achieve same by using test instead of src
This worked for me on windows
set TS_NODE_COMPILER_OPTIONS={\"module\":\"commonjs\"} && mocha -r ts-node/register test/unit/*.test.ts
This was the error that prompted me to use that solution
(function (exports, require, module, __filename, __dirname) { import 'mocha';
You can also use ts-mocha (https://www.npmjs.com/package/ts-mocha)
Example
package.json
"test": "ts-mocha -p test/tsconfig.cjs.json test/**/*.test.ts"
test/tsconfig.cjs.json
{
"extends": "../tsconfig.json",
"compilerOptions": {
"module": "commonjs"
}
}
While there doesn't seem to be a unified linux/windows command line phrasing that works, you can set the compiler-options from the command line. In my case, much like the OP, I have a tsconfig.json with a module: esnext. I was able to override on the command line:
Ran on Windows, w/ts-node installed globally, but in different shell types:
bash/mingw64:
ts-node --compiler-options={"module":"commonJS"} xxx.ts
cmd:
ts-node --compiler-options={\"module\":\"commonJS\"} xxx.ts
On mac
"test": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' mocha --require ts-node/register test/**/*.ts",
This doesn't address the core original question, but goes to the heart of getting mocha with typescript working, so I'll post it here in case it helps others.
We found that some ridiculous backslash escaping was needed to make this work on Windows. It works fine on Mac too, even though Mac doesn't need the same escaping.
Then, additional ts-node require directives were critical.
File package.json:
{
"scripts": {
...
"test": "cross-env TS_NODE_COMPILER_OPTIONS={\\\"module\\\":\\\"commonjs\\\"} mocha"
},
...
"mocha": {
"require": [
"ts-node/register",
"tsconfig-paths/register"
],
"watch-files": [
"./src/**/*.ts",
"./tests/**/*.spec.ts"
],
"spec": "./tests/**/*.spec.ts"
}