javascripttypescriptabsolute-pathtsconfigtsconfig-paths

TypeScript compiler error while finding absolute paths mentioned in `tsconfig.json`


I have the following directory structure:

.
├── \ .babelrc
├── README.md
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
│   ├── icon128.png
│   ├── icon16.png
│   ├── icon48.png
│   ├── manifest.json
│   └── popup.html
├── src
│   ├── App.tsx
│   ├── background.ts
│   ├── components
│   │   ├── Main.tsx
│   │   ├── Options.tsx
│   │   ├── SiteImage.tsx
│   │   ├── TrafficSignal.tsx
│   │   └── index.ts
│   ├── content.ts
│   ├── custom.d.ts
│   ├── popup.tsx
│   ├── store
│   │   ├── FrameItContext.tsx
│   │   ├── FrameItStore.ts
│   │   └── index.ts
│   ├── styles
│   │   ├── popup.css
│   │   └── tailwind.css
│   └── types
│       └── index.ts
├── tailwind.config.js
├── tsconfig.json
└── webpack.config.js

And tsconfig.json looks like:

{
    "compilerOptions": {
        "baseUrl": "node_modules",
        "paths": {
            "@/*": ["../src/*"]
        },
        "outDir": "./dist/",
        "sourceMap": true,
        "strict": true,
        "noImplicitReturns": true,
        "noImplicitAny": true,
        "module": "es6",
        "moduleResolution": "node",
        "target": "es5",
        "allowJs": true,
        "jsx": "react",
    },
    "exclude": ["node_modules"],
    "include": [
        "./src/**/*",
        "src/custom.d.ts"
    ]
}

My App.tsx looks like:

import { Main } from '@/components/index'
import { config, FrameItProvider } from '@/store/index'

TS error looks like:

ERROR in ./src/App.tsx 2:0-42
Module not found: Error: Can't resolve '@/components/index'

ERROR in ./src/App.tsx 3:0-48
Module not found: Error: Can't resolve '@/store/index'

How do I solve this? I'm following Next.js absolute imports structure.

Even tried doing:

{
    "compilerOptions": {
        "baseUrl": ".",
        "paths": {
            "@/*": ["./src/*"]
        },
}

But it still gives error. How do I solve it?

I have webpack.config.js too, which looks like:

const webpack = require('webpack')
const path = require('path')
const CopyPlugin = require('copy-webpack-plugin')

const config = {
  entry: {
    popup: path.join(__dirname, 'src/popup.tsx'),
    content: path.join(__dirname, 'src/content.ts'),
    background: path.join(__dirname, 'src/background.ts'),
  },
  output: { path: path.join(__dirname, 'dist'), filename: '[name].js' },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        use: 'babel-loader',
        exclude: /node_modules/,
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader', 'postcss-loader'],
        exclude: /\.module\.css$/,
      },
      {
        test: /\.ts(x)?$/,
        loader: 'ts-loader',
        exclude: /node_modules/,
      },
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
              modules: true,
            },
          },
        ],
        include: /\.module\.css$/,
      },
      {
        test: /\.svg$/,
        use: 'file-loader',
      },
      {
        test: /\.png$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              mimetype: 'image/png',
            },
          },
        ],
      },
    ],
  },
  resolve: {
    extensions: ['.js', '.jsx', '.tsx', '.ts'],
    alias: {
      'react-dom': '@hot-loader/react-dom',
    },
  },
  devServer: {
    contentBase: './dist',
  },
  plugins: [
    new CopyPlugin({
      patterns: [{ from: 'public', to: '.' }],
    }),
  ],
}

module.exports = config

Do I have to specify anything in webpack.config.js? Because VSCode autocomplete is working just fine.


Solution

  • It was a webpack issue, not TS which I found through https://decembersoft.com/posts/say-goodbye-to-relative-paths-in-typescript-imports/

    I kept tsconfig.json as it is:

    {
        "compilerOptions": {
            "baseUrl": "node_modules",
            "paths": {
                "@/*": ["../src/*"],
            },
    }
    

    And I had to add the following code to webpack.config.js:

    const fs = require('fs')
    
    const srcPath = (subdir) => path.join(__dirname, 'src', subdir)
    const getFilesAndDirectories = (source) =>
        fs.readdirSync(source, { withFileTypes: true }).map((dirent) => dirent.name)
    let absoluteImports = {}
    getFilesAndDirectories('src').forEach((fileName) => {
        const fileNameWithoutExtension = path.parse(fileName).name
        absoluteImports[`@/${fileNameWithoutExtension}`] = srcPath(fileName)
    })
    
    const config = {
        .
        .
        .
        resolve: {
            alias: {
                'react-dom': '@hot-loader/react-dom',
                ...absoluteImports,
            },
        },
    }
    

    That did the trick 🎉