cannot run compiled typescript prog using module resolution - node.js

I'm trying to compule the following typscript file
import { magic } from 'lib/magic';
magic();
The filestructure is:
./src/
main.ts
lib/
a/magic.ts
b/magic.ts
Inside tsconfig.json I map lib/magic to the right file as follows
{
"compilerOptions": {
"target": "esnext",
"module": "commonjs",
"outDir": "./dist",
"baseUrl": ".",
"paths": {
"lib/*": [ "src/lib/a/*" ]
}
},
"include": [ "./src/**/*.ts" ],
"exclude": [ "./node_modules" ]
}
It is that paths section that maps that import lib/magic to ./src/lib/a/magic.ts.
So, I can compile as follows
$> tsc -p ./tsconfig-a.json
It produces output in dist. However, when I try to run it
$> node ./dist/main.js
internal/modules/cjs/loader.js:626
throw err;
^
Error: Cannot find module 'lib/magic'
Require stack:
...
It makes sense, because in dist there is no such thing as lib/magic. Any suggestions how to fix this?

Until someone proves me wrong, here are my findings so far.
Here is an issue in which it is stated that all this is in fact expected behaviour.
So you basically have to fix it in a second step. In my case I want to make 2 build, one for a and one for b. The easiest way to fix it then is to have it working by default for a as follows
import { magic } from './lib/a/magic';
magic();
Then build b and replace that path as follows
sed -e 's/lib\/a/lib\/b/' ./dist/main.js > ./dist/main-node.js
In my case, one version was for NodeJs and the other for the browser, so I also needed to add the .js extension. Just in case anyone is interested, here is the command
$> echo "import { magic } from './lib/a/magic';" | sed "s#^\(import[^']*\)'\([^']*\)#\1 '\2.js#g"
Please let me know if you have a better solution, because all this feels a bit hacky

Related

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

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
}
}

Error [ERR_REQUIRE_ESM] - Husky, lint-staged, eslint - nodeJS:

although there were other questions about this, most were left without a response or the response given did not work for me.
For what it gives apparently eslint is looking within node_modules, here is the given error:
Error [ERR_REQUIRE_ESM]: require() of ES Module /home/kamoraes/Workspace/node_adc/node_modules/supports-color/index.js from /home/kamoraes/Workspace/node_adc/.git/hooks/commit-msg not supported.
Instead change the require of index.js in /home/kamoraes/Workspace/node_adc/.git/hooks/commit-msg to a dynamic import() which is available in all CommonJS modules.
at Object.<anonymous> (/home/kamoraes/Workspace/node_adc/.git/hooks/commit-msg:8:23) {
code: 'ERR_REQUIRE_ESM'
}
Node v16.13.0
The problem is, the project is in it's first steps, quickly redoing the project in another machine on the same node and yarn, version, don't give the same error. Also asked a friend of mine to try it. no error given.
also, this project is an course, same steps made, here is my entire code as for now:
https://github.com/kaiqueAMoraes/clean-node-api
the last commit for this given error is chore: eslintignore 6250e5bdea05cc2eb413c8a57a97e4bbe4bd5bb9
I've added husky, lint-staged
yarn add -D husky lint-staged
then added their respectively config files
.huskyrc.json:
{
"hooks": {
"pre-commit": "lint-staged"
}
}
.lintstagedrd.json:
{
"*.ts": [
"eslint 'src/**' --fix",
"git add"
]
}
for reference:
tsconfig:
{
"compilerOptions": {
"outDir" : "./dist",
"module": "commonjs",
"target": "es2019",
"strictNullChecks": true,
"esModuleInterop": true,
"allowJs": true
}
}
eslintrc:
{
"extends": "standard-with-typescript",
"parserOptions": {
"project": "./tsconfig.json"
}
}
both gitignore and eslintignore ignores node_modules and dist
Ran into the same issue.
You could bypass the check with git commit -m "your message here" --no-verify.

How should I configure Typescript to compile code to run in Node.JS?

First, let me be clear that this is not related to the browser. There are a hundred different questions & answers about this that all start off with "you can't use CommonJS modules in the browser...", but that's not what I'm trying to do.
I have some Typescript code which I'm trying to compile to Javascript for execution via node file-scanner.js on a server. It seems like the configuration should be pretty straightforward, but I always wind up with an error: ReferenceError: exports is not defined.
I am using import/export exclusively in my code. The main entry point looks like this:
import { loadEnv } from './modules/environment';
import { getDatabase } from './modules/models/helpers/database';
import { indexFiles } from './modules/services/indexFiles';
loadEnv();
getDatabase()
.then((db) => {
return indexFiles(db);
})
.catch((err: Error) => {
console.error('[ERROR] Unable to index files');
console.error(`[ERROR] Message = ${err.message}`)
console.error('[ERROR] Error object:', err);
console.error('[ERROR] Stack Trace', err.stack || '--no trace available--');
});
And my tsconfig.json (copied from #tsconfig/bases node 10 configuration) looks like this:
{
"compilerOptions": {
"lib": ["es2018"],
"module": "CommonJS",
"target": "es2018",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": [
"src/file-scanner.ts",
"src/modules/**/*/ts"
],
"exclude": [
"src/**/*.spec.ts"
]
}
I'm building it with:
./node_modules/.bin/tsc -p tsconfig.file-scanner.json --outDir lib
And running it with:
node lib/file-scanner.js
From what I understand, the Typescript compiler should be converting all my import/export statements to CommonJS module.exports & require statements. And I would hope that it includes all the appropriate code. But for some reason, when it comes time to execute the code, exports is not defined.
There's probably something obvious here that's staring me in the face, but I can't seem to figure it out. How do I get Typescript to compile for execution via Node.JS?

Running tests located in a separate directory with mocha and ts-node?

I have source code and tests separated as follows:
`src/main/ts/hello.ts` //SOURCE FILES HERE
`src/test/ts/hello.spec.ts` //SPEC FILES HERE
The import statement in src/test/ts/hello.spec.ts looks like this:
import hello from 'hello';
The hello.ts source code looks like this:
export function hello() {
return 'Hello World!';
}
export default hello;
My tsconfig.json is setup such that the test files can import source modules without using relative paths like this:
{
"include": [
"src/main/ts/**/*.ts"
],
"exclude": [
"node_modules"
],
"compilerOptions": {
"experimentalDecorators": true,
"noImplicitAny": true,
"moduleResolution": "node",
"target": "es6",
"baseUrl": ".",
"paths": {
"*": [
"*", "src/main/ts/*"
]
}
}
}
This way the hello.spec.ts file can import hello using the statement import hello from 'hello';
I'm trying to run the tests with npm test configured to run mocha and tsnode like this (Based on this article):
"scripts": {
"test": "mocha -r ts-node/register src/test/ts"
},
However it does not look like ts-node is picking up on my tsconfig.json configuration as I get this error:
mocha -r ts-node/register src/test/ts
Error: Cannot find module 'hello'
at Function.Module._resolveFilename (module.js:336:15)
at Function.Module._load (module.js:286:25)
The module resolution that you set through paths in tsconfig.json is purely an compile-time thing. (See this ts-node issue report and this TypeScript issue report for details.) It does not affect how the code is emitted, which means that your test file is doing a require("hello"), which Node cannot resolve. The consequence of paths being a compile-time thing is that your module loader needs to be configured to also perform the same kind of resolution that you specify in tsconfig.json. If you were using RequireJS, for instance, you'd need to have a configuration for it that does the same thing paths in tsconfig.json does. You are using Node, however...
What you can do in Node is use tsconfig-paths, which will read the tsconfig.json, parse the paths setting and change the module resolution in Node so that it works.
Using your code, I modified hello.spec.ts to have at least one test for feedback:
import hello from "hello";
import "mocha";
it("q", () => {
if (hello() !== "Hello World!") {
throw new Error("unequal");
}
});
I installed tsconfig-paths and #types/mocha (so that import "mocha" does the right thing compilation-wise in the test file I show above) and invoked Mocha like this:
$ ./node_modules/.bin/mocha --compilers ts:ts-node/register -r tsconfig-paths/register 'src/test/ts/**/*.ts'
I got this output:
✓ q
1 passing (20ms)

TypeScript AMD compilation and "barrel" modules

I'm trying to set up a Node.js + TypeScript project using Intern for testing. Everything works fine when I compile the project using "commonjs" (which I do for the normal build); and TypeScript is equally happy when compiling for "amd", which is required by Intern. However, when passing the tests with intern-client, it complains about a couple of things:
First, imports from "index.ts" files (so-called "barrel" modules) won't work. My setup is something like this (everything in the same directory):
// index.ts
export { x } from './x'
// x.ts
export function x() {}
// x.test.ts
import { x } from '.' // "Error: Failed to load module ..."
In fact, the generated JavaScript code (for x.test.ts) looks something like this:
define(["require", "exports", "."], function (...) { ... })
And I'm not sure that AMD knows how to handle the ".".
The second issue happens under the same circumstances (TypeScript compiles happily, but intern-client complains). In summary, I get an error when doing:
import jsdom = require('jsdom')
Which I need to transform to:
const jsdom = require('jsdom')
For Intern to be able to deal with it.
Here is the tsconfig.json file I use to compile the tests:
{
"compilerOptions": {
"target": "es6",
"module": "amd",
"moduleResolution": "node",
"sourceMap": true,
"rootDir": "src",
"outDir": "build/tests",
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true
}
}
And here is my intern.js configuration file, in case it helps:
define({
suites: ['build/tests/**/*.test.js'],
excludeInstrumentation: true,
filterErrorStack: true
})
Edit (2017-05-03)
To help understand the issue, here is an excerpt of the directory tree of the project:
build
tests // The compiled tests will end up here
src
core
utils
x.ts
x.test.ts
// Other files, each containing a function that I would like to unit-test...
intern.js
package.json
tsconfig.json
...
Regarding the first issue, AMD's handling of an import like '.' is different than Node's. While both of them will map '.' to a package, Node uses a default module name of index.js, while AMD uses main.js. To get things working in an AMD loader, you'll need to first define a package for '.', and then tell the AMD loader what default module to use for that package. Given your project layout, you could configure Intern like this:
loaderOptions: {
map: {
// When a module in src/ references 'src/utils', redirect
// it to 'utils'
'src': {
'src/utils': 'utils'
}
},
packages: [
// Define a package 'utils' with files in 'src/utils' that defaults
// to the module index.js
{ name: 'utils', location: 'src/utils', main: 'index.js' }
]
}
Regarding the second issue, its not clear what the problem actually is. Import statements will be transpiled into define dependencies by TypeScript, so Intern should never be seeing them.

Resources