Webpack resolve alias and compile file under that alias

Tags: , , , ,



I have project which uses lerna ( monorepo, multiple packages ). Few of the packages are standalone apps.

What I want to achieve is having aliases on few of the packages to have something like dependency injection. So for example I have alias @package1/backendProvider/useCheckout and in webpack in my standalone app I resolve it as ../../API/REST/useCheckout . So when I change backend provider to something else I would only change it in webpack.

Problem

Problem appears when this alias is used by some other package ( not standalone app ). For example:

Directory structure looks like this:

Project
    packageA
           ComponentA
    packageB
           API
              REST
                  useCheckout
    standalone app

ComponentA is in packageA

useCheckout is in packageB under /API/REST/useCheckout path

ComponentA uses useCheckout with alias like import useCheckout from '@packageA/backendProvider/useCheckout

Standalone app uses componentA

The error I get is that Module not found: Can't resolve '@packageA/backendProvider/useCheckout

However when same alias is used in standalone app ( that has webpack with config provided below ) it is working. Problem occurs only for dependencies.

Potential solutions

I know that one solution would be to compile each package with webpack – but that doesn’t really seem friendly. What I think is doable is to tell webpack to resolve those aliases to directory paths and then to recompile it. First part ( resolving aliases ) is done.

Current code

As I’m using NextJS my webpack config looks like this:

 webpack: (config, { buildId, dev, isServer, defaultLoaders }) => {
    // Fixes npm packages that depend on `fs` module
    config.node = {
      fs: "empty"
    };

    const aliases = {
    ...
      "@package1/backendProvider": "../../API/REST/"
    };

    Object.keys(aliases).forEach(alias => {
      config.module.rules.push({
        test: /.(js|jsx)$/,
        include: [path.resolve(__dirname, aliases[alias])],
        use: [defaultLoaders.babel]
      });

      config.resolve.alias[alias] = path.resolve(__dirname, aliases[alias]);
    });

    return config;
  }

Answer

You don’t need to use aliases. I have a similar setup, just switch to yarn (v1) workspaces which does a pretty smart trick, it adds sym link to all of your packages in the root node_modules.

This way, each package can import other packages without any issue.

In order to apply yarn workspaces with lerna:

// lerna.json
{
  "npmClient": "yarn",
  "useWorkspaces": true,
  "packages": [
    "packages/**"
  ],
}
// package.json
{
  ...
  "private": true,
  "workspaces": [
    "packages/*",
  ]
  ...
}

This will enable yarn workspace with lerna.

The only think that remains to solve is to make consumer package to transpile the required package (since default configs of babel & webpack is to ignore node_module transpilation).

In Next.js project it is easy, use next-transpile-modules.

// next.config.js

const withTM = require('next-transpile-modules')(['somemodule', 'and-another']); // pass the modules you would like to see transpiled
 
module.exports = withTM();

In other packages that are using webpack you will need to instruct webpack to transpile your consumed packages (lets assume that they are under npm scope of @somescope/).

So for example, in order to transpile typescript, you can add additional module loader.

// webpack.config.js
{
  ...
  module: {
    rules: [
      {
        test: /.ts$/,
        loader: 'ts-loader',
        include: /[\/]node_modules[\/]@somescope[\/]/, // <-- instruct to transpile ts files from this path
        options: {
          allowTsInNodeModules: true, // <- this a specific option of ts-loader
          transpileOnly: isDevelopment,
          compilerOptions: {
            module: 'commonjs',
            noEmit: false,
          },
        },
      }
    ]
  }
  ...
  resolve: {
      symlinks: false, // <-- important
  }
}

If you have css, you will need add a section for css as well.

Hope this helps.

Bonus advantage, yarn workspaces will reduce your node_modules size since it will install duplicate packages (with the same semver version) once!



Source: stackoverflow