I am working on a nodejs app and I want to configure my path in tsconfig.json but I am having issues with the path configuration I keep getting an error Error: Cannot find module '#environment'. what could be the issue?
tsconfig
{
"compilerOptions": {
"resolveJsonModule" : true,
"target": "es6",
"module": "commonjs" ,
"lib": [
"dom",
"es6"
],
"outDir": "build",
"rootDir": "src",
"removeComments": true ,
"strict": true,
"noImplicitAny": true,
"baseUrl": "./",
"paths": {
"#lime/*": ["src/*"],
"#environment": ["src/config/environment.ts"],
},
"esModuleInterop": true
}
}
Project tree:
src - config - environment.ts
...
- index.js
package.json
tsconfig.json
...
environment.ts
import * as dotenv from 'dotenv';
dotenv.config();
interface Environment {
port: number | string,
baseUrl: string,
databaseUri: string,
}
export const environment: Environment = {
port: process.env.PORT!,
baseUrl: process.env.BASE_URL!,
databaseUri: process.env.DATABASE_URI!
}
in index.ts i imported environment.ts as import { environment } from '#environment'; please what could be wrong?
If you are trying to execute this directly with node or ts-node, you should be aware that tsconfig paths are not resolved by default by node. If you are using tsc for the build (and not webpack or similar to generate a bundle), you can add to your dependencies tsconfig-paths like this:
npm install --save tsconfig-paths
And then execute the code with:
node -r tsconfig-paths/register dist/index.js
If you are using TS code directly with ts-node, you can use:
ts-node -r tsconfig-paths/register src/index.ts
In production is suggested instead to bundle the source with webpack for example, and use a plugin like tsconfig-paths-webpack-plugin) to resolve paths while bundling.
I may not sure, i check the documentation. Seems like paths should be dir. please try this combination.
{
"baseUrl": "./",
"paths": {
"#lime/*": ["src/*"],
"#environment": ["src/config/"],
},
"esModuleInterop": true
}
}
and import like:
import { environment } from '#environment/environment'
tsconfig.json
// prettier-ignore
{
"extends": "#tsconfig/react-native/tsconfig.json", /* Recommended React Native TSConfig base */
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Completeness */
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
/* Path */
"baseUrl": "./src",
"paths": {
"#/*": ["./*"],
"#assets/*": ["assets/*"],
"#components/*": ["components/*"],
"#hooks/*": ["hooks/*"],
"#lib/*": ["lib/*"],
"#ui/*": ["ui/*"],
}
},
"exclude": [
"node_modules",
"babel.config.js",
"metro.config.js",
"jest.config.js"
]
}
babel.config.js
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
[
'module-resolver',
{
root: ['./src'],
extensions: [
'.ios.ts',
'.android.ts',
'.ts',
'.ios.tsx',
'.android.tsx',
'.tsx',
'.jsx',
'.js',
'.json',
],
alias: {
'#': './src',
'#assets': './src/assets',
'#components': './src/components',
'#hooks': './src/hooks',
'#lib': './src/lib',
'#ui': './src/ui',
},
},
],
],
};
Related
*Converting a large monorepo ejected react app to vitejs, at least i am attempting to do so. However we use several aliased imports throughout the current app and i would like to leave that functionality there. We are currently using webpack.
ts.config file
{
"compilerOptions": {
"target": "esnext",
"allowJs": true,
"skipLibCheck": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"noEmit": true,
"jsx": "preserve",
"lib": ["esnext", "dom"],
"baseUrl": ".",
"paths": {
"src/*": ["src/*"],
"#src/*": ["src/*"],
"#status/*": ["src/packages/status/*"],
"#cli/*": ["src/packages/cli/*"],
}
},
"include": [
"./declarations.d.ts",
"src",
],
}
vite.config file:
import react from '#vitejs/plugin-react';
import legacy from '#vitejs/plugin-legacy'
import path from 'path';
import { defineConfig } from 'vite';
export default defineConfig({
root: 'src',
publicDir: './src/assets',
plugins: [
react({
// Use React plugin in all *.jsx and *.tsx files
include: '**/*.{jsx,tsx}',
babel: {
configFile: true,
},
}),
legacy({
targets: ['defaults', 'not IE 11']
})
],
resolve: {
alias: {
'#src': path.resolve(__dirname, './src'),
'#status': path.resolve(__dirname, './src/packages/status'),
'#cli': path.resolve(__dirname, './src/packages/cli')
},
},
build: {
outDir: 'build',
},
css: {
preprocessorOptions: {
scss: {
quietDeps: true,
javascriptEnabled: true,
},
sass: {
quietDeps: true,
javascriptEnabled: true,
},
},
},
});
Project structure is as follows:
package.json
vite.config
ts.config
src/
packages/
pages/
index.html
app/
app.tsx
The specific error i am seeing in the console when i run vite serve src is
6:33:25 PM [vite] Internal server error: Failed to resolve import
"#cli/constants/ReviewText" from
"src/packages/cli/pages/review/Review.tsx". Does the file exist?
Have scoured the interned and tried pretty much every variation of declaring the aliases in the vite.config and ts.config files. What is weird is that it seems that the ts compiler is at least recognizing the aliased path as it shows me it the full path when hovering over import statements, so i believe my problem is with the vite.config aliasing...
I also came across this issue and I found a solution. It turns out Vite makes use of absolute paths when resolving modules and Vite also treats "src/*" as a package in node_modules and this is what causes this import resolution issues.
To fix it, What I did was make this change to my tsconfig.json
// tsconfig.json
"baseUrl": ".",
"paths": {
"/src/*": ["src/*"]
}
Now I can import any module I want as below
// App.tsx
import { Home } from "/src/pages/Home";
The key here is to prefix all your paths with a /. In your case it would be
"paths": {
"/src/*": ["src/*"],
"/#src/*": ["src/*"],
"/#status/*": ["src/packages/status/*"],
"/#cli/*": ["src/packages/cli/*"],
}
I have a TS project with the following configuration:
tsconfig.json (partial)
{
"compilerOptions": {
"module": "commonjs",
"baseUrl": "src",
"esModuleInterop": true,
},
"include": [
"src"
]
}
I have a dependency on the stripe NPM package:
{
// ...
"dependencies": {
"stripe": "^8.45.0",
}
}
Then I have the following files:
src/routes/payments/index.ts
src/stripe/index.ts
I am having some trouble with imports in src/routes/payments/index.ts. I want to import the stripe library, not my own code.
This works:
// Uses me the 'Stripe' constructor which is the default export from the package
const stripe = require('stripe')('KEY');
This does not work:
import Stripe from 'stripe';
const stripe = new Stripe('KEY');
I get the following error:
Module '"PROJECT/src/stripe/index"' has no default export.
How do I disambiguate and tell TS I want to use stripe from node_modules?
Can you try to update the tsconfig.json file like this:
{
"compilerOptions": {
"module": "commonjs",
"esModuleInterop": true,
"baseUrl": ".",
"paths": {
"*": [
"node_modules/*",
"src/*"
]
}
}
}
I am setting a configuration to run my tests in a create-react-app + typescript app (from which I have ejected). I am using jest + enzyme. In my tsconfig.json I have set baseUrl='./src' so I can use absolute paths when I import modules. For example this is a typical import statement in one of my files:
import LayoutFlexBoxItem from 'framework/components/ui/LayoutFlexBoxItem';
You can see that the path is absolute (from /src folder) and not relative.
This works fine when I run in debug mode ( yarn start )
But when I run my test ( yarn test ), I get this error:
Cannot find module 'framework/components/Navigation' from 'index.tsx'
So it looks like jest is not able to resolve this absolute path although I have set it up in my tsconfig.json. This is my tsconfig.json:
{
"compilerOptions": {
"outDir": "dist",
"module": "esnext",
"target": "es5",
"lib": ["es6", "dom"],
"sourceMap": true,
"allowJs": true,
"jsx": "react",
"moduleResolution": "node",
"rootDir": "src",
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true,
"baseUrl": "./src"
},
"exclude": [
"node_modules",
"build",
"dist",
"config",
"scripts",
"acceptance-tests",
"webpack",
"jest",
"src/setupTests.ts"
]
}
Now I can see that there is a generated tsconfig.test.json at the root of my project. This is the ts configuration used for test. And here is its content:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs"
}
}
As you can see the "module" is commonjs here whereas in the default configuration it is esnext. Could this be one reason?
Has any one been able to unit test his typescript project with Jest and absolute path? or is this a known bug? Since I have ejected from default configuration, are there some settings to put in my webpack configuration?
Thanks for your input and suggestion.
I was struggling with the same problem and actually it turns out that a simple change seems to do the trick.
I just updated the moduleDirectories field in jest.config.js.
Before
moduleDirectories: ['node_modules']
After
moduleDirectories: ['node_modules', 'src']
As many here pointed out moduleNameMapper in jest.config.js needs to define paths specified in tsconfig.json. For example, if you have paths in tsconfig.json defined as follows
// tsconfig.json
{
...
"baseUrl": "src",
"paths": {
"#alias/*": [ 'path/to/alias/*' ]
}
...
}
then your jest.config.js needs to provide those paths in moduleNameMapper in the following format:
// jest.config.js
module.exports = {
'roots': [
'<rootDir>/src'
],
'transform': {
'^.+\\.tsx?$': 'ts-jest'
},
'moduleNameMapper': {
'#alias/(.*)': '<rootDir>/src/path/to/alias/$1'
}
};
Having that we can improve our jest.config.js to convert paths defined in tsconfig.json automatically. Here is a Gist code snippet for that:
// jest.config.js
function makeModuleNameMapper(srcPath, tsconfigPath) {
// Get paths from tsconfig
const {paths} = require(tsconfigPath).compilerOptions;
const aliases = {};
// Iterate over paths and convert them into moduleNameMapper format
Object.keys(paths).forEach((item) => {
const key = item.replace('/*', '/(.*)');
const path = paths[item][0].replace('/*', '/$1');
aliases[key] = srcPath + '/' + path;
});
return aliases;
}
const TS_CONFIG_PATH = './tsconfig.json';
const SRC_PATH = '<rootDir>/src';
module.exports = {
'roots': [
SRC_PATH
],
'transform': {
'^.+\\.tsx?$': 'ts-jest'
},
'moduleNameMapper': makeModuleNameMapper(SRC_PATH, TS_CONFIG_PATH)
};
Here is how I got moduleNameMapper working.
With the below config in my tsconfig:
"paths": {
"#App/*": [
"src/*"
],
"#Shared/*": [
"src/Shared/*"
]
},
Here's the moduleNameMapper:
"moduleNameMapper": {
"#App/(.*)": "<rootDir>/src/$1",
"#Shared/(.*)": "<rootDir>/src/Shared/$1"
}
Add this following section in your package.json. after you made the changes don't forget to restart your test watchers.
"jest": {
"moduleDirectories": [
"node_modules",
"src"
],
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"roots": [
"src"
],
"testRegex": ".spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"coverageDirectory": "../coverage",
"testEnvironment": "node",
"moduleNameMapper": {
"src/(.*)": "<rootDir>/src/$1"
}
}
For me, I just needed to add
"modulePaths": ["<rootDir>/src"],
to my jest.config.js file.
Following answer to modify moduleDirectories resulted in this error:
Jest encountered an unexpected token
Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.
Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.
By default "node_modules" folder is ignored by transformers.
Here's what you can do:
• If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in your config.
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.
You'll find more details and examples of these config options in the docs:
https://jestjs.io/docs/configuration
For information about custom transformations, see:
https://jestjs.io/docs/code-transformation
Using:
modulePaths: ["node_modules", "<rootDir>/src"],
From reading the docs it appears that this a list of additional directories and so node_modules is unnecessary.
For those using an absolute path but not using named mappings, this worked for me:
# jsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
}
}
# jest.config.js
const config = {
moduleDirectories: ['node_modules', '<rootDir>'],
};
Solution using the best practice
This error occurs because of using absolute paths in the import statements of our TypeScript/Nest.js/Angular projects while using Jest. Fixing it with moduleDirectories and moduleNameMapper options may solve your problem temporarily but it creates issues with other packages such as this TypeORM issue. Also, the creator of the Nest.js framework suggests that using absolute paths is a bad practice. TypeScript's official documentation recommends using relative paths for our own modules. There's also an ESLint rule that explains why absolute paths should be avoided.
Absolute path vs Relative path
import statement with absolute path looks like:
import { AuthService } from 'src/auth/auth.service'
import statement with relative path looks like:
import { AuthService } from '../auth/auth.service'
VS Code Setting
VS Code by default uses absolute path as shown above, when we auto-import using code completion or Command/Ctrl + .. We need to change this default setting to use relative paths.
Go to VS Code settings and search for a setting: Import Module Specifier. Change it from shortest to relative.
Now from here on, VS Code will automatically import using the relative paths.
Fixing imports in the project
Now in the project files, look for the absolute paths in the imports that look like the example above and delete them. You will see errors for the packages that you deleted. Simply use the auto-import suggestions and import them back. This time they will be imported using the relative paths. This step may be tedious depending on the size of your project but it's worth it in the long run.
Hope that works out! Cheers!
ts-jest can resolve this problem perfectly!
https://kulshekhar.github.io/ts-jest/docs/getting-started/paths-mapping#jest-config-with-helper
just modify jest.config.js like this:
const { pathsToModuleNameMapper } = require('ts-jest/utils');
const { compilerOptions } = require('./tsconfig.json');
module.exports = {
// preset is optional, you don't need it in case you use babel preset typescript
preset: 'ts-jest',
// note this prefix option
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, /* { prefix: '<rootDir>/' } */)
}
Here is what worked for me:
npm i -D jest typescript
npm i -D ts-jest #types/jest
npx ts-jest config:init
Then in jest.config.js, here's my setup
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
modulePaths: ["node_modules", "<rootDir>/src"],
};
in my case, I do not have any paths in tsconfig.json but I have baseUrl set to src
If you have intalled ts-jest you can use an util function called pathsToModuleNameMapper to convert the path inside tsconfig.json to your jest.config file:
My jest.config.js file:
const { join } = require('path');
const { pathsToModuleNameMapper } = require('ts-jest')
const { compilerOptions } = require('./tsconfig.json')
/** #type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
rootDir: __dirname,
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
setupFiles: ['<rootDir>/src/config/env.ts'],
collectCoverageFrom: ["<rootDir>/src/modules/**/*UseCase.ts"],
coverageProvider: "v8",
coverageThreshold: {
global: {
lines: 40
}
},
bail: true,
clearMocks: true,
displayName: 'unit-tests',
testMatch: ["<rootDir>/src/modules/**/*.spec.ts"],
preset: 'ts-jest',
testEnvironment: 'node',
modulePaths: ["<rootDir>/src"],
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {
prefix: join('<rootDir>', compilerOptions.baseUrl)
})
};
I've using React with Typescript, I removed react-scripts-ts test --env=jsdom from npm test and added jest --watch as my default test, after I added jest.config.js to my project following these instructions https://basarat.gitbooks.io/typescript/docs/testing/jest.html
and I used the the configuration mentioned by #Antonie Laffargue (add/edit property moduleDirectories: ['node_modules', 'src']), it works perfectly.
I had a similar problem. I hope this could help to spare time for some of you.
My problem:
using create-react-app with typescript
using absolute paths (src/MyComp) to import components inside other components (e.g. App.tsx)
it was working on compile/run/build
it was not working on test
I found that the error was due to a different value of the NODE_PATH. So I set it on tests run.
I recreated the issue and the fix in here: https://github.com/alessandrodeste/...
I'm not sure if this could bring side effects on tests. Let me know if you have feedback ;)
You probably want moduleNameMapper feature of jest config.
It will map your custom import namespaces to real module locations.
see official documentation here:
https://facebook.github.io/jest/docs/en/configuration.html#modulenamemapper-object-string-string
If this happens to you in monorepo here's what fixed the problem for me:
Inside jest.config.js
roots: ["<rootDir>packages"],
moduleNameMapper: {
'#monopre/(.+)$': '<rootDir>packages/$1/src',
},
Assuming you have in tsconfig.json
"paths": {
"#monopre/*": ["packages/*/src"],
}
Adding the following to my jest config in package.json resolved this problem for me.
"moduleDirectories": [
"node_modules",
"src"
]
I use "baseUrl": "./" without any aliases in tsconfig.json
moduleDirectories: ['node_modules', '<rootDir>'] in jest.config.ts worked for me.
Now I can import local modules e.g import { Hello } from "src/modules/hello" without any problems.
jest.config.ts
/*
* For a detailed explanation regarding each configuration property and type check, visit:
* https://jestjs.io/docs/configuration
*/
export default {
clearMocks: true,
collectCoverageFrom: ['**/*.(t|j)s'],
coverageDirectory: 'coverage',
coverageProvider: 'v8',
moduleDirectories: ['node_modules', '<rootDir>'],
moduleFileExtensions: ['js', 'json', 'ts'],
roots: ['src', 'test'],
setupFiles: ['dotenv/config'],
testRegex: ['.*\\.spec\\.ts$'],
transform: {
'^.+\\.(t|j)s$': 'ts-jest',
},
};
tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"esModuleInterop": true,
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false,
"resolveJsonModule": true
},
"include": ["src/**/*", "src/**/*.json", "test/**/*"]
}
Using Svelte Kit, my solution was:
import { readFileSync } from 'fs';
import pkg from 'ts-jest/utils/index.js';
const { pathsToModuleNameMapper } = pkg;
const { compilerOptions } = JSON.parse(readFileSync('./tsconfig.json'))
export default {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['<rootDir>/**/*.test.ts'],
testPathIgnorePatterns: ['/node_modules/'],
coverageDirectory: './coverage',
coveragePathIgnorePatterns: ['node_modules'],
globals: { 'ts-jest': { diagnostics: false } },
transform: {},
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/' }),
}
I had the same problem using StitchesJS, the module was not found, the solution was to put this in my jest.config.js
moduleNameMapper: {
"stitches.config": "<rootDir>/node_modules/#stitches/react/dist/index.cjs"}
You can adapt according to the module you want.
If you have a monorepo where each project has its own tsconfig.json that extends from the base (root) tsconfig.json, and each project has its own jest.config.js, and you launch tests from the project root, you should add your moduleNameMapper to the jest.config.js in the project root.
Eg.
backend/
- tsconfig.json
- jest.config.js
frontend/
- tsconfig.json
- jest.config.js
/
- tsconfig.json
- jest.config.js
And your problem is in the jest tests frontend/ and it has a tsconfig like:
{
"compilerOptions": {
"jsx": "react-native",
"paths": {
"src/*": ["client/src/*"]
},
"noEmit": true
},
"extends": "../tsconfig.json"
}
Then adding this to the root jest.config should fix the issue:
moduleNameMapper: {
'src/(.*)': '<rootDir>/client/src/$1',
},
So in total, something like:
module.exports = {
testEnvironment: 'node',
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$',
testPathIgnorePatterns: ['cdk.out/'],
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
modulePathIgnorePatterns: ['tests'],
globals: {
'ts-jest': {
isolatedModules: true,
},
},
moduleNameMapper: {
'src/(.*)': '<rootDir>/client/src/$1',
},
};
Sort of obvious, but adding this answer since I didn't see this case in the other answers, and monorepos are still a thing :)
Kind of an old thread but recently, on top of the other suggestions of adding the paths on "moduleNameMapper": on jest.config.js and "paths": on tsconfig.json, I also had to include my test folder so the compiler could recognize it.
So on tsconfig.json, add:
...
"include": ["src", "__tests__"],
...
Hope it helps on someone googling for it 😉
I've checked other threads for more than a few days; I've found the same error online a few times, but haven't been able to replicate a posted solution. The fact that there's so many ways of writing babel/webpack configs for each different version hasn't helped much. I'm running Webpack, TS, and ESLint. The 'best case' error I've been able to get is the following. I'd really love some help! :[ Among many things, I've tried turning tsx's into jsx's and using jsx preserve instead of react.
Terminal compiler error:
ERROR in ./src/index.tsx
Module build failed (from ./node_modules/babel-loader/lib/index.js):
SyntaxError: C:\Users\Milo\Desktop\Programacion\zekeapp\src\index.tsx: Unexpected token (12:2)
10 |
11 | ReactDOM.render(
> 12 | <Provider store={store}>
| ^
13 | <Router>
14 | <Main />
15 | </Router>
index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { ThemeProvider } from 'styled-components';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import store from './store';
import Main from './containers/Main';
import { lightTheme } from './templates/theme';
ReactDOM.render(
<Provider store={store}>
<Router>
<Main />
</Router>
</Provider>,
document.getElementById('root')
);
webpack.config.tsx
import * as path from 'path';
module.exports = {
entry: path.join(__dirname, './src/index.tsx'),
mode: 'production',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './dist/scripts')
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx', '.json']
},
module: {
rules: [
{
test: /\.(js|jsx|tsx|ts)$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
]
}
};
tsconfig.json
{
"compilerOptions": {
"target": "ES2018" /* Specify ECMAScript target version: "ES3" (default), "ES5", "ES2015", "ES2016", "ES2017", "ES2018", "ES2019" or "ESNEXT". */,
"module": "commonjs" /* Specify module code generation: "none", "commonjs", "amd", "system", "umd", "es2015", or "ESNext". */,
"jsx": "preserve" /* Specify JSX code generation: "preserve", "react-native", or "react". */,
"strict": true /* Enable all strict type-checking options. */,
"noImplicitAny": false /* Raise error on expressions and declarations with an implied "any" type. */,
"moduleResolution": "node" /* Specify module resolution strategy: "node" (Node.js) or "classic" (TypeScript pre-1.6). */,
"baseUrl": "./" /* Base directory to resolve non-absolute module names. */,
"paths": {
"#server/*": ["./server/*"],
"#src/*": ["./src/*"]
},
"experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
"emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}
I got an example project working by following the official React & Webpack documentation.
Make these changes:
Rename webpack.config.tsx to webpack.config.js (it's run by node not TypeScript)
Install ts-loader to transpile the .ts/.tsx files: npm install --save-dev ts-loader
Edit webpack.config.js and configure ts-loader
This example includes babel-loader as well.
Note the exclude: /node_modules/, and configFile: path.resolve('./tsconfig.json'), lines, they are important and required to work properly (see troubleshooting section below for details)
// webpack.config.js
{
//...
module: {
rules: [
{
test: /\.(js|jsx|tsx|ts)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
},
{
loader: 'ts-loader',
options: {
configFile: path.resolve('./tsconfig.json'),
},
},
],
}
]
}
}
Edit tsconfig.json and add these settings:
// tsconfig.json
{
"compilerOptions": {
//...
// Can use to "react" if you aren't using `babel-loader` and `#babel/preset-react` to handle jsx
"jsx": "react" /* Specify JSX code generation: "preserve", "react-native", or "react". */,
// Include these so the `react` imports work nicely:
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
}
}
You should hopefully be able to build the project at this point: npx webpack
$ npx webpack
Hash: 184cde71516bcbc08144
Version: webpack 4.41.5
Time: 2558ms
Built at: 01/13/2020 2:34:08 PM
Asset Size Chunks Chunk Names
bundle.js 128 KiB 0 [emitted] main
Entrypoint main = bundle.js
[2] ./src/index.tsx 498 bytes {0} [built]
[8] ./src/Main.tsx 385 bytes {0} [built]
+ 7 hidden modules
2. Example files
Here are the file contents for my test project:
package.json
{
"devDependencies": {
"#babel/core": "^7.8.0",
"babel-loader": "^8.0.6",
"ts-loader": "^6.2.1",
"typescript": "^3.7.4",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10"
},
"dependencies": {
"#types/react": "^16.9.17",
"#types/react-dom": "^16.9.4",
"react": "^16.12.0",
"react-dom": "^16.12.0"
}
}
tsconfig.json
{
"compilerOptions": {
"target": "ES2018" /* Specify ECMAScript target version: "ES3" (default), "ES5", "ES2015", "ES2016", "ES2017", "ES2018", "ES2019" or "ESNEXT". */,
"module": "commonjs" /* Specify module code generation: "none", "commonjs", "amd", "system", "umd", "es2015", or "ESNext". */,
"strict": true /* Enable all strict type-checking options. */,
"noImplicitAny": false /* Raise error on expressions and declarations with an implied "any" type. */,
"moduleResolution": "node" /* Specify module resolution strategy: "node" (Node.js) or "classic" (TypeScript pre-1.6). */,
"baseUrl": "./" /* Base directory to resolve non-absolute module names. */,
"experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
"emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,
"forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
"jsx": "react" /* Specify JSX code generation: "preserve", "react-native", or "react". */,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
}
}
webpack.config.json
const path = require('path');
module.exports = {
entry: path.join(__dirname, './src/index.tsx'),
mode: 'production',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './dist/scripts')
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx', '.json']
},
module: {
rules: [
{
test: /\.(js|jsx|tsx|ts)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
},
{
loader: 'ts-loader',
options: {
configFile: path.resolve('./tsconfig.json'),
},
},
],
}
]
}
};
src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import Main from './Main';
ReactDOM.render( <Main />, document.getElementById('root') );
src/Main.tsx
import React from 'react';
import ReactDOM from 'react-dom';
export default function Main(){
return (<h1>This is Main</h1>);
}
3. Troubleshooting
I ran into these issues getting this set up - here are the solutions I found.
Error: You get an error like: The 'files' list in config file 'tsconfig.json' is empty.
e.g.
ERROR in [tsl] ERROR
TS18002: The 'files' list in config file 'tsconfig.json' is empty.
ERROR in ./src/index.tsx
Module build failed (from ./node_modules/ts-loader/index.js):
Error: error while parsing tsconfig.json
Solution: resolve the full tsconfig.json path
// webpack.config.js
{
loader: 'ts-loader',
options: {
// configFile: './tsconfig.json', // !! WRONG
configFile: path.resolve('./tsconfig.json'), // CORRECT
},
}
Error: You get an error like: Module not found: Error: Can't resolve '...' in 'path-to-project/node_modules/react'
e.g.
ERROR in ./node_modules/react/index.js
Module not found: Error: Can't resolve './Main' in 'C:\webpack-typescript\node_modules\react'
# ./node_modules/react/index.js 15:31-48
# ./src/index.tsx
Solution: Make sure you are excluding node_modules from the ts-loader rule.
// webpack.config.js
{
module: {
rules: [
{
test: /\.(js|jsx|tsx|ts)$/,
exclude: /node_modules/, // <--- make sure this is here!
// ...
}
]
}
}
I have a problem on set webpack alias path using create-react-app and craco, already googling it but can't solve the problem.
I got an error Module not found: Can't resolve '#app/App' in 'C:\ReactSandbox\my-project\src everytime i run application using command yarn start
Steps to reproduce:
create-react-app my-project
cd my-project
yarn add #craco/craco
cat > craco.config.js (see configuration below)
replace react-scripts to craco on 'script' section on package.json (craco start, craco build, etc)
edit file src/index.js (replace line 4, see code below)
yarn start
craco.config.js
const path = require("path");
module.exports = {
webpack: {
resolve: {
alias: {
"#app": path.resolve(__dirname, "src/"),
}
}
}
};
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from '#app/App'; //replace './App' into '#app/App'
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();
Current result
Module not found: Can't resolve '#app/App' in 'C:\ReactSandbox\my-project\src
Expected
I'm avoiding call relative path hell, instead of import module like ../../../../FilterComment.js, it would be clean to write #app/FilterComment.js
resolve for carco:
u need install craco-alias, then will write in
craco.config.js
const CracoAlias = require('craco-alias')
module.exports = {
plugins: [
{
plugin: CracoAlias,
options: {
source: 'tsconfig',
baseUrl: '.',
tsConfigPath: './tsconfig.path.json',
},
},
],
}
tsconfig.path.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"#/*": ["src/*"],
"#svg/*": ["src/assets/svg/*"],
"#img/*": ["src/assets/images/*"],
"#icons/*": ["src/assets/icons/*"],
"#shared/*": ["src/shared/*"],
"#components/*": ["src/components/*"],
"#hooks/*": ["src/hooks/*"],
"#constants/*": ["src/constants/*"],
"#layout/*": ["src/layout/*"],
"#services/*": ["src/services/*"]
}
}
}
tsconfig.json
{
"extends": "./tsconfig.path.json",
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"checkJs": false,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"declaration": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"noFallthroughCasesInSwitch": true,
"removeComments": true
},
"include": [
"src",
"src/configs",
"react-app-env.d.ts"
]
}
First - tell to IDE about aliases in tsconfig.json:
create separate file tsconfig.paths.json and add aliases:
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"#utils/*": ["utils/*"]
}
}
}
Add created tsconfig.paths.json to main tsconfig.json
{
"extends": "./tsconfig.paths.json",
... other staff ...
}
Second - tell to webpack about aliases:
Add aliases to config-overrides.js
const {
override,
addWebpackAlias
} = require('customize-cra');
const path = require("path");
module.exports = override(
addWebpackAlias({
"#utils": path.resolve(__dirname, "./src/utils"),
}),
);
my craco.config.js look likes below, it works:
const path = require('path');
module.exports = {
// ...
webpack: {
alias: {
'#': path.join(path.resolve(__dirname, './src')),
}
}
}
Just keep your file craco.config.js just like this, and you need to add 1 more file with named jsconfig.json
it's content:
{
"compilerOptions": {
"module": "commonjs",
"target": "es2016",
"jsx": "preserve",
"checkJs": true,
"baseUrl": "./src/",
"paths": {
"#app/*": ["./*"]
}
},
"exclude": ["node_modules", "**/node_modules/*"]
}
then you can import from the absolute path like #app/FilterComment.js
It also worked for VSCode (editor now understand where #app point to).
But if you want VSCode to do the import automatically, then you should add more config for it to force it always do the import from absolute path.
file .vscode/settings.json
content:
{
"javascript.preferences.importModuleSpecifier": "non-relative"
}
An example (which I'm using) without using dependency:
//craco.config.js
const path = require('path')
const tsconfig = require('./tsconfig.base.json')
const removeAsterisk = path => path.replace('/*', '')
const aliasProps = Object.entries(tsconfig.compilerOptions.paths).map(([key, value]) => {
const newKey = removeAsterisk(key)
let newValue = removeAsterisk(value[0])
newValue = path.resolve(__dirname, newValue)
return [newKey, newValue]
})
const alias = Object.fromEntries(aliasProps)
module.exports = {
webpack: {
alias,
},
}
//tsconfig.base.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"~/*": ["./src/*"],
//...
}
}
}
// tsconfig.json
{
"extends": "./tsconfig.base.json",
"compilerOptions": {/*...*/}
}
Use react-app-alias:
Install craco and react-app-alias and create craco config:
// craco.config.js
const {CracoAliasPlugin} = require('react-app-alias')
module.exports = {
plugins: [
{
plugin: CracoAliasPlugin,
options: {}
}
]
}
Replace react-scripts with craco in package.json.
Create typescript config and alias in separated config like this:
// tsconfig.paths.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"example/*": ["example/src/*"],
"#app/*": ["app/src/*"]
}
}
}
// tsconfig.json
{
"extends": "./tsconfig.paths.json",
// ...
}
Now you can do import like this:
import App from '#app/App'
Maybe the OP had a different version of React / Webpack (I'm using today's current versions), but putting the alias object directly inside the webpack object (without nesting it in the resolve object) did the trick for me:
const path = require("path");
module.exports = {
webpack: {
alias: {
"#app": path.resolve(__dirname, "src"),
}
}
};
I am using craco-alias plugin for automatic aliases generation for Webpack and Jest.
(I am just an intern but that's how senior developers in my company are using absolute imports in react)
Install
yarn add #craco/craco
yarn add craco-alias
craco.config.js
const CracoAlias = require('craco-alias');
module.exports = {
plugins: [
{
plugin: CracoAlias,
options: {
source: 'tsconfig',
baseUrl: './src',
tsConfigPath: './tsconfig.paths.json', // or wherever you have defined your paths
},
},
],
};
And make sure that you scripts in package.json look like the following
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "craco eject",
...
},
Then It'll work as specified in tsconfig.json.
FYI I have,
"react-scripts": "4.0.3"
"#craco/craco": "^6.3.0"
"craco-alias": "^3.0.1"