├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── docs ├── how-it-works.md └── migration.md ├── example ├── above-root-js │ └── src │ │ └── AboveRootJs.js ├── above-root-ts │ └── src │ │ └── AboveRootTs.tsx ├── above │ ├── .gitignore │ ├── README.md │ ├── craco.config.js │ ├── near-src │ │ └── src │ │ │ └── NearSrc.tsx │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ ├── src │ │ ├── App.css │ │ ├── App.test.tsx │ │ ├── App.tsx │ │ ├── InternalEx │ │ │ └── index.tsx │ │ ├── index.css │ │ ├── index.tsx │ │ ├── logo.svg │ │ ├── react-app-env.d.ts │ │ ├── reportWebVitals.ts │ │ └── setupTests.ts │ ├── tsconfig.json │ ├── tsconfig.paths.json │ └── yarn.lock ├── below │ ├── .gitignore │ ├── README.md │ ├── config-overrides.js │ ├── craco.config.js │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ ├── src │ │ ├── App.css │ │ ├── App.test.tsx │ │ ├── App.tsx │ │ ├── Internal │ │ │ └── index.tsx │ │ ├── index.css │ │ ├── index.tsx │ │ ├── logo.svg │ │ ├── react-app-env.d.ts │ │ ├── reportWebVitals.ts │ │ └── setupTests.ts │ ├── tsconfig.json │ ├── tsconfig.paths.json │ └── yarn.lock └── main │ ├── .gitignore │ ├── README.md │ ├── config-overrides.js │ ├── craco.config.js │ ├── near-src │ └── src │ │ └── NearSrc.tsx │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ ├── src │ ├── App.css │ ├── App.test.tsx │ ├── App.tsx │ ├── Internal │ │ └── index.tsx │ ├── index.css │ ├── index.tsx │ ├── logo.svg │ ├── react-app-env.d.ts │ ├── reportWebVitals.ts │ └── setupTests.ts │ ├── tsconfig.json │ ├── tsconfig.paths.json │ └── yarn.lock ├── jest.config.js ├── lerna.json ├── lib-for-support ├── aliasDangerous.js └── index.js ├── package-for-support.json ├── package.json ├── packages ├── react-app-alias-ex │ ├── README.md │ ├── __tests__ │ │ └── react-app-alias-ex.test.js │ ├── package.json │ └── src │ │ └── index.js └── react-app-alias │ ├── README.md │ ├── __tests__ │ ├── 59-tsconfig.json │ ├── 59-tsconfig.paths.json │ └── react-app-alias.test.js │ ├── package.json │ └── src │ └── index.js ├── src-for-support ├── aliasDangerous.js └── index.js └── yarn.lock /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/github/administering-a-repository/displaying-a-sponsor-button-in-your-repository 2 | 3 | github: [oklas] 4 | custom: https://paypal.me/oklaspec 5 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | tests: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | node-version: [14, 16] 11 | fail-fast: false 12 | steps: 13 | - name: Checkout ${{ matrix.node-version }} 14 | uses: actions/checkout@v2 15 | 16 | - name: Get yarn cache 17 | id: yarn-cache 18 | run: | 19 | echo "::set-output name=path::$(yarn cache dir)" 20 | echo "::set-output name=version::$(yarn -v)" 21 | 22 | - name: Setup Node.js ${{ matrix.node }} 23 | uses: actions/setup-node@v2 24 | with: 25 | node-version: ${{ matrix.node-version }} 26 | 27 | - name: Cache dependencies ${{ matrix.node-version }} 28 | uses: actions/cache@v2 29 | env: 30 | key-prefix: ${{ runner.os }}-node-${{ matrix.node-version }} 31 | with: 32 | path: | 33 | **/node_modules 34 | **/.eslintcache 35 | ${{ steps.yarn-cache.outputs.path }} 36 | key: ${{ env.key-prefix }}-${{ hashFiles('**/yarn.lock') }} 37 | restore-keys: | 38 | ${{ env.key-prefix }}- 39 | 40 | - name: Install node_modules 41 | run: yarn && npx lerna bootstrap 42 | 43 | - name: Test – unit tests 44 | run: yarn test:unit --coverage 45 | env: 46 | CI: true 47 | 48 | - name: Test – integration 49 | run: yarn test:integration 50 | 51 | - name: Test – Eslint 52 | if: ${{ always() && matrix.node-version == '14' }} 53 | run: yarn eslint 54 | 55 | - name: Test – TSCheck 56 | #if: ${{ always() && matrix.node-version == '14' }} 57 | if: false 58 | run: yarn tscheck 59 | 60 | - name: Publish Test Report 61 | if: ${{ always() && matrix.node-version == '14' }} 62 | uses: mikepenz/action-junit-report@v2 63 | with: 64 | check_name: JUnit Annotations for Node ${{ matrix.node-version }} 65 | report_paths: '**/coverage/*.xml' 66 | 67 | - name: Send codecov.io stats 68 | if: matrix.node-version == '14' 69 | run: bash <(curl -s https://codecov.io/bash) || echo '' 70 | 71 | publish: 72 | if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/alpha' || github.ref == 'refs/heads/beta' 73 | needs: [tests] 74 | runs-on: ubuntu-latest 75 | steps: 76 | - name: Checkout 14 77 | uses: actions/checkout@v2 78 | 79 | - name: Setup Node.js 14 80 | uses: actions/setup-node@v2 81 | with: 82 | node-version: 14 83 | 84 | - name: Cache dependencies 14 85 | uses: actions/cache@v2 86 | env: 87 | key-prefix: ${{ runner.os }}-node-14 88 | with: 89 | path: | 90 | **/node_modules 91 | **/.eslintcache 92 | ${{ steps.yarn-cache.outputs.path }} 93 | key: ${{ env.key-prefix }}-${{ hashFiles('**/yarn.lock') }} 94 | restore-keys: | 95 | ${{ env.key-prefix }}- 96 | 97 | - name: Install node_modules 98 | run: yarn 99 | 100 | - name: Build 101 | if: false 102 | run: yarn build 103 | 104 | - name: Semantic Release 105 | run: yarn release 106 | env: 107 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} 108 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 109 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | example/*/node_modules 3 | test/dists 4 | coverage 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright (c) 2019-present Serguei Okladnikov 5 | ------------------------------------------------------------------ 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Alias solution for craco or rewired create-react-app 2 | 3 | This is more than simple alias. This is also a multi-project `src` 4 | directory. Currently, `create-react-app` (CRA) does not support more than one 5 | `src` directory in the project. Monorepo, multi-repo and library projects with 6 | examples require more than one directory like `src`. 7 | 8 | This is merely an alias and multi-source solution for CRA 9 | and this is not a replacement for multi-package management tools like 10 | [Lerna](https://github.com/lerna/lerna). 11 | 12 | This requires to modify the CRA webpack configuration in runtime 13 | (without ejecting) and works with one of: 14 | * **[react-app-rewired](https://github.com/timarney/react-app-rewired)** 15 | * **[customize-cra](https://github.com/arackaf/customize-cra)** 16 | * **[craco](https://github.com/gsoft-inc/craco)** (see [Using craco](#using-craco) below) 17 | 18 | [![Npm package](https://img.shields.io/npm/v/react-app-rewire-alias.svg?style=flat)](https://npmjs.com/package/react-app-rewire-alias) 19 | [![Npm downloads](https://img.shields.io/npm/dm/react-app-rewire-alias.svg?style=flat)](https://npmjs.com/package/react-app-rewire-alias) 20 | [![Dependency Status](https://david-dm.org/oklas/react-app-rewire-alias.svg)](https://david-dm.org/oklas/react-app-rewire-alias) 21 | [![Dependency Status](https://img.shields.io/github/stars/oklas/react-app-rewire-alias.svg?style=social&label=Star)](https://github.com/oklas/react-app-rewire-alias) 22 | [![Dependency Status](https://img.shields.io/twitter/follow/oklaspec.svg?style=social&label=Follow)](https://twitter.com/oklaspec) 23 | 24 | #### This allows: 25 | 26 | * quality and secure exports from outside `src` 27 | * absolute imports 28 | * any `./directory` at root outside of `src` with Babel/Typescript and all CRA features 29 | 30 | #### This is designed for: 31 | 32 | * monorepo projects 33 | * multi-repo projects 34 | * library projects with examples 35 | 36 | #### Advantages over other solutions: 37 | 38 | * provided fully functional aliases and allows the use of Babel, JSX, etc. 39 | outside of `src` (outside of project `root` may be enbled with special way 40 | see the section below) 41 | 42 | * provided fully secure aliases and uses the same module scope plugin from 43 | the original create-react-app package for modules (instead of removing it), 44 | to minimize the probability of including unwanted code 45 | 46 | #### Installation 47 | 48 | ```sh 49 | yarn add --dev react-app-alias 50 | ``` 51 | 52 | or 53 | 54 | ```sh 55 | npm install --save-dev react-app-alias 56 | ``` 57 | 58 | ### Usage 59 | 60 | By default folders for alias may be near to **src** folder or in it. 61 | Outside of project `root` is implemented in `react-app-alias-ex` (suffix `-ex`). 62 | 63 | Usage steps: 64 | 65 | * enumerate aliases in *jsconfig.paths.json* or *tsconfig.paths.json* 66 | * include it in *jsconfig.json* or *tsconfig.json* 67 | * enable your favorite any of *react-app-rewired* or *craco* 68 | * apply react-app-alias in config 69 | 70 | #### Enumerate aliases in **jsconfig.paths.json** or **tsconfig.paths.json** 71 | 72 | Create a separate file `jsconfig.paths.json` or `tsconfig.paths.json`, like this: 73 | 74 | ```js 75 | // jsconfig.paths.json or tsconfig.paths.json 76 | { 77 | "compilerOptions": { 78 | "baseUrl": ".", 79 | "paths": { 80 | "example/*": ["example/src/*"], 81 | "@library/*": ["library/src/*"] 82 | } 83 | } 84 | } 85 | ``` 86 | 87 | #### Add **extends** section to **jsconfig.json** or **tsconfig.json** 88 | 89 | The **paths** section must not be configured directly in `jsconfig.json` 90 | or `tsconfig.json`, but in a separate extends file mentioned above. 91 | Now include this file in **extends** section, like this: 92 | 93 | ```js 94 | // jsconfig.json or tsconfig.json 95 | { 96 | "extends": "./jsconfig.paths.json", // or "./tsconfig.paths.json" 97 | "compilerOptions": { 98 | // ... 99 | } 100 | } 101 | ``` 102 | 103 | ### Configure plugin for craco or react-app-rewired 104 | 105 | * **`react-app-rewired`** 106 | 107 | ```js 108 | // config-overrides.js 109 | const {aliasWebpack, aliasJest} = require('react-app-alias') 110 | 111 | const options = {} // default is empty for most cases 112 | 113 | module.exports = aliasWebpack(options) 114 | module.exports.jest = aliasJest(options) 115 | ``` 116 | 117 | * **`craco`** 118 | 119 | ```js 120 | // craco.config.js 121 | 122 | const {CracoAliasPlugin} = require('react-app-alias') 123 | 124 | const options = {} // default is empty for most cases 125 | 126 | module.exports = { 127 | plugins: [ 128 | { 129 | plugin: CracoAliasPlugin, 130 | options: {} 131 | } 132 | ] 133 | } 134 | ``` 135 | 136 | 137 | #### Enable craco or react-app-rewired 138 | 139 | * **`react-app-rewired`** 140 | 141 | Integrating `react-app-rewired` into your project is simple 142 | (see [its documentation](https://github.com/timarney/react-app-rewired#readme)): 143 | Create `config-overrides.js` mentioned above, in the project's root directory 144 | (the same including the `package.json` and `src` directory). 145 | Install `react-app-rewired` 146 | 147 | ```sh 148 | yarn add --dev react-app-rewired 149 | - or - 150 | npm install --save-dev react-app-rewired 151 | ``` 152 | and rewrite the `package.json` like this: 153 | 154 | ```diff 155 | "scripts": { 156 | - "start": "react-scripts start", 157 | + "start": "react-app-rewired start", 158 | + ... // same way 159 | } 160 | ``` 161 | 162 | * **`craco`** 163 | 164 | According to [craco](https://github.com/gsoft-inc/craco/blob/master/packages/craco/README.md#installation) 165 | docs install craco: 166 | 167 | ```sh 168 | yarn add --dev craco 169 | - or - 170 | npm install --save-dev craco 171 | ``` 172 | 173 | and replace `react-scripts` in `package.json`: 174 | 175 | ```diff 176 | "scripts": { 177 | - "start": "react-scripts start", 178 | + "start": "craco start", 179 | + ... // same way 180 | } 181 | ``` 182 | 183 | 184 | #### Using baseUrl 185 | 186 | * **`baseUrl = '.'`** 187 | 188 | * able to create alias outside of `src` (near `src`) 189 | * for each directory in `src` alias **does not** created automatically (must be declared manually) 190 | 191 | * **baseUrl = 'src'** 192 | 193 | * alias outside of `src` is not possible, only in directory specified in `baseUrl` 194 | * for each folder in `src` alias created automatically with same name as folder 195 | 196 | See also experimental [autoscan](https://github.com/oklas/react-app-alias/issues/70) feature 197 | 198 | ### Using SWC etc 199 | 200 | Alias plugin must be aplied **after** SWC plugin. 201 | 202 | Plugin SWC must be declared in plugin section before alias plugn. This is because 203 | SWC plugn recreate SWC configuration instead of babel configuration. Both babel and 204 | swc configurations originally without alias configuration. So to configure alias, 205 | **alias plugin must be aplied after SWC plugin**: 206 | 207 | ```js 208 | const { CracoAliasPlugin } = require('react-app-alias'); 209 | const CracoSwcPlugin = require('craco-swc'); 210 | 211 | module.exports = { 212 | plugins: [ 213 | { plugin: CracoSwcPlugin }, 214 | { plugin: CracoAliasPlugin }, 215 | ], 216 | } 217 | ``` 218 | 219 | 220 | ### API 221 | 222 | * **options** 223 | 224 | ```ts 225 | Options { 226 | alias?: { [alias: string]: string }; // optional alias map 227 | tsconfig?: string, // optional tsconfig.json path 228 | jsconfig?: string, // optional jsconfig.json path 229 | baseUrl?: string, // optional by default from config 230 | } 231 | ``` 232 | 233 | // optional alias map has following form: 234 | const alias = { 235 | example: 'example/src', 236 | '@library': 'library/src', 237 | } 238 | 239 | 240 | * **aliasWebpack(options)(webpackConfig)** 241 | 242 | The function `aliasWebpack()` accepts aliases declared in form: 243 | 244 | ```js 245 | const aliasMap = { 246 | example: 'example/src', 247 | '@library': 'library/src', 248 | } 249 | 250 | const options = { 251 | alias: aliasMap, 252 | } 253 | 254 | module.exports = aliasWebpack(options) 255 | module.exports.jest = aliasJest(options) 256 | ``` 257 | 258 | To make all things worked, aliases must be declared in `jsconfig.json` or `tsconfig.json`. 259 | However, it must be declared in a separate extends file (see section `Workaround for 260 | "aliased imports are not supported"` below) 261 | 262 | The result is a function which will modify Wepack config 263 | 264 | * **configPaths()** 265 | 266 | The function `configPaths()` loads paths from file compatible with `jsconfig.json` 267 | or `tsconfig.json` and returns path in form acceptable for `aliasWebpack()` function. 268 | The `tsconfig.json` is prioritized over the `jsconfig.json` in the loading sequence. 269 | 270 | ```js 271 | const aliasMap = configPaths('./tsconfig.paths.json') 272 | 273 | const options = { 274 | alias: aliasMap, 275 | } 276 | 277 | module.exports = aliasWebpack(aliasMap) 278 | ``` 279 | 280 | * **extendability** 281 | 282 | As any `react-app-rewire` or `customize-cra` rewire extension this can be integrated 283 | with another: 284 | 285 | ```js 286 | module.exports = function override(config) { 287 | const modifiedConfig = aliasWebpack(...)(config) 288 | ... 289 | return someElse(modifiedConfig) 290 | } 291 | module.exports.jest = function override(config) { 292 | const modifiedConfig = aliasJest(...)(config) 293 | ... 294 | return modifiedConfig 295 | } 296 | ``` 297 | 298 | #### Workaround for "aliased imports are not supported" 299 | 300 | CRA [overwrites](/blob/v3.4.1/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js#L242) 301 | your `tsconfig.json` at runtime and removes `paths` from the `tsconfig.json`, 302 | which is not officially supported, with this message: 303 | 304 | > ``` 305 | > The following changes are being made to your tsconfig.json file: 306 | > - compilerOptions.paths must not be set (aliased imports are not supported) 307 | > ``` 308 | 309 | The [suggested workaround](https://github.com/facebook/create-react-app/issues/5645#issuecomment-435201019) 310 | is to move the paths to a different `.json` file, e.g. `tsconfig.paths.json`, like this: 311 | 312 | ```json 313 | /* tsconfig.paths.json */ 314 | { 315 | "compilerOptions": { 316 | "baseUrl": ".", 317 | "paths": { 318 | "example/*": ["example/src/*"], 319 | "@library/*": ["library/src/*"] 320 | } 321 | } 322 | } 323 | ``` 324 | 325 | with that file's subsequent inclusion in the `tsconfig.json` using `extends`: 326 | 327 | ```json 328 | /* tsconfig.json */ 329 | { 330 | "extends": "./tsconfig.paths.json" 331 | } 332 | ``` 333 | 334 | ## Outside of root 335 | 336 | Alias folders outside of the root of the project currently fully functional and 337 | works fine but are not recommended. It may bring hard-to-detect errors. 338 | 339 | Alias with support of **Outside of root** is implemented in separated library: 340 | 341 | `react-app-alias-ex` 342 | 343 | with identical API, just install and add suffix `-ex` in import statement: 344 | 345 | ```diff 346 | - const {aliasWebpack, CracoAliasPlugin} = require('react-app-alias') 347 | + const {aliasWebpack, CracoAliasPlugin} = require('react-app-alias-ex') 348 | ``` 349 | 350 | ## Tips 351 | 352 | * **keep only one `node_modules` directory** 353 | 354 | Confusions in deps versions may bring unclear errors or problems. For example, an application 355 | is not working without any error. Or another example is error in `react-router` - `` 356 | component do not see `` when actually code is correct and it falls with: 357 | 358 | > should not use Route or withRouter() outside a Router 359 | 360 | This may be a result of some confusion in `node_modules` folders for multi-repo projects. 361 | Same take place in plain `create-react-app` if somehow one or more additional 362 | `node_modulest` directories appear in `src`. 363 | 364 | To avoid this problem **use only one main project `node_modules` directory**. 365 | 366 | * **keep away from working with nested project** 367 | 368 | Default bundler configuration doesn't assume your configuration and may mix deps from 369 | `node_modules` from different projects (top project and nested project) so this may 370 | bring mentioned above confusions with deps versions. To avoid problems: 371 | **do not install and run within nested project directly when it is nested or integrated 372 | in another one - but only independent top level configuration** Or consider to eject 373 | or configure Webpack manually. 374 | 375 | * **do not relay to deps versions synchronization** 376 | 377 | Some libraries use `instanceof` and other type comparisions. For example , two objects 378 | created with the same params in the same code of the same library version but installed in 379 | different `node_modules` and bundled separately - will mostly have the same data and same 380 | behaviour but different instance type. Such libraries will be unable to recognize their own 381 | objects and will lead to unpredictable behaviour. So **use only one main project 382 | `node_modules` directory**. 383 | -------------------------------------------------------------------------------- /docs/how-it-works.md: -------------------------------------------------------------------------------- 1 | # How it works 2 | 3 | The library `create-react-app` is an executable that generates minimal simple apps with 4 | `react-scripts` dependency. The `react-scripts` is a complicated library that has webpack 5 | and jest and other configuration files. It makes app simple by doing all the complicated 6 | work it configures webpack with plugins and loaders, configure jest, etc. 7 | 8 | If someone wants to modify configuration, react-scripts suggests eject command. 9 | Eject copys all mentioned config files to your project so you can modify it. 10 | But after eject the project owner must update and fix configuration which requires 11 | knowledge and attention to packages updates and security fixes. Updating `react-sctipt` 12 | is more simple but it is not possible after eject. 13 | 14 | The library `react-scripts` does not provide a way to get runtime configs and do some 15 | fixes. There are some libraries like `react-app-rewired`, `customize-cra`, `craco` that do 16 | runtime patch configs of `react-scripts` and pass final config to user function to modify. 17 | 18 | The react-app-alias library confgures aliases in runtime. 19 | -------------------------------------------------------------------------------- /docs/migration.md: -------------------------------------------------------------------------------- 1 | # Migration 2 | 3 | ## Migration from react-app-rewired-alias 4 | 5 | ### Generic mode 6 | 7 | * Replace library name for generic mode 8 | 9 | ```diff 10 | - const {...} = require('react-app-rewire-alias') 11 | + const {...} = require('react-app-alias') 12 | ``` 13 | 14 | * Rename function `alias` to `aliasWebpack` 15 | 16 | ```diff 17 | - const {alias} = require('react-app-alias') 18 | + const {aliasWebpack} = require('react-app-alias') 19 | ``` 20 | 21 | If dangerous mode is not used, scroll dow through next section to continue. 22 | 23 | ### Dangerous mode 24 | 25 | Dangerous mode is renamed to extended/extremal and the implementation 26 | is moved to separated liblrary with suffix **`-ex`**. 27 | 28 | * Replace library name for dangerous mode, 29 | * Remove `/lib/aliasDangerous` path part from import, 30 | * Notice that library name is different (suffix `-ex` added), 31 | 32 | ```diff 33 | - const {...} = require('react-app-rewired-alias/lib/aliasDangerous') 34 | + const {...} = require('react-app-alias-ex') 35 | ``` 36 | 37 | * For dangerous mode function `aliasDangerous` for `react-app-rewired` or `customize-cra` is renamed to `aliasWebpack` (also same as function `alias` for generic mode). 38 | 39 | * Rename function `alias` to `aliasWebpack` 40 | 41 | ```diff 42 | - const {aliasDangerous} = require('react-app-alias-ex') 43 | + const {aliasWebpack} = require('react-app-alias-ex') 44 | ``` 45 | 46 | ### Options 47 | 48 | Before the agrument of `aliasWebpack()` and `aliasJest()` was alias map. 49 | Now the argument of these functions is options. 50 | Alias map now is `alias` option. So replace: 51 | 52 | ```diff 53 | const aliasMap = configPaths("./tsconfig.paths.json") 54 | // or possible alias map created in code 55 | const aliasMap = { 56 | '@app': 'src/app', 57 | '@lib': 'lib', 58 | } 59 | 60 | - module.exports = aliasWebpack(aliasMap) 61 | - module.exports.jest = aliasJest(aliasMap) 62 | + module.exports = aliasWebpack({alias: aliasMap}) 63 | + module.exports.jest = aliasJest({alias: aliasMap}) 64 | ``` 65 | 66 | Fucntion `configPaths()` now become needed in rare cases. It is used internally. 67 | Now you can specify path of *tsconfig.json* or *jsconfig.json* as an option. 68 | 69 | ```diff 70 | - const aliasMap = configPaths("./tsconfig.paths.json") 71 | - module.exports = aliasWebpack({alias: aliasMap}) 72 | + module.exports = aliasWebpack({tsconfig: "./tsconfig.paths.json"}) 73 | ``` 74 | 75 | If you uses standart name of these files you can skip it. It will be opned automatically. 76 | 77 | ```diff 78 | - const aliasMap = configPaths("./jsconfig.paths.json") 79 | - module.exports = aliasWebpack({jsconfig: "./jsconfig.paths.json"}) 80 | + module.exports = aliasWebpack({}) 81 | ``` 82 | 83 | -------------------------------------------------------------------------------- /example/above-root-js/src/AboveRootJs.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function AboveRootJs() { 4 | return ( 5 |

Above root js

6 | ) 7 | } 8 | 9 | export default AboveRootJs -------------------------------------------------------------------------------- /example/above-root-ts/src/AboveRootTs.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function AboveRootTs() { 4 | return ( 5 |

Above root ts

6 | ) 7 | } 8 | 9 | export default AboveRootTs -------------------------------------------------------------------------------- /example/above/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /example/above/README.md: -------------------------------------------------------------------------------- 1 | # Demo 2 | 3 | This example demonstrate dangerous `alias` implementation (with `above root` imports) 4 | 5 | *Base configuration (without `above root`) imports is in `example/main` directory* 6 | 7 | Here is example for crete-react-app versions 4.1 and above 8 | 9 | The version 4.0 of create-react-app requires patch due to 10 | [create-react-app #9921](https://github.com/facebook/create-react-app/pull/9921) 11 | The example with patch available [here](https://github.com/oklas/react-app-rewire-alias/tree/055bd2fd79dd391b2b79be15dcaa2a78b7779e8e/example/main) use yarn (yarn.lock) 12 | 13 | -------------------------------------------------------------------------------- /example/above/craco.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const { CracoAliasPlugin } = require('react-app-alias-ex'); 3 | 4 | module.exports = { 5 | plugins: [ 6 | { 7 | plugin: CracoAliasPlugin, 8 | options: { 9 | }, 10 | }, 11 | ], 12 | }; 13 | -------------------------------------------------------------------------------- /example/above/near-src/src/NearSrc.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function NearSrc() { 4 | return ( 5 |

Near Src

6 | ) 7 | } 8 | 9 | export default NearSrc -------------------------------------------------------------------------------- /example/above/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "above", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@craco/craco": "^6.4.3", 7 | "@testing-library/jest-dom": "^5.15.0", 8 | "@testing-library/react": "^12.1.0", 9 | "@testing-library/user-event": "^13.5.0", 10 | "@types/jest": "^27.0.1", 11 | "@types/node": "^16.11.7", 12 | "@types/react": "^17.0.34", 13 | "@types/react-dom": "^17.0.11", 14 | "react": "^17.0.2", 15 | "react-app-alias-ex": "^0.0.0", 16 | "react-dom": "^17.0.2", 17 | "react-scripts": "4.0.3", 18 | "typescript": "4.4.4", 19 | "web-vitals": "^2.1.0" 20 | }, 21 | "scripts": { 22 | "start": "craco start", 23 | "build": "craco build", 24 | "test": "craco test" 25 | }, 26 | "eslintConfig": { 27 | "extends": [ 28 | "react-app", 29 | "react-app/jest" 30 | ] 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /example/above/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oklas/react-app-alias/653ebaceec766da6c0f8f988ec894896bc6cca17/example/above/public/favicon.ico -------------------------------------------------------------------------------- /example/above/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /example/above/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oklas/react-app-alias/653ebaceec766da6c0f8f988ec894896bc6cca17/example/above/public/logo192.png -------------------------------------------------------------------------------- /example/above/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oklas/react-app-alias/653ebaceec766da6c0f8f988ec894896bc6cca17/example/above/public/logo512.png -------------------------------------------------------------------------------- /example/above/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /example/above/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /example/above/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /example/above/src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders text', () => { 6 | render(); 7 | const linkElement = screen.getByText(/Main/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | 11 | test('renders internal component from src', () => { 12 | render(); 13 | const nearElement = screen.getByText(/Internal/i); 14 | expect(nearElement).toBeInTheDocument(); 15 | }); 16 | 17 | test('renders component from near src', () => { 18 | render(); 19 | const nearElement = screen.getByText(/Near Src/i); 20 | expect(nearElement).toBeInTheDocument(); 21 | }); 22 | 23 | test('renders JS component from above root', () => { 24 | render(); 25 | const aboveElement = screen.getByText(/Above root js/i); 26 | expect(aboveElement).toBeInTheDocument(); 27 | }); 28 | 29 | test('renders TS component from above root', () => { 30 | render(); 31 | const aboveElement = screen.getByText(/Above root ts/i); 32 | expect(aboveElement).toBeInTheDocument(); 33 | }); -------------------------------------------------------------------------------- /example/above/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import AboveRootJs from 'above-root-js/AboveRootJs' 3 | import AboveRootTs from 'above-root-ts/AboveRootTs' 4 | import NearSrc from 'near-src/NearSrc' 5 | import Internal from 'InternalEx/index' 6 | import './App.css' 7 | 8 | function App() { 9 | return ( 10 |
11 |

Main

12 | 13 | 14 | 15 | 16 |
17 | ); 18 | } 19 | 20 | export default App; 21 | -------------------------------------------------------------------------------- /example/above/src/InternalEx/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function Internal() { 4 | return ( 5 |

Internal

6 | ) 7 | } 8 | 9 | export default Internal -------------------------------------------------------------------------------- /example/above/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /example/above/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /example/above/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/above/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /example/above/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | } 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /example/above/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /example/above/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "extends": "./tsconfig.paths.json", 24 | "include": [ 25 | "src" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /example/above/tsconfig.paths.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "above-root-ts/*": [ "../above-root-ts/src/*" ], 6 | "above-root-js/*": [ "../above-root-js/src/*" ], 7 | "near-src/*": [ "near-src/src/*" ], 8 | "InternalEx/*": [ "src/InternalEx/*" ] 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /example/below/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /example/below/README.md: -------------------------------------------------------------------------------- 1 | # Demo 2 | 3 | This example demonstrate base `alias` implementation (without `above root` imports) 4 | 5 | *Configuration (with `above root`) imports is in `example/above` directory* 6 | 7 | Here is example for crete-react-app versions 4.1 and above 8 | 9 | The version 4.0 of create-react-app requires patch due to 10 | [create-react-app #9921](https://github.com/facebook/create-react-app/pull/9921) 11 | The example with patch available [here](https://github.com/oklas/react-app-rewire-alias/tree/055bd2fd79dd391b2b79be15dcaa2a78b7779e8e/example/main) use yarn (yarn.lock) 12 | 13 | -------------------------------------------------------------------------------- /example/below/config-overrides.js: -------------------------------------------------------------------------------- 1 | const {aliasWebpack, aliasJest} = require('react-app-alias') 2 | 3 | const options = { 4 | } 5 | 6 | module.exports = aliasWebpack(options) 7 | module.exports.jest = aliasJest(options) -------------------------------------------------------------------------------- /example/below/craco.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const { CracoAliasPlugin } = require('react-app-alias'); 3 | 4 | module.exports = { 5 | plugins: [ 6 | { 7 | plugin: CracoAliasPlugin, 8 | options: { 9 | }, 10 | }, 11 | ], 12 | }; 13 | -------------------------------------------------------------------------------- /example/below/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "below", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@craco/craco": "^6.4.3", 7 | "@testing-library/jest-dom": "^5.15.0", 8 | "@testing-library/react": "^12.1.0", 9 | "@testing-library/user-event": "^13.5.0", 10 | "@types/jest": "^27.0.1", 11 | "@types/node": "^16.11.7", 12 | "@types/react": "^17.0.34", 13 | "@types/react-dom": "^17.0.11", 14 | "react": "^17.0.2", 15 | "react-app-alias": "^0.0.0", 16 | "react-dom": "^17.0.2", 17 | "react-scripts": "4.0.3", 18 | "typescript": "4.4.4", 19 | "web-vitals": "^2.1.0" 20 | }, 21 | "scripts": { 22 | "start": "craco start", 23 | "build": "craco build", 24 | "test": "craco test" 25 | }, 26 | "eslintConfig": { 27 | "extends": [ 28 | "react-app", 29 | "react-app/jest" 30 | ] 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /example/below/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oklas/react-app-alias/653ebaceec766da6c0f8f988ec894896bc6cca17/example/below/public/favicon.ico -------------------------------------------------------------------------------- /example/below/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /example/below/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oklas/react-app-alias/653ebaceec766da6c0f8f988ec894896bc6cca17/example/below/public/logo192.png -------------------------------------------------------------------------------- /example/below/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oklas/react-app-alias/653ebaceec766da6c0f8f988ec894896bc6cca17/example/below/public/logo512.png -------------------------------------------------------------------------------- /example/below/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /example/below/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /example/below/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /example/below/src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders text', () => { 6 | render(); 7 | const linkElement = screen.getByText(/Main/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | 11 | test('renders internal component from src', () => { 12 | render(); 13 | const nearElement = screen.getByText(/Internal/i); 14 | expect(nearElement).toBeInTheDocument(); 15 | }); -------------------------------------------------------------------------------- /example/below/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Internal from 'Internal/index' 3 | import './App.css' 4 | 5 | function App() { 6 | return ( 7 |
8 |

Main

9 | 10 |
11 | ); 12 | } 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /example/below/src/Internal/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function Internal() { 4 | return ( 5 |

Internal

6 | ) 7 | } 8 | 9 | export default Internal -------------------------------------------------------------------------------- /example/below/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /example/below/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /example/below/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/below/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /example/below/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | } 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /example/below/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /example/below/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "src", 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "allowJs": true, 11 | "skipLibCheck": true, 12 | "esModuleInterop": true, 13 | "allowSyntheticDefaultImports": true, 14 | "strict": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "noFallthroughCasesInSwitch": true, 17 | "module": "esnext", 18 | "moduleResolution": "node", 19 | "resolveJsonModule": true, 20 | "isolatedModules": true, 21 | "noEmit": true, 22 | "jsx": "react-jsx" 23 | }, 24 | "extends": "./tsconfig.paths.json", 25 | "include": [ 26 | "src" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /example/below/tsconfig.paths.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | } 5 | } 6 | } -------------------------------------------------------------------------------- /example/main/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /example/main/README.md: -------------------------------------------------------------------------------- 1 | # Demo 2 | 3 | This example demonstrate base `alias` implementation (without `above root` imports) 4 | 5 | *Configuration (with `above root`) imports is in `example/above` directory* 6 | 7 | Here is example for crete-react-app versions 4.1 and above 8 | 9 | The version 4.0 of create-react-app requires patch due to 10 | [create-react-app #9921](https://github.com/facebook/create-react-app/pull/9921) 11 | The example with patch available [here](https://github.com/oklas/react-app-rewire-alias/tree/055bd2fd79dd391b2b79be15dcaa2a78b7779e8e/example/main) use yarn (yarn.lock) 12 | 13 | -------------------------------------------------------------------------------- /example/main/config-overrides.js: -------------------------------------------------------------------------------- 1 | const {aliasWebpack, aliasJest} = require('react-app-alias') 2 | 3 | const options = { 4 | autoscan: 'src', 5 | } 6 | 7 | module.exports = aliasWebpack(options) 8 | module.exports.jest = aliasJest(options) -------------------------------------------------------------------------------- /example/main/craco.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const { CracoAliasPlugin } = require('react-app-alias'); 3 | 4 | module.exports = { 5 | plugins: [ 6 | { 7 | plugin: CracoAliasPlugin, 8 | options: { 9 | }, 10 | }, 11 | ], 12 | }; 13 | -------------------------------------------------------------------------------- /example/main/near-src/src/NearSrc.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function NearSrc() { 4 | return ( 5 |

Near Src

6 | ) 7 | } 8 | 9 | export default NearSrc -------------------------------------------------------------------------------- /example/main/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "main", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@craco/craco": "^6.4.3", 7 | "@testing-library/jest-dom": "^5.15.0", 8 | "@testing-library/react": "^12.1.0", 9 | "@testing-library/user-event": "^13.5.0", 10 | "@types/jest": "^27.0.1", 11 | "@types/node": "^16.11.7", 12 | "@types/react": "^17.0.34", 13 | "@types/react-dom": "^17.0.11", 14 | "react": "^17.0.2", 15 | "react-app-alias": "^0.0.0", 16 | "react-dom": "^17.0.2", 17 | "react-scripts": "4.0.3", 18 | "typescript": "4.4.4", 19 | "web-vitals": "^2.1.0" 20 | }, 21 | "scripts": { 22 | "start": "craco start", 23 | "build": "craco build", 24 | "test": "craco test" 25 | }, 26 | "eslintConfig": { 27 | "extends": [ 28 | "react-app", 29 | "react-app/jest" 30 | ] 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /example/main/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oklas/react-app-alias/653ebaceec766da6c0f8f988ec894896bc6cca17/example/main/public/favicon.ico -------------------------------------------------------------------------------- /example/main/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /example/main/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oklas/react-app-alias/653ebaceec766da6c0f8f988ec894896bc6cca17/example/main/public/logo192.png -------------------------------------------------------------------------------- /example/main/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oklas/react-app-alias/653ebaceec766da6c0f8f988ec894896bc6cca17/example/main/public/logo512.png -------------------------------------------------------------------------------- /example/main/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /example/main/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /example/main/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /example/main/src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders text', () => { 6 | render(); 7 | const linkElement = screen.getByText(/Main/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | 11 | test('renders internal component from src', () => { 12 | render(); 13 | const nearElement = screen.getByText(/Internal/i); 14 | expect(nearElement).toBeInTheDocument(); 15 | }); 16 | 17 | test('renders component from near src', () => { 18 | render(); 19 | const nearElement = screen.getByText(/Near src/i); 20 | expect(nearElement).toBeInTheDocument(); 21 | }); 22 | -------------------------------------------------------------------------------- /example/main/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import NearSrc from 'near-src/NearSrc' 3 | import Internal from 'Internal/index' 4 | import './App.css' 5 | 6 | function App() { 7 | return ( 8 |
9 |

Main

10 | 11 | 12 |
13 | ); 14 | } 15 | 16 | export default App; 17 | -------------------------------------------------------------------------------- /example/main/src/Internal/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function Internal() { 4 | return ( 5 |

Internal

6 | ) 7 | } 8 | 9 | export default Internal -------------------------------------------------------------------------------- /example/main/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /example/main/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /example/main/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/main/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /example/main/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | } 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /example/main/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /example/main/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "allowJs": true, 11 | "skipLibCheck": true, 12 | "esModuleInterop": true, 13 | "allowSyntheticDefaultImports": true, 14 | "strict": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "noFallthroughCasesInSwitch": true, 17 | "module": "esnext", 18 | "moduleResolution": "node", 19 | "resolveJsonModule": true, 20 | "isolatedModules": true, 21 | "noEmit": true, 22 | "jsx": "react-jsx" 23 | }, 24 | "extends": "./tsconfig.paths.json", 25 | "include": [ 26 | "src" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /example/main/tsconfig.paths.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "Internal/*": [ "src/Internal/*" ], 6 | "near-src/*": [ "near-src/src/*" ] 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * For a detailed explanation regarding each configuration property, visit: 3 | * https://jestjs.io/docs/configuration 4 | */ 5 | 6 | module.exports = { 7 | // All imported modules in your tests should be mocked automatically 8 | // automock: false, 9 | 10 | // Stop running tests after `n` failures 11 | // bail: 0, 12 | 13 | // The directory where Jest should store its cached dependency information 14 | // cacheDirectory: "/private/var/folders/h4/61fjtl3n1x9by4n16k6vtzn40000gn/T/jest_dx", 15 | 16 | // Automatically clear mock calls, instances and results before every test 17 | // clearMocks: false, 18 | 19 | // Indicates whether the coverage information should be collected while executing the test 20 | collectCoverage: true, 21 | 22 | // An array of glob patterns indicating a set of files for which coverage information should be collected 23 | // collectCoverageFrom: undefined, 24 | 25 | // The directory where Jest should output its coverage files 26 | coverageDirectory: "coverage", 27 | 28 | // An array of regexp pattern strings used to skip coverage collection 29 | // coveragePathIgnorePatterns: [ 30 | // "/node_modules/" 31 | // ], 32 | 33 | // Indicates which provider should be used to instrument code for coverage 34 | coverageProvider: "v8", 35 | 36 | // A list of reporter names that Jest uses when writing coverage reports 37 | // coverageReporters: [ 38 | // "json", 39 | // "text", 40 | // "lcov", 41 | // "clover" 42 | // ], 43 | 44 | // An object that configures minimum threshold enforcement for coverage results 45 | // coverageThreshold: undefined, 46 | 47 | // A path to a custom dependency extractor 48 | // dependencyExtractor: undefined, 49 | 50 | // Make calling deprecated APIs throw helpful error messages 51 | // errorOnDeprecated: false, 52 | 53 | // Force coverage collection from ignored files using an array of glob patterns 54 | // forceCoverageMatch: [], 55 | 56 | // A path to a module which exports an async function that is triggered once before all test suites 57 | // globalSetup: undefined, 58 | 59 | // A path to a module which exports an async function that is triggered once after all test suites 60 | // globalTeardown: undefined, 61 | 62 | // A set of global variables that need to be available in all test environments 63 | // globals: {}, 64 | 65 | // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. 66 | // maxWorkers: "50%", 67 | 68 | // An array of directory names to be searched recursively up from the requiring module's location 69 | // moduleDirectories: [ 70 | // "node_modules" 71 | // ], 72 | 73 | // An array of file extensions your modules use 74 | // moduleFileExtensions: [ 75 | // "js", 76 | // "jsx", 77 | // "ts", 78 | // "tsx", 79 | // "json", 80 | // "node" 81 | // ], 82 | 83 | // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module 84 | // moduleNameMapper: {}, 85 | 86 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader 87 | // modulePathIgnorePatterns: [], 88 | 89 | // Activates notifications for test results 90 | // notify: false, 91 | 92 | // An enum that specifies notification mode. Requires { notify: true } 93 | // notifyMode: "failure-change", 94 | 95 | // A preset that is used as a base for Jest's configuration 96 | // preset: undefined, 97 | 98 | // Run tests from one or more projects 99 | // projects: undefined, 100 | 101 | // Use this configuration option to add custom reporters to Jest 102 | // reporters: undefined, 103 | 104 | // Automatically reset mock state before every test 105 | // resetMocks: false, 106 | 107 | // Reset the module registry before running each individual test 108 | // resetModules: false, 109 | 110 | // A path to a custom resolver 111 | // resolver: undefined, 112 | 113 | // Automatically restore mock state and implementation before every test 114 | // restoreMocks: false, 115 | 116 | // The root directory that Jest should scan for tests and modules within 117 | // rootDir: undefined, 118 | 119 | // A list of paths to directories that Jest should use to search for files in 120 | roots: [ 121 | "packages" 122 | ], 123 | 124 | // Allows you to use a custom runner instead of Jest's default test runner 125 | // runner: "jest-runner", 126 | 127 | // The paths to modules that run some code to configure or set up the testing environment before each test 128 | // setupFiles: [], 129 | 130 | // A list of paths to modules that run some code to configure or set up the testing framework before each test 131 | // setupFilesAfterEnv: [], 132 | 133 | // The number of seconds after which a test is considered as slow and reported as such in the results. 134 | // slowTestThreshold: 5, 135 | 136 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing 137 | // snapshotSerializers: [], 138 | 139 | // The test environment that will be used for testing 140 | // testEnvironment: "jest-environment-node", 141 | 142 | // Options that will be passed to the testEnvironment 143 | // testEnvironmentOptions: {}, 144 | 145 | // Adds a location field to test results 146 | // testLocationInResults: false, 147 | 148 | // The glob patterns Jest uses to detect test files 149 | // testMatch: [ 150 | // "**/__tests__/**/*.[jt]s?(x)", 151 | // "**/?(*.)+(spec|test).[tj]s?(x)" 152 | // ], 153 | 154 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 155 | // testPathIgnorePatterns: [ 156 | // "/node_modules/" 157 | // ], 158 | 159 | // The regexp pattern or array of patterns that Jest uses to detect test files 160 | // testRegex: [], 161 | 162 | // This option allows the use of a custom results processor 163 | // testResultsProcessor: undefined, 164 | 165 | // This option allows use of a custom test runner 166 | // testRunner: "jest-circus/runner", 167 | 168 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href 169 | // testURL: "http://localhost", 170 | 171 | // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" 172 | // timers: "real", 173 | 174 | // A map from regular expressions to paths to transformers 175 | // transform: undefined, 176 | 177 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 178 | // transformIgnorePatterns: [ 179 | // "/node_modules/", 180 | // "\\.pnp\\.[^\\/]+$" 181 | // ], 182 | 183 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them 184 | // unmockedModulePathPatterns: undefined, 185 | 186 | // Indicates whether each individual test should be reported during the run 187 | // verbose: undefined, 188 | 189 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode 190 | // watchPathIgnorePatterns: [], 191 | 192 | // Whether to use watchman for file crawling 193 | // watchman: true, 194 | }; 195 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*", 4 | "example/*" 5 | ], 6 | "version": "independent", 7 | "npmClient": "yarn" 8 | } 9 | -------------------------------------------------------------------------------- /lib-for-support/aliasDangerous.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../src/aliasDangerous') -------------------------------------------------------------------------------- /lib-for-support/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../src/index') -------------------------------------------------------------------------------- /package-for-support.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-app-rewire-alias", 3 | "version": "1.1.7", 4 | "description": "Alias for craco or rewired create-react-app", 5 | "main": "src/index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/oklas/react-app-rewire-alias" 9 | }, 10 | "keywords": [ 11 | "create-react-app", 12 | "alias", 13 | "rewire", 14 | "craco", 15 | "cra", 16 | "aliases", 17 | "webpack", 18 | "jest", 19 | "outside of src", 20 | "imports restriction", 21 | "monorepo", 22 | "multirepo", 23 | "react-app-rewire" 24 | ], 25 | "author": "Serguei Okladnikov (https://github.com/oklas)", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/oklas/react-app-rewire-alias/issues" 29 | }, 30 | "homepage": "https://github.com/oklas/react-app-rewire-alias", 31 | "devDependencies": { 32 | }, 33 | "peerDependencies": { 34 | "react-app-rewired": "1 || ^2" 35 | }, 36 | "dependencies": { 37 | }, 38 | "scripts": { 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-app-alias", 3 | "private": true, 4 | "devDependencies": { 5 | "lerna": "^4.0.0", 6 | "react-scripts": "^4.0.3", 7 | "semantic-release": "^19.0.2", 8 | "semantic-release-monorepo": "^7.0.5" 9 | }, 10 | "scripts": { 11 | "example:above:start": "cd example/above && yarn start", 12 | "example:main:start": "cd example/main && yarn start", 13 | "example:below:start": "cd example/below && yarn start", 14 | "example:above:test": "cd example/above && yarn test --watchAll=false", 15 | "example:main:test": "cd example/main && yarn test --watchAll=false", 16 | "example:below:test": "cd example/below && yarn test --watchAll=false", 17 | "example:install": "lerna buutstrap", 18 | "example:test": "yarn example:below:test && yarn example:main:test && yarn example:above:test", 19 | "coverage": "jest --coverage", 20 | "test:unit": "jest", 21 | "test:integration": "yarn example:test", 22 | "test": "yarn test:unit && yarn test:integration", 23 | "release": "lerna exec --concurrency 1 --scope 'react-app-alias*' -- npx --no-install semantic-release -e semantic-release-monorepo" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/react-app-alias-ex/README.md: -------------------------------------------------------------------------------- 1 | # `react-app-alias-ex` 2 | 3 | ## Usage 4 | 5 | [docs](http://github.com/oklas/react-app-alias) -------------------------------------------------------------------------------- /packages/react-app-alias-ex/__tests__/react-app-alias-ex.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const reactAppAliasEx = require('../src'); 4 | 5 | describe('react-app-alias-ex', () => { 6 | test.todo('tested by tests in projects in example folder'); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/react-app-alias-ex/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-app-alias-ex", 3 | "version": "0.0.0", 4 | "description": "Alias for craco or rewire react app", 5 | "keywords": [ 6 | "create-react-app", 7 | "alias", 8 | "rewire", 9 | "craco", 10 | "cra", 11 | "aliases", 12 | "webpack", 13 | "jest", 14 | "outside of src", 15 | "imports restriction", 16 | "monorepo", 17 | "multirepo", 18 | "react-app-rewire" 19 | ], 20 | "author": "Serguei Okladnikov ", 21 | "homepage": "https://github.com/oklas/react-app-alias", 22 | "license": "MIT", 23 | "main": "src/index.js", 24 | "directories": { 25 | "src": "src", 26 | "test": "__tests__" 27 | }, 28 | "files": [ 29 | "src" 30 | ], 31 | "repository": { 32 | "type": "git", 33 | "url": "git+https://github.com/oklas/react-app-alias.git" 34 | }, 35 | "scripts": { 36 | "test": "echo \"Error: run tests from root\" && exit 1" 37 | }, 38 | "bugs": { 39 | "url": "https://github.com/oklas/react-app-alias/issues" 40 | }, 41 | "dependencies": { 42 | "react-app-alias": "^2.0.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/react-app-alias-ex/src/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const paths = require('react-scripts/config/paths') 3 | const { 4 | aliasJest: aliasJestSafe, 5 | configFilePathSafe, 6 | readConfig, 7 | configPathsRaw, 8 | configPaths, 9 | defaultOptions, 10 | expandResolveAlias, 11 | expandPluginsScope, 12 | } = require('react-app-alias') 13 | 14 | function requireEslintLoaderModule() { 15 | try { 16 | return require.resolve('eslint-loader') 17 | } catch(e) {} 18 | return undefined 19 | } 20 | 21 | const eslintLoaderModule = requireEslintLoaderModule() 22 | 23 | function isRuleOfEslint(rule) { 24 | if(eslintLoaderModule && eslintLoaderModule === rule.loader) return true 25 | return rule.use && 0 < rule.use.filter(isRuleOfEslint).length 26 | } 27 | 28 | function isLocalPath(parent) { 29 | return function(dir) { 30 | const relative = path.relative(parent, dir) 31 | return relative && !relative.startsWith('..') && !path.isAbsolute(relative) 32 | } 33 | } 34 | 35 | function updateInclude(rule, include) { 36 | rule.include = ( 37 | isRuleOfEslint(rule) ? include.filter(isLocalPath(paths.appSrc)) : include 38 | ).concat(rule.include) 39 | } 40 | 41 | function expandRulesInclude(rules, include) { 42 | rules.forEach(function(rule) { 43 | if(rule.include === paths.appSrc) { 44 | updateInclude(rule, include) 45 | } 46 | if(rule.oneOf) 47 | expandRulesInclude(rule.oneOf, include) 48 | }) 49 | } 50 | 51 | function packageList() { 52 | const packagePath = path.resolve(paths.appPath, 'package.json') 53 | const pack = require(packagePath) 54 | const depsSections = [ 55 | ...Object.keys(pack).filter(s => s.match(/.*Dependencies/)), 56 | 'dependencies', 57 | ] 58 | const list = [].concat(...depsSections.map(s => Object.keys(pack[s]))) 59 | return list 60 | } 61 | 62 | function packagePathsRaw() { 63 | const packages = packageList(); 64 | const pathList = packages.reduce((a,i) => ( 65 | { 66 | ...a, 67 | [i]: [ 68 | path.resolve(paths.appPath, 'node_modules', i), 69 | path.resolve(paths.appPath, 'node_modules', '@types', i), 70 | ], 71 | [`${i}/*`]: [ 72 | path.resolve(paths.appPath, 'node_modules', i) + '/*', 73 | path.resolve(paths.appPath, 'node_modules', '@types', i) + "/*", 74 | ], 75 | } 76 | ), {}) 77 | return pathList 78 | } 79 | 80 | function expandPluginsTsChecker(plugins, configPath) { 81 | const pluginName = 'ForkTsCheckerWebpackPlugin' 82 | const confPath = configFilePathSafe(configPath) 83 | const conf = readConfig(confPath) 84 | const tsjsPaths = configPathsRaw(conf) 85 | const packagePaths = packagePathsRaw(confPath) 86 | const pluginPos = plugins 87 | .map(x => x.constructor.name) 88 | .indexOf(pluginName) 89 | if(pluginPos !== -1) { 90 | const opts = plugins[pluginPos].options 91 | const Consructor = plugins[pluginPos].constructor 92 | const paths = { 93 | ...packagePaths, 94 | ...((opts.compilerOptions || {}).paths || {}), 95 | ...tsjsPaths, 96 | } 97 | const compilerOptions = { 98 | ...(opts.compilerOptions || {}), 99 | paths, 100 | } 101 | // https://github.com/oklas/react-app-alias/issues/44 102 | const typescriptIsString = String(opts.typescript) === opts.typescript 103 | const options = typescriptIsString ? { 104 | ...opts, compilerOptions 105 | } : { 106 | ...opts, 107 | typescript: { 108 | ...opts.typescript, 109 | configOverwrite: compilerOptions, 110 | } 111 | } 112 | plugins[pluginPos] = new Consructor(options) 113 | } 114 | } 115 | 116 | function aliasJest(options) { 117 | const aliasMap = defaultOptions(options).aliasMap 118 | const aliasJestInstance = aliasJestSafe(aliasMap) 119 | return function(config) { 120 | const expanded = aliasJestInstance(config) 121 | return { 122 | ...expanded, 123 | moduleDirectories: [ 124 | ...(config.moduleDirectories || ['node_modules']), 125 | path.resolve(paths.appPath, 'node_modules') 126 | ], 127 | } 128 | } 129 | } 130 | 131 | function aliasWebpack(options) { 132 | const aliasMap = defaultOptions(options).aliasMap 133 | const aliasLocal = Object.keys(aliasMap).reduce( (a,i) => { 134 | a[i] = path.resolve(paths.appPath, aliasMap[i]) 135 | return a 136 | }, {}) 137 | return function(config) { 138 | expandResolveAlias(config.resolve, aliasLocal) 139 | expandRulesInclude(config.module.rules, Object.values(aliasLocal)) 140 | expandPluginsScope(config.resolve.plugins, Object.values(aliasLocal), Object.values(aliasLocal)) 141 | expandPluginsTsChecker(config.plugins) 142 | return config 143 | } 144 | } 145 | 146 | const CracoAliasPlugin = { 147 | overrideWebpackConfig: function({webpackConfig, pluginOptions}) { 148 | return aliasWebpack(pluginOptions)(webpackConfig) 149 | }, 150 | overrideJestConfig: function({jestConfig, pluginOptions}) { 151 | return aliasJest(pluginOptions)(jestConfig) 152 | } 153 | } 154 | 155 | module.exports = { 156 | aliasWebpack, 157 | aliasJest, 158 | configPaths, 159 | expandResolveAlias, 160 | expandRulesInclude, 161 | expandPluginsScope, 162 | CracoAliasPlugin, 163 | } 164 | -------------------------------------------------------------------------------- /packages/react-app-alias/README.md: -------------------------------------------------------------------------------- 1 | # `react-app-alias` 2 | 3 | ## Usage 4 | 5 | [docs](http://github.com/oklas/react-app-alias) -------------------------------------------------------------------------------- /packages/react-app-alias/__tests__/59-tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "alias/*": [ "target/*" ] 6 | } 7 | }, 8 | "extends": "./59-tsconfig.paths.json" 9 | } -------------------------------------------------------------------------------- /packages/react-app-alias/__tests__/59-tsconfig.paths.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "alias-paths/*": [ "target-paths/*" ] 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /packages/react-app-alias/__tests__/react-app-alias.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const path = require('path') 3 | const {configPathsRaw, readConfig} = require('../src'); 4 | 5 | describe('react-app-alias', () => { 6 | test.todo('tested by tests in projects in example folder'); 7 | }); 8 | 9 | describe('extends section of tsconfig on detect config file stage', () => { 10 | test('read both file and extends', () => { 11 | const conf = readConfig(path.resolve(__dirname, './59-tsconfig.json')) 12 | const paths = configPathsRaw(conf) 13 | expect(paths['alias/*'][0]).toBe('target/*'); 14 | expect(paths['alias-paths/*'][0]).toBe('target-paths/*'); 15 | }); 16 | }); 17 | 18 | -------------------------------------------------------------------------------- /packages/react-app-alias/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-app-alias", 3 | "version": "0.0.0", 4 | "description": "Alias for craco or rewire react app", 5 | "keywords": [ 6 | "create-react-app", 7 | "alias", 8 | "rewire", 9 | "craco", 10 | "cra", 11 | "aliases", 12 | "webpack", 13 | "jest", 14 | "outside of src", 15 | "imports restriction", 16 | "monorepo", 17 | "multirepo", 18 | "react-app-rewire" 19 | ], 20 | "author": "Serguei Okladnikov ", 21 | "homepage": "https://github.com/oklas/react-app-alias", 22 | "license": "MIT", 23 | "main": "src/index.js", 24 | "directories": { 25 | "src": "src", 26 | "test": "__tests__" 27 | }, 28 | "files": [ 29 | "src" 30 | ], 31 | "repository": { 32 | "type": "git", 33 | "url": "git+https://github.com/oklas/react-app-alias.git" 34 | }, 35 | "scripts": { 36 | "test": "echo \"Error: run tests from root\" && exit 1" 37 | }, 38 | "bugs": { 39 | "url": "https://github.com/oklas/react-app-alias/issues" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/react-app-alias/src/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin') 4 | const paths = require('react-scripts/config/paths') 5 | 6 | const guessConfigFiles = [ 7 | 'tsconfig.json', 8 | 'tsconfig.paths.json', 9 | 'jsconfig.json', 10 | 'jsconfig.paths.json', 11 | ] 12 | 13 | function expandResolveAlias(resolve, alias) { 14 | resolve.alias = Object.assign(resolve.alias || {}, alias) 15 | } 16 | 17 | function expandRulesInclude(rules, include) { 18 | rules.forEach(function(rule) { 19 | if(rule.include === paths.appSrc) 20 | rule.include = include.concat(rule.include) 21 | if(rule.oneOf) 22 | expandRulesInclude(rule.oneOf, include) 23 | }) 24 | } 25 | 26 | function expandPluginsScope(plugins, dirs, files) { 27 | dirs = [ 28 | paths.appSrc, 29 | ...dirs.filter( 30 | dir => !fs.existsSync(dir) || fs.lstatSync(dir).isDirectory() 31 | ) 32 | ] 33 | files = [ 34 | paths.appPackageJson, 35 | ...dirs.filter(dir => fs.existsSync(dir) && fs.lstatSync(dir).isFile()), 36 | ...(files || []), 37 | ] 38 | const pluginName = 'ModuleScopePlugin' 39 | const pluginPos = plugins 40 | .map(x => x.constructor.name) 41 | .indexOf(pluginName) 42 | if(pluginPos !== -1) { 43 | const oldModuleScopePlugin = plugins[pluginPos] 44 | const allowedFiles = [...oldModuleScopePlugin.allowedFiles].filter(f => f !== paths.appPackageJson) 45 | files.push(...allowedFiles) 46 | plugins[pluginPos] = new ModuleScopePlugin(dirs, files) 47 | } 48 | } 49 | 50 | function isOutsideOfRoot(targ) { 51 | const rel = path.relative(paths.appPath, targ) 52 | return rel.startsWith('..') || path.isAbsolute(rel) 53 | } 54 | 55 | function checkOutside(aliasMap) { 56 | const outside = Object.keys(aliasMap).reduce( (a, i) => { 57 | if(isOutsideOfRoot(aliasMap[i])) { 58 | console.error( 59 | `alias '${i}' is outside of root - supported only by react-app-alias-ex` 60 | ) 61 | return true 62 | } 63 | return a 64 | }, false) 65 | if(outside) { 66 | console.error( 67 | `https://github.com/oklas/react-app-alias#outside-of-root` 68 | ) 69 | process.exit(-1) 70 | } 71 | } 72 | 73 | function aliasWebpack(options) { 74 | const {aliasMap, baseUrl} = defaultOptions(options) 75 | checkOutside(aliasMap) 76 | const aliasLocal = Object.keys(aliasMap).reduce( (a,i) => { 77 | a[i] = path.resolve(paths.appPath, baseUrl, aliasMap[i]) 78 | return a 79 | }, {}) 80 | return function(config) { 81 | expandResolveAlias(config.resolve, aliasLocal) 82 | expandRulesInclude(config.module.rules, Object.values(aliasLocal)) 83 | expandPluginsScope(config.resolve.plugins, Object.values(aliasLocal), Object.values(aliasLocal)) 84 | return config 85 | } 86 | } 87 | 88 | function aliasJest(options) { 89 | const {baseUrl, aliasMap} = defaultOptions(options) 90 | const jestAliasMap = aliasMapForJest(baseUrl, aliasMap) 91 | return function(config) { 92 | return { 93 | ...config, 94 | moduleNameMapper: { 95 | ...config.moduleNameMapper, 96 | ...jestAliasMap, 97 | } 98 | } 99 | } 100 | } 101 | 102 | function autoscan(tasks) { 103 | const dirlist = dir => 104 | fs.readdirSync(dir).filter( 105 | file => fs.statSync(path.resolve(dir, file)).isDirectory()) 106 | if(!Array.isArray(tasks)) tasks = tasks ? [tasks] : [] 107 | tasks = tasks.map(task => (task===task+'') ? {path: task} : task) 108 | tasks = tasks.map(task => ({ 109 | prefix: '', 110 | suffix: '', 111 | ...task, 112 | })) 113 | const aliasMap = tasks.map(task => ( 114 | dirlist(task.path).reduce( 115 | (a, alias) => ({ 116 | ...a, 117 | [task.prefix + alias + task.suffix]: 118 | path.join(task.path, alias) 119 | }), 120 | {} 121 | ) 122 | )).reduce((a, map) => ({...a, ...map}), {}) 123 | return aliasMap 124 | } 125 | 126 | function configFilePathSafe(configPath = '') { 127 | if( 128 | configPath.length > 0 && fs.existsSync(path.resolve(paths.appPath, configPath)) 129 | ) return path.resolve(paths.appPath, configPath) 130 | const existsPaths = (guessConfigFiles 131 | .map(filename => path.resolve(paths.appPath, filename)) 132 | .filter(pathname => fs.existsSync(pathname)) 133 | ) 134 | return existsPaths.length ? existsPaths[0] : '' 135 | } 136 | 137 | function readConfig(confPath) { 138 | if(!confPath) 139 | throw Error('react-app-alias:readConfig: there is no [ts|js]config file found') 140 | 141 | const confdir = path.dirname(confPath) 142 | const conf = {...require(confPath)} 143 | 144 | const extUrl = conf.extends 145 | const extPath = extUrl ? path.resolve(confdir, extUrl) : '' 146 | if(extPath && !fs.existsSync(extPath)) 147 | throw Error(`react-app-alias:readConfig: '${confPath}' field 'extends' is '${extPath}' - not exists'`) 148 | conf.extends = extUrl ? require(extPath) : {} 149 | 150 | return conf 151 | } 152 | 153 | function configPathsRaw(conf) { 154 | const confPaths = conf.compilerOptions && conf.compilerOptions.paths ? 155 | conf.compilerOptions.paths : {} 156 | 157 | const ext = conf.extends || {} 158 | const extPaths = ext.compilerOptions && ext.compilerOptions.paths ? 159 | ext.compilerOptions.paths : {} 160 | 161 | if(typeof confPaths !== 'object') 162 | throw Error(`react-app-alias:configPathsRaw: compilerOptions.paths must be object`) 163 | if(typeof extPaths !== 'object') 164 | throw Error(`react-app-alias:configPathsRaw: compilerOptions.extends->compilerOptions.paths must be object`) 165 | 166 | return { 167 | ...confPaths, 168 | ...extPaths, 169 | } 170 | } 171 | 172 | function configPaths(configPath = '', confUndoc) { 173 | const confPath = configFilePathSafe(configPath) 174 | const conf = confUndoc || readConfig(confPath) 175 | const paths = configPathsRaw(conf) 176 | const aliasMap = Object.keys(paths).reduce( (a, path) => { 177 | const value = paths[path] 178 | const target = Array.isArray(value) ? value[0] : value 179 | a[path.replace(/\/\*$/,'')] = target.replace(/\/\*$/,'') 180 | return a 181 | }, {}) 182 | return aliasMap 183 | } 184 | 185 | function defaultOptions(options = {}) { 186 | const configPath = configFilePathSafe( 187 | options.tsconfig || options.jsconfig 188 | ) 189 | const conf = readConfig(configPath) 190 | 191 | const aliasMap = options.alias || configPaths(configPath, conf) 192 | const aliasAutoMap = autoscan(options.autoscan) 193 | 194 | if(options.autoscan) 195 | console.warn('react-app-alias: You are using experimental `autoscan` feature (https://github.com/oklas/react-app-alias/issues/70) it is not documented and may be it will be removed') 196 | 197 | const opts = { 198 | baseUrl: conf.compilerOptions.baseUrl || '.', 199 | ...options, 200 | aliasMap: { 201 | ...aliasAutoMap, 202 | ...aliasMap, 203 | }, 204 | } 205 | return opts 206 | } 207 | 208 | function aliasMapForJest(baseUrl, aliasMap) { 209 | return Object.keys(aliasMap).reduce( (a, i) => { 210 | const outside = isOutsideOfRoot(aliasMap[i]) 211 | const restr = i.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') 212 | const alias = `^${restr}/(.*)$` 213 | const targ = outside ? path.resolve(baseUrl, aliasMap[i])+'/$1' : `/${aliasMap[i]}/$1` 214 | return { ...a, [alias]: targ } 215 | }, {}) 216 | } 217 | 218 | const CracoAliasPlugin = { 219 | overrideWebpackConfig: function({webpackConfig, pluginOptions}) { 220 | return aliasWebpack(pluginOptions)(webpackConfig) 221 | }, 222 | overrideJestConfig: function({jestConfig, pluginOptions}) { 223 | return aliasJest(pluginOptions)(jestConfig) 224 | } 225 | } 226 | 227 | module.exports = { 228 | aliasWebpack, 229 | aliasJest, 230 | autoscan, 231 | configFilePathSafe, 232 | readConfig, 233 | configPathsRaw, 234 | configPaths, 235 | defaultOptions, 236 | expandResolveAlias, 237 | expandRulesInclude, 238 | expandPluginsScope, 239 | CracoAliasPlugin, 240 | } 241 | -------------------------------------------------------------------------------- /src-for-support/aliasDangerous.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const paths = require('react-scripts/config/paths') 3 | const { 4 | aliasJest: aliasJestSafe, 5 | configFilePath, 6 | configPathsRaw, 7 | configPaths, 8 | expandResolveAlias, 9 | expandPluginsScope, 10 | } = require('./index') 11 | 12 | function requireEslintLoaderModule() { 13 | try { 14 | return require.resolve('eslint-loader') 15 | } catch(e) {} 16 | return undefined 17 | } 18 | 19 | const eslintLoaderModule = requireEslintLoaderModule() 20 | 21 | function isRuleOfEslint(rule) { 22 | if(eslintLoaderModule && eslintLoaderModule === rule.loader) return true 23 | return rule.use && 0 < rule.use.filter(isRuleOfEslint).length 24 | } 25 | 26 | function isLocalPath(parent) { 27 | return function(dir) { 28 | const relative = path.relative(parent, dir) 29 | return relative && !relative.startsWith('..') && !path.isAbsolute(relative) 30 | } 31 | } 32 | 33 | function updateInclude(rule, include) { 34 | rule.include = ( 35 | isRuleOfEslint(rule) ? include.filter(isLocalPath(paths.appSrc)) : include 36 | ).concat(rule.include) 37 | } 38 | 39 | function expandRulesInclude(rules, include) { 40 | rules.forEach(function(rule) { 41 | if(rule.include === paths.appSrc) { 42 | updateInclude(rule, include) 43 | } 44 | if(rule.oneOf) 45 | expandRulesInclude(rule.oneOf, include) 46 | }) 47 | } 48 | 49 | function packageList() { 50 | const packagePath = path.resolve(paths.appPath, 'package.json') 51 | const package = require(packagePath) 52 | const depsSections = [ 53 | ...Object.keys(package).filter(s => s.match(/.*Dependencies/)), 54 | 'dependencies', 55 | ] 56 | const list = [].concat(...depsSections.map(s => Object.keys(package[s]))) 57 | return list 58 | } 59 | 60 | function packagePathsRaw() { 61 | const packages = packageList(); 62 | const pathList = packages.reduce((a,i) => ( 63 | { 64 | ...a, 65 | [i]: [ 66 | path.resolve(paths.appPath, 'node_modules', i), 67 | path.resolve(paths.appPath, 'node_modules', '@types', i), 68 | ], 69 | [`${i}/*`]: [ 70 | path.resolve(paths.appPath, 'node_modules', i) + '/*', 71 | path.resolve(paths.appPath, 'node_modules', '@types', i) + "/*", 72 | ], 73 | } 74 | ), {}) 75 | return pathList 76 | } 77 | 78 | function expandPluginsTsChecker(plugins, configPath) { 79 | const pluginName = 'ForkTsCheckerWebpackPlugin' 80 | const confPath = configFilePath(configPath) 81 | const tsjsPaths = configPathsRaw(confPath) 82 | const packagePaths = packagePathsRaw(confPath) 83 | const pluginPos = plugins 84 | .map(x => x.constructor.name) 85 | .indexOf(pluginName) 86 | if(pluginPos !== -1) { 87 | const opts = plugins[pluginPos].options 88 | const Consructor = plugins[pluginPos].constructor 89 | const paths = { 90 | ...packagePaths, 91 | ...((opts.compilerOptions || {}).paths || {}), 92 | ...tsjsPaths, 93 | } 94 | const compilerOptions = { 95 | ...(opts.compilerOptions || {}), 96 | paths, 97 | } 98 | const options = {...opts, compilerOptions} 99 | plugins[pluginPos] = new Consructor(options) 100 | } 101 | } 102 | 103 | function aliasJest(aliasMap) { 104 | const aliasJestInstance = aliasJestSafe(aliasMap) 105 | return function(config) { 106 | const expanded = aliasJestInstance(config) 107 | return { 108 | ...expanded, 109 | moduleDirectories: [ 110 | ...(config.moduleDirectories || []), 111 | path.resolve(paths.appPath, 'node_modules') 112 | ], 113 | } 114 | } 115 | } 116 | 117 | function aliasDangerous(aliasMap) { 118 | const aliasLocal = Object.keys(aliasMap).reduce( (a,i) => { 119 | a[i] = path.resolve(paths.appPath, aliasMap[i]) 120 | return a 121 | }, {}) 122 | return function(config) { 123 | expandResolveAlias(config.resolve, aliasLocal) 124 | expandRulesInclude(config.module.rules, Object.values(aliasLocal)) 125 | expandPluginsScope(config.resolve.plugins, Object.values(aliasLocal), Object.values(aliasLocal)) 126 | expandPluginsTsChecker(config.plugins) 127 | return config 128 | } 129 | } 130 | 131 | const CracoAliasPlugin = { 132 | overrideWebpackConfig: function({webpackConfig, pluginOptions}) { 133 | return aliasDangerous(pluginOptions.alias||configPaths())(webpackConfig) 134 | }, 135 | overrideJestConfig: function({jestConfig, pluginOptions}) { 136 | return aliasJest(pluginOptions.alias||configPaths())(jestConfig) 137 | } 138 | } 139 | 140 | module.exports = { 141 | aliasDangerous, 142 | aliasJest, 143 | configPaths, 144 | expandResolveAlias, 145 | expandRulesInclude, 146 | expandPluginsScope, 147 | CracoAliasPlugin, 148 | } 149 | -------------------------------------------------------------------------------- /src-for-support/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin') 4 | const paths = require('react-scripts/config/paths') 5 | 6 | function expandResolveAlias(resolve, alias) { 7 | resolve.alias = Object.assign(resolve.alias || {}, alias) 8 | } 9 | 10 | function expandRulesInclude(rules, include) { 11 | rules.forEach(function(rule) { 12 | if(rule.include === paths.appSrc) 13 | rule.include = include.concat(rule.include) 14 | if(rule.oneOf) 15 | expandRulesInclude(rule.oneOf, include) 16 | }) 17 | } 18 | 19 | function expandPluginsScope(plugins, dirs, files) { 20 | dirs = [ 21 | paths.appSrc, 22 | ...dirs.filter( 23 | dir => !fs.existsSync(dir) || fs.lstatSync(dir).isDirectory() 24 | ) 25 | ] 26 | files = [ 27 | paths.appPackageJson, 28 | ...dirs.filter(dir => fs.existsSync(dir) && fs.lstatSync(dir).isFile()), 29 | ...(files || []), 30 | ] 31 | const pluginName = 'ModuleScopePlugin' 32 | const pluginPos = plugins 33 | .map(x => x.constructor.name) 34 | .indexOf(pluginName) 35 | if(pluginPos !== -1) { 36 | plugins[pluginPos] = new ModuleScopePlugin(dirs, files) 37 | } 38 | } 39 | 40 | function isOutsideOfRoot(targ) { 41 | const rel = path.relative(paths.appPath, targ) 42 | return rel.startsWith('..') || path.isAbsolute(rel) 43 | } 44 | 45 | function checkOutside(aliasMap) { 46 | const outside = Object.keys(aliasMap).reduce( (a, i) => { 47 | if(isOutsideOfRoot(aliasMap[i])) { 48 | console.error( 49 | `alias '${i}' is outside of root - supported only by aliasDangerous` 50 | ) 51 | return true 52 | } 53 | return a 54 | }, false) 55 | if(outside) { 56 | console.error( 57 | `https://github.com/oklas/react-app-rewire-alias#outside-of-root` 58 | ) 59 | process.exit(-1) 60 | } 61 | } 62 | 63 | function alias(aliasMap) { 64 | checkOutside(aliasMap) 65 | const aliasLocal = Object.keys(aliasMap).reduce( (a,i) => { 66 | a[i] = path.resolve(paths.appPath, aliasMap[i]) 67 | return a 68 | }, {}) 69 | return function(config) { 70 | expandResolveAlias(config.resolve, aliasLocal) 71 | expandRulesInclude(config.module.rules, Object.values(aliasLocal)) 72 | expandPluginsScope(config.resolve.plugins, Object.values(aliasLocal), Object.values(aliasLocal)) 73 | return config 74 | } 75 | } 76 | 77 | function aliasJest(aliasMap) { 78 | const jestAliasMap = aliasMapForJest(aliasMap) 79 | return function(config) { 80 | return { 81 | ...config, 82 | moduleNameMapper: { 83 | ...config.moduleNameMapper, 84 | ...jestAliasMap, 85 | } 86 | } 87 | } 88 | } 89 | 90 | function configFilePath(configPath = '') { 91 | return ( 92 | configPath.length > 0 && fs.existsSync(path.resolve(paths.appPath, configPath)) ? 93 | path.resolve(paths.appPath, configPath) : 94 | fs.existsSync(path.resolve(paths.appPath, 'tsconfig.paths.json')) ? 95 | path.resolve(paths.appPath, 'tsconfig.paths.json') : 96 | fs.existsSync(path.resolve(paths.appPath, 'tsconfig.json')) ? 97 | path.resolve(paths.appPath, 'tsconfig.json') : 98 | fs.existsSync(path.resolve(paths.appPath, 'jsconfig.paths.json')) ? 99 | path.resolve(paths.appPath, 'jsconfig.paths.json') : 100 | fs.existsSync(path.resolve(paths.appPath, 'jsconfig.json')) ? 101 | path.resolve(paths.appPath, 'jsconfig.json') : 102 | '' 103 | ) 104 | } 105 | 106 | function configPathsRaw(confPath) { 107 | if(!confPath) 108 | throw Error('react-app-rewire-alias:configPaths: there is no config file found') 109 | 110 | const conf = require(confPath) 111 | const extmsg = !conf.extends ? '' : `, specify ${conf.extends} instead of ${confPath} for configPaths()` 112 | 113 | if(typeof conf.compilerOptions.paths !== 'object') 114 | throw Error(` 115 | react-app-rewire-alias:configPaths: array expected for paths${extmsg}`) 116 | 117 | if(!conf.compilerOptions || !conf.compilerOptions.paths) 118 | return {} 119 | 120 | return conf.compilerOptions.paths 121 | } 122 | 123 | function configPaths(configPath = '') { 124 | const confPath = configFilePath(configPath) 125 | const paths = configPathsRaw(confPath) 126 | return Object.keys(paths).reduce( (a, path) => { 127 | const value = paths[path] 128 | const target = Array.isArray(value) ? value[0] : value 129 | a[path.replace(/\/\*$/,'')] = target.replace(/\/\*$/,'') 130 | return a 131 | }, {}) 132 | } 133 | 134 | function aliasMapForJest(aliasMap) { 135 | return Object.keys(aliasMap).reduce( (a, i) => { 136 | const outside = isOutsideOfRoot(aliasMap[i]) 137 | const restr = i.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') 138 | const alias = `^${restr}/(.*)$` 139 | const targ = outside ? path.resolve(aliasMap[i])+'/$1' : `/${aliasMap[i]}/$1` 140 | return { ...a, [alias]: targ } 141 | }, {}) 142 | } 143 | 144 | const CracoAliasPlugin = { 145 | overrideWebpackConfig: function({webpackConfig, pluginOptions}) { 146 | return alias(pluginOptions.alias||configPaths())(webpackConfig) 147 | }, 148 | overrideJestConfig: function({jestConfig, pluginOptions}) { 149 | return aliasJest(pluginOptions.alias||configPaths())(jestConfig) 150 | } 151 | } 152 | 153 | module.exports = { 154 | alias, 155 | aliasJest, 156 | configFilePath, 157 | configPathsRaw, 158 | configPaths, 159 | expandResolveAlias, 160 | expandRulesInclude, 161 | expandPluginsScope, 162 | CracoAliasPlugin, 163 | } 164 | --------------------------------------------------------------------------------