How configure a local package for use in a NestJS app? - nestjs

I cant import classes from a local package, please see summary below.
NestJS app
I created a NestJS app called exp1 with the cli:
nest new exp1
Then, add my local package
yarn add "file:../core"
Next, I want to import classes from the package. In app.controller I put
import { Feature } from '#me/core'
...
console.log(Feature)
So far so good, no warnings. But the console.log() output is undefined.
Package configuration
package.json
{
"name": "#me/core",
"main": "dist/index.js",
"types": "src/index.d.ts",
"files": [
"/dist",
"/src"
],
...
}
tsconfig.json
{
"compilerOptions": {
"module": "ES6",
"esModuleInterop": true,
"moduleResolution": "node",
"declaration": true,
"removeComments": true,
"preserveConstEnums": true,
"sourceMap": true,
"lib": ["es2019",],
"target":"ES6"
},
"include": [
"src/**/*"
]
}
index.ts
export { Feature } from './Feature'
Feature.ts
export class Feature {
public type = 'unknown'
}
webpack.mix.js
const mix = require('laravel-mix');
mix.ts('src/index.ts', 'dist')
Running this successfully outputs dist/index.js, src/index.d.ts, src/Feature.d.ts
Clues
The typing seems to work in NestJS app
(new Feature).type // OK
(new Feature).typo // Warning
However when accessing Feature it is always undefined..
Not sure what I am missing. Is it correct to point package.json main attribute to dist/index.js file or should it point to index.ts?

Related

Run mocha with Typescript throws: TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts"

I'm trying to write some tests with Typescript and Mocha.
Following its documentation I ended up with the following setup:
package.json
{
//...
"scripts": {
"test": "mocha",
},
//...
}
.mocharc.json
{
"extension": ["test.ts"],
"spec": "tests/*.test.ts",
"require": "ts-node/register",
"recursive": true
}
tsconfig.json
{
"compilerOptions": {
"outDir": "dist",
"module": "commonjs",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"sourceMap": false,
"strict": true,
"esModuleInterop": true,
"isolatedModules": true,
},
"files": [
"src/main/main.ts",
],
}
Running npm test throws the following error: TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for [...]/tests/task.test.ts.
That is my only test, and it works until I import a separate .ts file (../src/core/task), for completeness:
task.test.ts
import { assert } from 'chai';
import { Task } from '../src/core/task';
describe('Task', () => {
it('Task Run', () => {
const task = new Task({
title: "My Title",
command: "echo hello",
path: "."
});
task.run();
})
});
I have tried several permutations of my config according to some other answers as well as ts-mocha without success.
I have same problem before it work in ts-node#9 but not in ts-node#10 and then i tried this configuration in .mocharc.json to make it work as expected
{
"extensions": ["ts"],
"spec": ["**/*.spec.*"],
"node-option": [
"experimental-specifier-resolution=node",
"loader=ts-node/esm"
]
}
In my case the problem was caused by an import in a JS file imported by TypeScript test files. Transforming it to a require fixed the problem.
Generally speaking, when the exception occurs in:
Loader.defaultGetFormat [as _getFormat] internal/modules/esm/get_format.js
there can be only one explanation: Node.js is using its ESM loader to load what should be code transpiled by ts-node.
So the fix should always be - find the reason Node.js is switching to ESM and eliminate it.

How do I import from a relative path like "/utils" within my "src" folder in Jest + RTL Tests

I am creating tests with Jest and React Testing Library and I am having difficulties with imports to the util folder that exists within the src folder. I am using TypeScript for this which seems to create its own interesting challenges.
My jest.config.ts file looks like the following:
module.exports = {
'preset': 'ts-jest',
'testEnvironment': 'node',
'testPathIgnorePatterns': [
'<rootDir>/dist'
],
'moduleNameMapper': { 'src/(.*)': '<rootDir>/src/$1' }
};
The test looks like the following:
import React from 'react';
import { render, screen } from '#testing-library/react';
import { Tag } from '/components';
describe('Tabs Component', () => {
it('should render a tab component correctly', () => {
render(<Tag id="test" />);
expect(screen.getAllByTestId('test')).toHaveBeenCalled();
});
});
When I run this I am getting import issues:
Cannot find module '/components' from 'src/components/Tag/Tag.spec.tsx'
Additionally my tsconfig looks like the following:
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"/*": [
"*"
]
},
"outDir": "./tested/esm",
"module": "esnext",
"target": "es5",
"lib": [
"es6",
"dom",
"es2016",
"es2017"
],
"jsx": "react",
"declaration": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"esModuleInterop": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"allowSyntheticDefaultImports": true
},
"include": [
"src/**/*.spec.{ts,tsx}",
"./jest.config.ts"
],
"exclude": [
"node_modules",
"dist",
"./src/**/*.stories.tsx",
"./*.js"
]
}
I have tried a few of the tricks from Stack Overflow like:
Jest gives `Cannot find module` when importing components with absolute paths
None of these seemed to fix the issue.
I think the problem here is how the import is done. As the jest.config.ts has already specified the moduleNameMapper as { 'src/(.*)': '<rootDir>/src/$1' }, it means if Tag component is imported as below,
import { Tag } from "src/components";
Jest will look into the <rootDir>/src/components/index.ts to import the Tag component.
If you want to import modules in this way,
import { Tag } from "components"; // not "/components"
It's better to replace moduleNameMapper config with moduleDirectories:
module.exports = {
'preset': 'ts-jest',
'testEnvironment': 'node',
'testPathIgnorePatterns': [
'<rootDir>/dist'
],
'moduleDirectories': ['node_modules', 'src']
};
This tells Jest when it sees an import from "components", it will look into both node_modules and src and see if there is the file components/index.ts or components.ts.
PS: tsconfig.json is only used by the editor. Therefore, Jest won't take it into consideration. jest.config.ts is the config file that matters to Jest.

tsconfig's path parameter and ESLint

I've been setting up the paths option in my tsconfig.json file. So far everything works fine. I can run my tests, and I can execute the program as usual. My only problem is, that ESLint does not find the modules that are accessed with one of the paths defined in tsconfig.json.
Here are all of the files that are related to the problem:
tsconfig.json:
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"allowJs": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./",
"strict": true,
"esModuleInterop": true,
"declaration": true,
"resolveJsonModule": true,
"baseUrl": ".",
"paths": {
"#/*": ["./src/*"]
}
},
"include": ["src/**/*", "test/**/*", "index.ts"]
}
tsconfig.eslint.json:
{
"extends": "./tsconfig.json",
"include": ["src/**/*", "test/**/*", "index.ts", ".eslintrc.js"]
}
.eslintrc.js:
{
root: true,
env: {
browser: true,
es6: true,
node: true
},
extends: ['plugin:#typescript-eslint/recommended', 'plugin:#typescript-eslint/recommended-requiring-type-checking'],
parser: '#typescript-eslint/parser',
parserOptions: {
project: ['tsconfig.eslint.json'],
sourceType: 'module'
},
settings: {
'import/resolver': {
typescript: {}
}
},
plugins: ['#typescript-eslint', 'import', 'prefer-arrow'],
rules: {...}
}
Am am using the package eslint-import-resolver-typescript.
Now, if I try to import the file './src/test.ts' into './index.ts' with the path '#/test', then ESLint will not be able to resolve that import (although TypeScript resolves it just fine).
I've mostly copied my current solution from here, because I thought the person who asked that problem had the same problem that I had, but my setup still does not work.
I am in an NodeJS environment by the way.
EDIT:
I've also tried using the package eslint-import-resolver-alias. This only helped partially. I could get rid of the 'import/no-unresolved' errors, but whenever I call an imported function, I get '#typescript-eslint/no-unsafe-call' because apparently, ESLint does not find the types for the imported files and thusly gives everything the type any.
Could you try adding the tsconfigRootDir to your .eslintrc.js? It has worked for me.
parserOptions: {
tsconfigRootDir: __dirname,
}
For those using Angular and.eslintrc.json, I use this (notice the 2 stars in "Project"
"parserOptions": {
"project": ["**/tsconfig.json"],
"createDefaultProgram": true
},
you need to add eslint plugin https://github.com/benmosher/eslint-plugin-import#typescript
there under the extends you can specify option for typescript
extends:
- plugin:import/typescript
it should do the trick

Can i exclude files dynamically from ts compiler?

I'm setting up a node/typescript server to build a real time application. I have my server and my client on the same folder.
What i want to do is to exclude "src/client" from the typescript compiler when i run the "server:dev" script end exclude "src/server" when i run "client:dev".
I already tried to find a way to exclude files from command line, but i didn't find solutions for that.
that's how my tsconfig.json look like
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"lib": ["dom","es2017"],
"sourceMap": true,
"outDir": "dist",
"strict": true,
"noImplicitAny": true,
"moduleResolution": "node",
"esModuleInterop": true,
"resolveJsonModule": true
},
"exclude": [
"src/client"
]
}
but i need to include "src/client" and exclude "src/server" when i run the client.
tsconfig.json support extends field, in your case, you should put the common config in a base tsconfig.json, then create tsconfig with extends for client and server respectively.
// tsconfig.json
{
"compilerOptions": {
...
}
}
// tsconfig.client.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
...
},
"exclude": ["src/server"]
}
// tsconfig.server.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
...
},
"exclude": ["src/client"]
}
For npm scripts:
// package.json
{
"scripts": {
"server:dev": "tsc --build tsconfig.server.json",
"client:dev": "tsc --build tsconfig.client.json",
}
}
The general approach to change the compiler options dynamically:
since fs module can be used in node.js, you could add this util function to your node.j sproject:
const fs = require("fs")
const prettier = require("prettier")
function changeTs(){
// this guarantees that I am on server
if(typeof window === "undefined"){
// process.cwd returns base folder, current working directory
// whatever path is
const tsPath = path.join(process.cwd(), "tsconfig.json")
const tsConfig = require(tsPath)
tsConfig.compilerOptions.exclude = ["src/client"]
fs.writeFileSync(
tsPath,
prettier.format(
JSON.stringify(tsConfig), { parser: "json" }
)
)
}
}
call the above function, top-level when node.js app loads, so it tsconfig.json will be updated. default setting should be "exclude": ["src/server"]. When you are on server, it will be changed to "exclude": ["src/client"]
With this approach, you can even pass an argument to the function, so you can change the ts compiler options based on a specific setting that your app needs.

Error compiling Typescript with graphql files

So I'm new in this area, the thing is that I'm trying to compile a Typescript project with graphql files on it (with .graphqlextension).
It' based on the serverless framework, so to compile it I launch npm startwhich launches a cd src/app && tscin the command line.
The .tsfile references the .graphqlfile like this:
import SchemaDefinition from './schemaDefinition.graphql';
And the error is
data/schema/index.ts(2,30): error TS2307: Cannot find module './schemaDefinition.graphql'.
I think the issue here is in the tsccompilation, as it creates the output directory (../../built) but it is not copying the .graphqlfiles. Here is my tsconfig.json file:
{
"compilerOptions": {
"declaration": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es5",
"lib": ["dom", "es6"],
"module": "commonjs",
"allowJs": true,
"moduleResolution": "node",
"noImplicitAny": false,
"removeComments": true,
"preserveConstEnums": true,
"rootDir": "./",
"outDir": "../../built",
"sourceMap": true,
"pretty": true,
"typeRoots": [
"node_modules/#types"
],
"types": [
"#types/node",
"#types/graphql"
],
"allowSyntheticDefaultImports": true
},
"include": [
"./*"
],
"exclude": [
"node_modules"
]
}
I'm not sure if I have to do some trick to copy these files or put a precompiler step to convert the .graphqlfiles in .tsfiles, using something like this: GraphQL Code Generator
Any ideas out there? I'm stuck :S
If you are packaging the banckend in NodeJS, it should be configured in the webpack.config.js file and in the typings.d.ts files, so both the editor and the packager knows about these files.
typings.d.ts:
declare module "*.graphql" {
const value: any;
export default value;
}
code:
import * as query from query.graphql
For this to work, a loader such as raw-loader for those using Webpack will work.
module: {
rules: [
{ test: /\.json$/, loader: 'json-loader' },
{ test: /\.html$/, loader: 'raw-loader' },
{ test: /\.graphql$/, loader: 'raw-loader' },
]
This sample has been taken from https://github.com/apollographql/graphql-tools/issues/273 where there's an active discussion about different approaches.
Here is a GitHub repo which does roughly the same but with a more powerful solution: https://github.com/webpack-contrib/copy-webpack-plugin
On the other hand, if you are packaging the front, there's a handy plugin to do the work for you: https://www.npmjs.com/package/webpack-graphql-loader
Assuming the graphql files have a JS extension so are processed if they do not export anything or nothing imports them you could try the not recommended
import "./my-module.js";
Another workaround is to copy .graphql file to the output folder through automated script for example https://www.npmjs.com/package/copy

Resources