TypeError [ERR_IMPORT_ASSERTION_TYPE_MISSING]: Module "file:///path/to/data.json" needs an import assertion of type "json" - node.js

I'm trying to import JSON in nodejs.
// tsconfig.json
...
"lib": ["es2022"],
"target": "es2022",
"module": "nodenext",
"moduleResolution": "node",
...
"resolveJsonModule": true,
...
// .swcrc.json
...
"target": "es2022",
...
"module": {
"type": "nodenext",
...
When I then compile it and run "start": "NODE_ENV=production node --es-module-specifier-resolution=node --experimental-json-modules --no-warnings lib/index.js" I get TypeError [ERR_IMPORT_ASSERTION_TYPE_MISSING]: Module "file:///path/to/data.json" needs an import assertion of type "json".
I then add:
import data from './data.json' assert {type: 'json'}
console.log(data)
I then open up the compiled code and I can see:
import data from"./data.json";console.log(data);
//# sourceMappingURL=index.js.map
At this point I thought maybe it's SWC not compiling the assertation?
I then run tsc --emitDeclarationsOnly and I get Import assertions are not allowed on statements that transpile to commonjs 'require' calls. At this point I have no idea why on earth commonjs has anything to do with it, I'm not using commonjs anywhere am I?
Also I'm using node 18.
What am I doing wrong? I am simply trying to import that json.
Edit: Okay so the reason TS was breaking was because of missing "include": ["src/**/*.ts", "src/**/*.json", "types.d.ts"],. After adding that it now works. Unfortunately SWC is still giving the same error so I cannot run it.

Finally figured it out. There's an experimental option in .swcrc.json that allows you to tell it to keep the assertations.
// .swcrc.json
...
"jsc": {
"experimental": {
"keepImportAssertions": true
}
}

Related

Jest Typescript with ES Module in node_modules error - Must use import to load ES Module:

I'm trying to write a simple jest test for a 3rd party package were using that only exports an ES module. It's a wrapper around an http server.
Here is a test repo I setup (just run yarn && yarn jest to reproduce): https://github.com/jamesopti/hocuspocus-testing
No matter what config I experiment with, I still get this error when trying to run it:
Must use import to load ES Module: /Users/j/hocuspocus-testing/node_modules/#hocuspocus/server/dist/hocuspocus-server.esm.js
> 1 | import { Server, Hocuspocus } from '#hocuspocus/server'
| ^
2 | import * as request from 'supertest'
3 |
4 | describe('Server (e2e)', () => {
Things I've tried already:
The Jest instructions on ES modules: https://jestjs.io/docs/ecmascript-modules
In Jest configuration using transformIgnorePatterns
transformIgnorePatterns: ['node_modules/(?!#hocuspocus/)']
Using Babel via babel-jest
modifying transform setup in Jest configuration as '^.+\.jsx?$': 'babel-jest', '^.+\.tsx?$': 'ts-jest'
Ran into the error You appear to be using a native ECMAScript module configuration file, which is only supported when running Babel asynchronously.
Using .babel.config.js instead of .babelrc.js
Any ideas what I'm missing here? I thought this would be straightforward
[EDIT 1] - Added tsconfig.json and a working src/index.ts file to the example repo.
So for anyone still hitting this, ESM configuration explained in this section of documentation :
https://kulshekhar.github.io/ts-jest/docs/guides/esm-support
{
// [...]
"jest": {
"extensionsToTreatAsEsm": [".ts"],
"globals": {
"ts-jest": {
"useESM": true
}
}
}
}
JAN 2023: ES2022, TypeScript 4.9.4, jest 29.3.1, ts-jest 29.0.3
This is what worked for me, after 2 hours of frustration.
I used this configuration in jest.config.ts:
import type { JestConfigWithTsJest } from 'ts-jest'
const config: JestConfigWithTsJest = {
extensionsToTreatAsEsm: ['.ts'],
verbose: true,
preset: 'ts-jest/presets/default-esm',
testEnvironment: 'node',
transform: {
'^.+\\.(ts|tsx)?$': ['ts-jest', { useESM: true }]
},
testPathIgnorePatterns: ['./dist']
}
export default config
Change the test script in package.json to:
I use pnpm. Change to npx jest with npm,
or yarn exec with yarn
...
"scripts": {
...
"test": "NODE_OPTIONS=--experimental-vm-modules pnpm exec jest",
...
}
...
tsconfig.json:
{
"compilerOptions": {
"rootDirs": ["src"],
"outDir": "dist",
"lib": ["ES2022"],
"target": "ES2022",
"module": "ES2022",
"composite": true,
"moduleResolution": "node",
"declaration": true,
"declarationMap": true,
"incremental": true,
"esModuleInterop": true,
"types": ["jest", "node", "#types/jest"],
"sourceMap": true
},
"ts-node": {
"esm": true,
"experimentalSpecifierResolution": "node"
},
"include": ["./src/**/*", "./tests/**/*"]
}
See this (rather confusing) documentation for reference:
https://kulshekhar.github.io/ts-jest/docs/guides/esm-support/
You don't have a tsconfig.json file that specifies module. Therefore it uses the default, where it transpiles all your modules to CommonJS syntax, which uses require.
If you actually look at your dist/hocuspocus-server.esm.js, you should see it using require over the ESM import syntax.
I was having the same problem with my svelte app and testing. I ultimately traced it to having a jest.config.js and a jest.config.json in my root folder. It seems that jest does not have automatic config file resolution and was using a default configuration instead of either of my specified configurations.

How to pass separate TypeScript config for TSNode when it has being indecency running by Mocha & IntellIJ IDEA?

Although TS-Node started the ECMAScript modules support, it has a lot of limitations and below example:
import { assert as Assert } from "chai";
describe("firstTest", (): void => {
it("", (): void => {
Assert.isTrue(true);
});
});
does not work with next settings:
tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ES2020",
"moduleResolution": "node",
"strict": true,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"baseUrl": "./",
"paths": {},
"noUnusedParameters": true,
"noImplicitReturns": true
}
}
.mocharc.yaml
extension:
- ts
spec: "**/*.test.ts"
require:
- ts-node/register
- tsconfig-paths/register
Error:
import { assert as Assert } from "chai";
^^^^^^
SyntaxError: Cannot use import statement outside a module
I need the ECMAScript modules because the Webpack dynamic loading does not work with CommonJS modules. So the conceptual solution is create the additional tsconfig.json for TSNode or pass modules type via console. It's has been documented how to do it for TSNode, but here I don't execute TSNode directly - I launch the Mocha via IntelliJ IDEA:
IntelliJ IDEA and, I suppose, all IDEs of this family including WebStorm generates the below command (added the line breaks):
"C:\Program Files\nodejs\node.exe"
"D:\XXX\MyProject\node_modules\mocha\bin\mocha" --require ts-node/register --ui bdd --reporter C:\Users\XXX\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\203.7148.57\plugins\NodeJS\js\mocha-intellij\lib\mochaIntellijReporter.js
"D:\XXX\MyProject\Test\Test.test.ts" --grep "^firstTest "
Now how to set CommonJS modules type?
According to ts-node's documentation (search for IntelliJ), you can create an alternative tsconfig.json (containing the option "module": "CommonJS") and enforce it for Mocha tests using environment variable TS_NODE_PROJECT. In the configuration corresponding to your Mocha testing, in the Environment variables section, you set this variable to the pathname of this alternative tsconfig.json. Note, multiple tsconfig.json files can use one common "base" file and extend/partially override it (see).

Typescript declaration file for an external npm package - constructor

I am using ES6 and Typescript for my Node project, however one library is a commonjs library.
For that library, I created my own .d.ts declaration file:
module "#alpacahq/alpaca-trade-api" {
export interface AlpacaParams { ... }
// ...
export class Alpaca implements Broker {
// ...
constructor(params: AlpacaParams);
}
export default Alpaca;
}
Everything works as expected, but I'm having a problem with the constructor.
If I use that class from within my project, and I try this:
this.alpaca = new Alpaca.Alpaca({...});
I get told that Alpaca.Alpaca is not a constructor.
The only way it seems to work is if I do:
this.alpaca = new Alpaca.default({...});
I'm quite new to Typescript, so I'm sure I'm doing something wrong. Any ideas?
The latter works, so I'm not blocked in my work, but I would like to set things up properly.
Thank you!
Edited to show TS config and imports
tsconfig.json
{
"compilerOptions": {
"target": "es6",
"module": "es6",
"lib": ["es6", "es5"],
"sourceMap": true,
"outDir": "dist",
"rootDir": "src",
"strict": true,
"moduleResolution": "node",
"typeRoots": ["./types"],
"esModuleInterop": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
This is how I import it. Couldn't figure out how to make it work otherwise. If I import modules ES6 style, it breaks unless I use commonjs. If I use commonjs, I get an "export undefined" error.
import * as Alpaca from '#alpacahq/alpaca-trade-api';
The problem is that you're importing all named exports with import * meaning that Alpaca refers to the module and .default refers to the exported class. Instead you should be importing just the default member.
Change your import to look like this:
// Import default member from module
import Alpaca from '#alpacahq/alpaca-trade-api';
this.alpaca = new Alpaca({...});

ReferenceError: Node is not defined (trying to use Node interface in typescript function in nodejs application)

While extending Cheerio library, I implemented the following static function (other extension functions work fine):
$.nodeType = function (elem: CheerioElement): number {
switch (elem.type) {
case "comment":
return Node.COMMENT_NODE; // <--- it fails here
case "tag":
return Node.ELEMENT_NODE; // <--- it fails here
case "text":
return Node.TEXT_NODE; // <--- it fails here
default:
return -1;
}
};
The following error appears during runtime (compilation with tsc -b succeed):
ReferenceError: Node is not defined
Node interface is part of the DOM API. Thus, I realized the need of explicitly include the DOM API under compilerOptions section of tsconfig.json.
However, I still get that runtime error.
Minimal relevant part of tsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"incremental": true,
"lib": [
"esnext",
"dom"
],
"module": "commonjs",
"noImplicitAny": true,
"outDir": "./lib/",
"sourceMap": true,
"target": "esnext",
"watch": true
},
"include": [
"./src/**/*.ts",
]
}
I thought of explicitly import Node lib in the specific .ts file which contains the function, but I didn't find any "include-able" standard DOM lib.
Including a typescript lib doesn't polyfill the feature in an environment where it is not available.
While including "dom" type definitions will make the types available (at the compile time), but it doesn't actually make the Node API (typically provided by the browser runtime) available at runtime.
If you really need this functionality at runtime, you will need to also include an implementation of DOM for node.js such as jsdom which provides this API.
lorefnon explained the problem; here's a slightly hacky way to fix it:
Install jsdom
npm install jsdom
Add this to the top of your file:
const { JSDOM } = require('jsdom'); // or import { JSDOM } from 'jsdom';
const Node = new JSDOM('').window.Node;

importing class in Node js with typescript

I would import class in nodejs and use it in app.ts
var nano = require("nano");
import { EnvConfig } from './envConfig.service';
let config = new EnvConfig();
const dbCredentials: any = config.appEnv.getServiceCreds('dataservices');
export const nanodb = nano({
url: dbCredentials.url,
});
export const nanodbCockpitLight = nanodb.use('data');
console.log(dbCredentials);
When I try to compile I get this error.
import { EnvConfig } from './envConfig.service';
^
SyntaxError: Unexpected token {
I have created the tsconfig file :
{
"compilerOptions": {
"module": "commonjs",
"declaration": false,
"noImplicitAny": false,
"removeComments": true,
"noLib": false,
"allowSyntheticDefaultImports": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es6",
"sourceMap": true,
"allowJs": true,
"outDir": "./dist",
//"baseUrl": "src" // Attention !! nécessite l'utilisation d'un loader de module node pour fonctionner sur node
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.spec.ts"]
}
I get this warning
No inputs were found in config file 'c:/Users/EHHD05911.COMMUN/Documents/cockpitLight/DB mananger/tsconfig.json'. Specified 'include' paths were '["src//"]' and 'exclude' paths were '["node_modules","/.spec.ts"]'
You cannot run node app.ts file directly that won't work
You need transpiler like babel js or typescript compiler tsc so first transpile to js file and then run node app.js
You're using .js extension, you need .ts extension, e.g.: app.ts instead of app.js.
Make sure you have typescript either in npm global or in dev dependencies.
I suspect whatever you're importing has typescript syntax (strong typing and such), and so running node directly won't work. You need to run tsc first, which will transpile everything to javascript in a dist folder, and then run node dist/app.js.
This is a bit cumbersome though, which is why there is ts-node. It's exactly what it sounds like, a node REPL for typescript. You should be able to run ts-node src/app.ts.
import { something } is a typescript syntax, it won't work in a .js file. That is a separate language. Try using require instead.
Use babel js which is a toolchain that is mainly used to convert ECMAScript 2015+ code into a backwards compatible version of JavaScript in current and older browsers or environments.
package.json
"dependencies": {
"#babel/polyfill": "^7.0.0",
}
"babel": {
"presets": [
"#babel/preset-env"
]
},
"scripts": {
"start": "server.js --exec babel-node",
}
https://babeljs.io/docs
This will enable/resolve your import statements.

Resources