oklas / react-app-alias

:label: Alias and multiple src directory for craco or rewired create-react-app
MIT License
173 stars 18 forks source link
alias cra craco create-react-app monorepo multi multirepo react react-app-rewired workaround

Alias solution for craco or rewired create-react-app

This is more than simple alias. This is also a multi-project src directory. Currently, create-react-app (CRA) does not support more than one src directory in the project. Monorepo, multi-repo and library projects with examples require more than one directory like src.

This is merely an alias and multi-source solution for CRA and this is not a replacement for multi-package management tools like Lerna.

This requires to modify the CRA webpack configuration in runtime (without ejecting) and works with one of:

Npm package Npm downloads Dependency Status Dependency Status Dependency Status

This allows:

This is designed for:

Advantages over other solutions:

Installation

yarn add --dev react-app-alias

or

npm install --save-dev react-app-alias

Usage

By default folders for alias may be near to src folder or in it. Outside of project root is implemented in react-app-alias-ex (suffix -ex).

Usage steps:

Enumerate aliases in jsconfig.paths.json or tsconfig.paths.json

Create a separate file jsconfig.paths.json or tsconfig.paths.json, like this:

// jsconfig.paths.json or tsconfig.paths.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "example/*": ["example/src/*"],
      "@library/*": ["library/src/*"]
    }
  }
}

Add extends section to jsconfig.json or tsconfig.json

The paths section must not be configured directly in jsconfig.json or tsconfig.json, but in a separate extends file mentioned above. Now include this file in extends section, like this:

// jsconfig.json or tsconfig.json
{
  "extends": "./jsconfig.paths.json", // or "./tsconfig.paths.json"
  "compilerOptions": {
    // ...
  }
}

Configure plugin for craco or react-app-rewired

// config-overrides.js
const {aliasWebpack, aliasJest} = require('react-app-alias')

const options = {} // default is empty for most cases

module.exports = aliasWebpack(options)
module.exports.jest = aliasJest(options)
// craco.config.js

const {CracoAliasPlugin} = require('react-app-alias')

const options = {} // default is empty for most cases

module.exports = {
  plugins: [
    {
      plugin: CracoAliasPlugin,
      options: {}
    }
  ]
}

Enable craco or react-app-rewired

Integrating react-app-rewired into your project is simple (see its documentation): Create config-overrides.js mentioned above, in the project's root directory (the same including the package.json and src directory). Install react-app-rewired

yarn add --dev react-app-rewired
- or -
npm install --save-dev react-app-rewired

and rewrite the package.json like this:

  "scripts": {
-   "start": "react-scripts start",
+   "start": "react-app-rewired start",
+   ... // same way
  }

According to craco docs install craco:

yarn add --dev craco
- or -
npm install --save-dev craco

and replace react-scripts in package.json:

  "scripts": {
-   "start": "react-scripts start",
+   "start": "craco start",
+   ... // same way
  }

Using baseUrl

See also experimental autoscan feature

Using SWC etc

Alias plugin must be aplied after SWC plugin.

Plugin SWC must be declared in plugin section before alias plugn. This is because SWC plugn recreate SWC configuration instead of babel configuration. Both babel and swc configurations originally without alias configuration. So to configure alias, alias plugin must be aplied after SWC plugin:

const { CracoAliasPlugin } = require('react-app-alias');
const CracoSwcPlugin = require('craco-swc');

module.exports = {
  plugins: [
    { plugin: CracoSwcPlugin },
    { plugin: CracoAliasPlugin },
  ],
}

API

  Options {
    alias?: { [alias: string]: string }; // optional alias map
    tsconfig?: string, // optional tsconfig.json path
    jsconfig?: string, // optional jsconfig.json path
    baseUrl?: string, // optional by default from config
  }

// optional alias map has following form:
const alias = { example: 'example/src', '@library': 'library/src', }

The function aliasWebpack() accepts aliases declared in form:

const aliasMap = {
  example: 'example/src',
  '@library': 'library/src',
}

const options = {
  alias: aliasMap,
}

module.exports = aliasWebpack(options)
module.exports.jest = aliasJest(options)

To make all things worked, aliases must be declared in jsconfig.json or tsconfig.json. However, it must be declared in a separate extends file (see section Workaround for "aliased imports are not supported" below)

The result is a function which will modify Wepack config

The function configPaths() loads paths from file compatible with jsconfig.json or tsconfig.json and returns path in form acceptable for aliasWebpack() function. The tsconfig.json is prioritized over the jsconfig.json in the loading sequence.

const aliasMap = configPaths('./tsconfig.paths.json')

const options = {
  alias: aliasMap,
}

module.exports = aliasWebpack(aliasMap)

As any react-app-rewire or customize-cra rewire extension this can be integrated with another:

module.exports = function override(config) {
  const modifiedConfig = aliasWebpack(...)(config)
  ...
  return someElse(modifiedConfig)
}
module.exports.jest = function override(config) {
  const modifiedConfig = aliasJest(...)(config)
  ...
  return modifiedConfig
}

Workaround for "aliased imports are not supported"

CRA overwrites your tsconfig.json at runtime and removes paths from the tsconfig.json, which is not officially supported, with this message:

The following changes are being made to your tsconfig.json file:
  - compilerOptions.paths must not be set (aliased imports are not supported)

The suggested workaround is to move the paths to a different .json file, e.g. tsconfig.paths.json, like this:

/* tsconfig.paths.json */
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "example/*": ["example/src/*"],
      "@library/*": ["library/src/*"]
    }
  }
}

with that file's subsequent inclusion in the tsconfig.json using extends:

/* tsconfig.json */
{
  "extends": "./tsconfig.paths.json"
}

Outside of root

Alias folders outside of the root of the project currently fully functional and works fine but are not recommended. It may bring hard-to-detect errors.

Alias with support of Outside of root is implemented in separated library:

react-app-alias-ex

with identical API, just install and add suffix -ex in import statement:

- const {aliasWebpack, CracoAliasPlugin} = require('react-app-alias')
+ const {aliasWebpack, CracoAliasPlugin} = require('react-app-alias-ex')

Tips

Confusions in deps versions may bring unclear errors or problems. For example, an application is not working without any error. Or another example is error in react-router - <Route> component do not see <Router> when actually code is correct and it falls with:

should not use Route or withRouter() outside a Router

This may be a result of some confusion in node_modules folders for multi-repo projects. Same take place in plain create-react-app if somehow one or more additional node_modulest directories appear in src.

To avoid this problem use only one main project node_modules directory.

Default bundler configuration doesn't assume your configuration and may mix deps from node_modules from different projects (top project and nested project) so this may bring mentioned above confusions with deps versions. To avoid problems: do not install and run within nested project directly when it is nested or integrated in another one - but only independent top level configuration Or consider to eject or configure Webpack manually.

Some libraries use instanceof and other type comparisions. For example , two objects created with the same params in the same code of the same library version but installed in different node_modules and bundled separately - will mostly have the same data and same behaviour but different instance type. Such libraries will be unable to recognize their own objects and will lead to unpredictable behaviour. So use only one main project node_modules directory.