├── .eslintignore
├── .eslintrc
├── .github
├── ISSUE_TEMPLATE
│ └── bug_report.md
└── workflows
│ ├── nodejs.yml
│ └── npmpublish.yml
├── .gitignore
├── .husky
├── .gitignore
└── pre-commit
├── .prettierrc
├── LICENSE
├── README.md
├── jest.config.json
├── mocks
├── jsconfig.json
└── tsconfig.paths.json
├── package.json
├── plugin
├── __snapshots__
│ ├── generate-module-name-mapper.test.js.snap
│ └── normalize-plugin-options.test.js.snap
├── create-overrider.js
├── debug.js
├── exit-with-error.js
├── extract-aliases
│ ├── __snapshots__
│ │ ├── index.test.js.snap
│ │ └── normalize-aliases.test.js.snap
│ ├── index.js
│ ├── index.test.js
│ ├── normalize-aliases.js
│ └── normalize-aliases.test.js
├── filter-aliases.js
├── filter-aliases.test.js
├── generate-module-name-mapper.js
├── generate-module-name-mapper.test.js
├── helpers
│ └── escape-string-for-regexp.js
├── index.js
├── normalize-plugin-options.js
├── normalize-plugin-options.test.js
└── pre-check
│ ├── check-config-contents.js
│ ├── check-config-contents.test.js
│ ├── check-config-existence.js
│ ├── check-config-existence.test.js
│ ├── check-config.js
│ ├── check-options.js
│ ├── check-options.test.js
│ └── index.js
└── yarn.lock
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/**
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "jest": true
4 | },
5 | "extends": [
6 | "@eslint-kit/base",
7 | "@eslint-kit/node",
8 | "@eslint-kit/prettier"
9 | ],
10 | "parser": "babel-eslint",
11 | "rules": {
12 | "no-console": "off"
13 | }
14 | }
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | Follow these steps:
11 |
12 | https://github.com/risenforces/craco-alias#ran-into-a-problem
13 |
14 | Then describe your problem and paste the plugin output.
15 |
--------------------------------------------------------------------------------
/.github/workflows/nodejs.yml:
--------------------------------------------------------------------------------
1 | name: Node CI
2 |
3 | on:
4 | pull_request:
5 | branches: master
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 |
11 | strategy:
12 | matrix:
13 | node-version: [12.x]
14 |
15 | steps:
16 | - uses: actions/checkout@v1
17 | - name: Use Node.js ${{ matrix.node-version }}
18 | uses: actions/setup-node@v1
19 | with:
20 | node-version: ${{ matrix.node-version }}
21 | - name: npm install, and test
22 | run: |
23 | yarn install --frozen-lockfile
24 | yarn test
25 | env:
26 | CI: true
27 |
--------------------------------------------------------------------------------
/.github/workflows/npmpublish.yml:
--------------------------------------------------------------------------------
1 | name: Node.js Package
2 |
3 | on:
4 | release:
5 | types: [created]
6 |
7 | jobs:
8 | test:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v1
12 | - uses: actions/setup-node@v1
13 | with:
14 | node-version: 12
15 | - run: yarn install --frozen-lockfile
16 | - run: yarn test
17 |
18 | publish-npm:
19 | needs: test
20 | runs-on: ubuntu-latest
21 | steps:
22 | - uses: actions/checkout@v1
23 | - uses: actions/setup-node@v1
24 | with:
25 | node-version: 12
26 | registry-url: https://registry.npmjs.org/
27 | - run: yarn install --frozen-lockfile
28 | - run: npm publish
29 | env:
30 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
--------------------------------------------------------------------------------
/.husky/.gitignore:
--------------------------------------------------------------------------------
1 | _
2 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | yarn lint-staged
5 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "singleQuote": true,
4 | "tabWidth": 2,
5 | "quoteProps": "consistent",
6 | "trailingComma": "es5",
7 | "endOfLine": "lf"
8 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Evgeny Zakharov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # This package is deprecated, use [react-app-alias](https://github.com/oklas/react-app-alias) instead
2 |
3 | [](https://www.npmjs.com/package/craco-alias)
4 |
5 | A [craco](https://github.com/sharegate/craco) plugin for automatic aliases generation for Webpack and Jest.
6 |
7 | ## List of Contents
8 |
9 | - [Installation](#installation)
10 | - [Options](#options)
11 | - [Examples](#examples)
12 | - [Ran into a problem?](#ran-into-a-problem)
13 | - [If you want to help](#if-you-want-to-help)
14 |
15 | ### Installation
16 |
17 | 1. Install [craco](https://github.com/gsoft-inc/craco/blob/master/packages/craco/README.md#installation)
18 |
19 | 2. Install `craco-alias`:
20 |
21 | ```sh
22 | npm i -D craco-alias
23 | ```
24 |
25 | ```sh
26 | yarn add -D craco-alias
27 | ```
28 |
29 | 3. Edit `craco.config.js`:
30 |
31 | ```js
32 | const CracoAlias = require("craco-alias");
33 |
34 | module.exports = {
35 | plugins: [
36 | {
37 | plugin: CracoAlias,
38 | options: {
39 | // see in examples section
40 | }
41 | }
42 | ]
43 | };
44 | ```
45 |
46 | 4. Go to [Examples](#examples) section
47 |
48 | ### Options
49 |
50 | - `source`:
51 | One of `"options"`, `"jsconfig"`, `"tsconfig"`
52 | Optional, defaults to `"options"`
53 |
54 | - `baseUrl`:
55 | A base url for aliases. (`./src` for example)
56 | Optional, defaults to `./` (project root directory)
57 |
58 | - `aliases`:
59 | An object with aliases names and paths
60 | Only required when `source` is set to `"options"`
61 |
62 | - `tsConfigPath`:
63 | A path to tsconfig file
64 | Only required when `source` is set to `"tsconfig"`
65 |
66 | - `filter`:
67 | A function of type `([key, value]) => boolean`
68 | Optional, used to remove some aliases from the resulting config
69 | Example: `([key]) => !key.startsWith('node_modules')`
70 |
71 | - `unsafeAllowModulesOutsideOfSrc`:
72 | Allow importing modules outside of `./src` folder.
73 | Disables webpack `ModuleScopePlugin`.
74 |
75 | - `debug`:
76 | Enable it if you ran into a problem. It will log a useful info in console.
77 | Optional, defaults to `false`
78 |
79 | ### Examples
80 |
81 |
82 | Specify aliases manually (source: "options")
83 |
84 | > Note: you don't need to add `/*` part for directories in this case
85 |
86 | ```js
87 | /* craco.config.js */
88 |
89 | const CracoAlias = require("craco-alias");
90 |
91 | module.exports = {
92 | plugins: [
93 | {
94 | plugin: CracoAlias,
95 | options: {
96 | source: "options",
97 | baseUrl: "./",
98 | aliases: {
99 | "@file": "./src/file.js",
100 | "@dir": "./src/some/dir",
101 | // you can alias packages too
102 | "@material-ui": "./node_modules/@material-ui-ie10"
103 | }
104 | }
105 | }
106 | ]
107 | };
108 | ```
109 |
110 |
111 |
112 |
113 | Use aliases from jsconfig.json (source: "jsconfig")
114 |
115 | ```js
116 | /* craco.config.js */
117 |
118 | const CracoAlias = require("craco-alias");
119 |
120 | module.exports = {
121 | plugins: [
122 | {
123 | plugin: CracoAlias,
124 | options: {
125 | source: "jsconfig",
126 | // baseUrl SHOULD be specified
127 | // plugin does not take it from jsconfig
128 | baseUrl: "./src"
129 | }
130 | }
131 | ]
132 | };
133 | ```
134 |
135 | > **Note:** your jsconfig should always have `compilerOptions.paths` property. `baseUrl` is optional for plugin, but some IDEs and editors require it for intellisense.
136 |
137 | ```js
138 | /* jsconfig.json */
139 |
140 | {
141 | "compilerOptions": {
142 | "baseUrl": "./src",
143 | "paths": {
144 | // file aliases
145 | "@baz": ["./baz.js"],
146 | "@boo": ["./boo.jsx"],
147 |
148 | // folder aliases
149 | "@root": ["./"],
150 | "@root/*": ["./*"],
151 | "@lib": ["./lib"],
152 | "@lib/*": ["./lib/*"],
153 |
154 | // package aliases (types is optional without ts)
155 | "@my-react-select": [
156 | "../node_modules/react-select",
157 | "../node_modules/@types/react-select"
158 | ],
159 | "@my-react-select/*": [
160 | "../node_modules/react-select/*",
161 | "../node_modules/@types/react-select"
162 | ]
163 | }
164 | }
165 | }
166 | ```
167 |
168 |
169 |
170 |
171 | Use aliases from tsconfig.json (source: "tsconfig")
172 |
173 | 1. Go to project's root directory.
174 |
175 | 2. Create `tsconfig.extend.json`.
176 |
177 | 3. Edit it as follows:
178 |
179 | ```js
180 | {
181 | "compilerOptions": {
182 | "baseUrl": "./src",
183 | "paths": {
184 | // file aliases
185 | "@baz": ["./baz.ts"],
186 | "@boo": ["./boo.tsx"],
187 |
188 | // folder aliases
189 | "@root": ["./"],
190 | "@root/*": ["./*"],
191 | "@lib": ["./lib"],
192 | "@lib/*": ["./lib/*"],
193 |
194 | // package aliases
195 | "@my-react-select": [
196 | "../node_modules/react-select",
197 | "../node_modules/@types/react-select"
198 | ],
199 | "@my-react-select/*": [
200 | "../node_modules/react-select/*",
201 | "../node_modules/@types/react-select"
202 | ]
203 | }
204 | }
205 | }
206 | ```
207 |
208 | 4. Go to `tsconfig.json`.
209 |
210 | 5. Extend `tsconfig.json` from `tsconfig.extend.json`:
211 |
212 | ```diff
213 | {
214 | + "extends": "./tsconfig.extend.json",
215 | "compilerOptions": {
216 | ...
217 | },
218 | ...
219 | }
220 | ```
221 |
222 | 6. Edit `craco.config.js`:
223 |
224 | ```js
225 | const CracoAlias = require("craco-alias");
226 |
227 | module.exports = {
228 | plugins: [
229 | {
230 | plugin: CracoAlias,
231 | options: {
232 | source: "tsconfig",
233 | // baseUrl SHOULD be specified
234 | // plugin does not take it from tsconfig
235 | baseUrl: "./src",
236 | // tsConfigPath should point to the file where "baseUrl" and "paths" are specified
237 | tsConfigPath: "./tsconfig.extend.json"
238 | }
239 | }
240 | ]
241 | };
242 | ```
243 |
244 |
245 |
246 | ### Ran into a problem?
247 |
248 | 1. Make sure your config is valid.
249 |
250 | 2. Set `debug` to `true` in [options](#options).
251 |
252 | 3. Run application again.
253 |
254 | 4. Copy a printed info.
255 |
256 | 5. [Here](https://github.com/risenforces/craco-alias/issues), create an issue describing your problem (do not forget to add the debug info).
257 |
258 | ### If you want to help
259 |
260 | Install:
261 |
262 | ```sh
263 | yarn
264 | ```
265 |
266 | > Use yarn please. npm may fail the dependencies installation.
267 |
268 | Run tests:
269 |
270 | ```
271 | yarn test
272 | ```
273 |
--------------------------------------------------------------------------------
/jest.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "snapshotSerializers": [
3 | "jest-serializer-path"
4 | ]
5 | }
--------------------------------------------------------------------------------
/mocks/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@file": ["./src/file.js"],
5 | "@file2": ["src/file2.js"],
6 | "@dir/*": ["./src/dir/*"],
7 | "@dir2/*": ["././src/dir2/*"],
8 | "$dir3/*": ["src/dir3/*", "src/dir3"],
9 | "my-package": [
10 | "./node_modules/some-package",
11 | "./node_modules/some-package/*"
12 | ],
13 | "external-package": [
14 | "/absolute_path/external-package",
15 | "/absolute_path/external-package/*"
16 | ],
17 | "@material-ui": ["node_modules/@material-ui/ie-10/ie-10.js"]
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/mocks/tsconfig.paths.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@file": ["./src/file.js"],
5 | "@file2": ["src/file2.js"],
6 | "@dir/*": ["./src/dir/*"],
7 | "@dir2/*": ["././src/dir2/*"],
8 | "$dir3/*": ["src/dir3/*", "src/dir3"],
9 | "my-package": [
10 | "./node_modules/some-package",
11 | "./node_modules/some-package/*"
12 | ],
13 | "external-package": [
14 | "/absolute_path/external-package",
15 | "/absolute_path/external-package/*"
16 | ],
17 | "@material-ui": ["node_modules/@material-ui/ie-10/ie-10.js"]
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "craco-alias",
3 | "version": "3.0.2",
4 | "description": "A craco plugin for automatic aliases generation",
5 | "main": "plugin/index.js",
6 | "scripts": {
7 | "test": "jest \".*\\.test\\.js\" --config jest.config.json",
8 | "prepare": "husky install"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/risenforces/craco-alias.git"
13 | },
14 | "keywords": [
15 | "craco",
16 | "create-react-app",
17 | "react",
18 | "cra",
19 | "alias",
20 | "aliases",
21 | "webpack",
22 | "jest"
23 | ],
24 | "author": "Evgeny Zakharov ",
25 | "license": "MIT",
26 | "bugs": {
27 | "url": "https://github.com/risenforces/craco-alias/issues"
28 | },
29 | "homepage": "https://github.com/risenforces/craco-alias#readme",
30 | "devDependencies": {
31 | "@eslint-kit/eslint-config-base": "^3.2.0",
32 | "@eslint-kit/eslint-config-node": "^2.0.0",
33 | "@eslint-kit/eslint-config-prettier": "^2.0.0",
34 | "@types/jest": "^26.0.23",
35 | "babel-eslint": "10.1.0",
36 | "eslint": "7.10.0",
37 | "husky": "^6.0.0",
38 | "jest": "^24.9.0",
39 | "jest-serializer-path": "^0.1.15",
40 | "lint-staged": "^11.0.0",
41 | "prettier": "2.2.1"
42 | },
43 | "lint-staged": {
44 | "**/*.js": [
45 | "eslint --fix",
46 | "git add"
47 | ]
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/plugin/__snapshots__/generate-module-name-mapper.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`generate-module-name-mapper should correctly generate moduleNameMapper 1`] = `
4 | Object {
5 | "^@dir$": "/some/absolute/path/to/dir",
6 | "^@dir/(.*)$": "/some/absolute/path/to/dir/$1",
7 | "^@file$": "/some/absolute/path/to/file.js",
8 | }
9 | `;
10 |
11 | exports[`generate-module-name-mapper should correctly generate moduleNameMapper when alias name have special RegExp characters 1`] = `
12 | Object {
13 | "^/$dir$": "/some/absolute/path/to/dir",
14 | "^/$dir/(.*)$": "/some/absolute/path/to/dir/$1",
15 | "^/$file$": "/some/absolute/path/to/file.js",
16 | }
17 | `;
18 |
--------------------------------------------------------------------------------
/plugin/__snapshots__/normalize-plugin-options.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`normalize-plugin-options should return default config 1`] = `
4 | Object {
5 | "aliases": null,
6 | "baseUrl": "./",
7 | "debug": false,
8 | "filter": [Function],
9 | "source": "options",
10 | "unsafeAllowModulesOutsideOfSrc": false,
11 | }
12 | `;
13 |
14 | exports[`normalize-plugin-options should return default config 2`] = `
15 | Object {
16 | "aliases": null,
17 | "baseUrl": "./",
18 | "debug": false,
19 | "filter": [Function],
20 | "source": "options",
21 | "unsafeAllowModulesOutsideOfSrc": false,
22 | }
23 | `;
24 |
25 | exports[`normalize-plugin-options should return jsconfig-specific config 1`] = `
26 | Object {
27 | "baseUrl": "./",
28 | "debug": false,
29 | "filter": [Function],
30 | "source": "jsconfig",
31 | "unsafeAllowModulesOutsideOfSrc": false,
32 | }
33 | `;
34 |
35 | exports[`normalize-plugin-options should return the same as an input 1`] = `
36 | Object {
37 | "aliases": Object {
38 | "@file": "./file.js",
39 | },
40 | "baseUrl": "./src",
41 | "debug": false,
42 | "filter": [Function],
43 | "source": "options",
44 | "unsafeAllowModulesOutsideOfSrc": true,
45 | }
46 | `;
47 |
48 | exports[`normalize-plugin-options should return tsconfig-specific config 1`] = `
49 | Object {
50 | "baseUrl": "./",
51 | "debug": false,
52 | "filter": [Function],
53 | "source": "tsconfig",
54 | "tsConfigPath": "tsconfig.paths.json",
55 | "unsafeAllowModulesOutsideOfSrc": false,
56 | }
57 | `;
58 |
--------------------------------------------------------------------------------
/plugin/create-overrider.js:
--------------------------------------------------------------------------------
1 | const preCheck = require('./pre-check')
2 | const normalizePluginOptions = require('./normalize-plugin-options')
3 | const extractAliases = require('./extract-aliases')
4 | const { searchObject, printBaseData, printObject } = require('./debug')
5 | const { filterAliases } = require('./filter-aliases')
6 |
7 | const createOverrider = (callback, debugInfo) => (cracoOptions) => {
8 | preCheck(cracoOptions)
9 |
10 | const options = normalizePluginOptions(cracoOptions.pluginOptions)
11 | const initialAliases = extractAliases(cracoOptions)
12 | const aliases = filterAliases(initialAliases, options.filter)
13 |
14 | if (options.debug) {
15 | printBaseData({
16 | initialOptions: cracoOptions.pluginOptions,
17 | normalizedOptions: options,
18 | initialAliases,
19 | aliases,
20 | })
21 | }
22 |
23 | const overridedConfig = callback({ aliases, options }, cracoOptions)
24 |
25 | if (options.debug) {
26 | const { name, aliasesPath } = debugInfo
27 | const aliases = searchObject(overridedConfig, aliasesPath)
28 | printObject(name, aliases)
29 | }
30 |
31 | return overridedConfig
32 | }
33 |
34 | module.exports = createOverrider
35 |
--------------------------------------------------------------------------------
/plugin/debug.js:
--------------------------------------------------------------------------------
1 | const state = {
2 | wasOptionsPrinted: false,
3 | callCount: 0,
4 | }
5 |
6 | function debounce(callback, wait, immediate = false) {
7 | let timeout = null
8 |
9 | return function (...args) {
10 | const callNow = immediate && !timeout
11 | const next = () => callback.apply(this, args)
12 |
13 | clearTimeout(timeout)
14 | timeout = setTimeout(next, wait)
15 |
16 | if (callNow) {
17 | next()
18 | }
19 | }
20 | }
21 |
22 | function blue(string) {
23 | return '\u001B[34m' + string + '\u001B[0m'
24 | }
25 |
26 | function searchObject(object, path) {
27 | return path.split('.').reduce((acc, segment) => {
28 | if (typeof acc !== 'object') {
29 | return null
30 | }
31 |
32 | return acc[segment]
33 | }, object)
34 | }
35 |
36 | function exit() {
37 | process.exit(0)
38 | }
39 |
40 | const debouncedExit = debounce(exit, 70)
41 |
42 | function printBaseData({
43 | initialOptions,
44 | normalizedOptions,
45 | initialAliases,
46 | aliases,
47 | }) {
48 | if (state.wasOptionsPrinted) return
49 |
50 | console.log(blue('Initial options:') + '\n')
51 | console.log(JSON.stringify(initialOptions, null, 2) + '\n')
52 | console.log(blue('Normalized options:') + '\n')
53 | console.log(JSON.stringify(normalizedOptions, null, 2) + '\n')
54 | console.log(blue('Initial aliases:') + '\n')
55 | console.log(JSON.stringify(initialAliases, null, 2) + '\n')
56 | console.log(blue('Aliases:') + '\n')
57 | console.log(JSON.stringify(aliases, null, 2) + '\n')
58 |
59 | state.wasOptionsPrinted = true
60 |
61 | debouncedExit()
62 | }
63 |
64 | function printObject(name, data) {
65 | console.log(blue(name + ':') + '\n')
66 |
67 | if (typeof data === 'object') {
68 | console.log(JSON.stringify(data, null, 2) + '\n')
69 | } else {
70 | console.log('Not an object \n')
71 | }
72 |
73 | debouncedExit()
74 | }
75 |
76 | exports.searchObject = searchObject
77 | exports.printBaseData = printBaseData
78 | exports.printObject = printObject
79 |
--------------------------------------------------------------------------------
/plugin/exit-with-error.js:
--------------------------------------------------------------------------------
1 | const exitWithError = (message) => {
2 | console.log('\u001B[31m%s\u001B[0m', '[Craco-Alias Error]', message)
3 |
4 | console.log(
5 | '\nPlugin documentation:',
6 | '\u001B[34mhttps://github.com/risenforces/craco-alias\u001B[0m'
7 | )
8 |
9 | process.exit(0)
10 | }
11 |
12 | module.exports = exitWithError
13 |
--------------------------------------------------------------------------------
/plugin/extract-aliases/__snapshots__/index.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`extract-aliases should correctly extract aliases from jsconfig: extract-aliases/snap-1 1`] = `
4 | Object {
5 | "$dir3": "/src/dir3",
6 | "@dir": "/src/dir",
7 | "@dir2": "/src/dir2",
8 | "@file": "/src/file.js",
9 | "@file2": "/src/file2.js",
10 | "@material-ui": "/node_modules/@material-ui/ie-10/ie-10.js",
11 | "external-package": "/absolute_path/external-package",
12 | "my-package": "/node_modules/some-package",
13 | }
14 | `;
15 |
16 | exports[`extract-aliases should correctly extract aliases from options: extract-aliases/snap-1 1`] = `
17 | Object {
18 | "$dir3": "/src/dir3",
19 | "@dir": "/src/dir",
20 | "@dir2": "/src/dir2",
21 | "@file": "/src/file.js",
22 | "@file2": "/src/file2.js",
23 | "@material-ui": "/node_modules/@material-ui/ie-10/ie-10.js",
24 | "external-package": "/absolute_path/external-package",
25 | "my-package": "/node_modules/some-package",
26 | }
27 | `;
28 |
29 | exports[`extract-aliases should correctly extract aliases from tsconfig: extract-aliases/snap-1 1`] = `
30 | Object {
31 | "$dir3": "/src/dir3",
32 | "@dir": "/src/dir",
33 | "@dir2": "/src/dir2",
34 | "@file": "/src/file.js",
35 | "@file2": "/src/file2.js",
36 | "@material-ui": "/node_modules/@material-ui/ie-10/ie-10.js",
37 | "external-package": "/absolute_path/external-package",
38 | "my-package": "/node_modules/some-package",
39 | }
40 | `;
41 |
--------------------------------------------------------------------------------
/plugin/extract-aliases/__snapshots__/normalize-aliases.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`normalize-aliases should correctly normalize aliases 1`] = `
4 | Object {
5 | "$dir3": "/src/dir3",
6 | "@dir": "/src/dir",
7 | "@dir2": "/src/dir2",
8 | "@file": "/src/file.js",
9 | "@file2": "/src/file2.js",
10 | "@material-ui": "/node_modules/@material-ui/ie-10/ie-10.js",
11 | "external-package": "/absolute_path/external-package",
12 | "my-package": "/node_modules/some-package",
13 | }
14 | `;
15 |
16 | exports[`normalize-aliases should correctly normalize aliases 2`] = `
17 | Object {
18 | "$dir3": "/src/dir3",
19 | "@dir": "/src/dir",
20 | "@dir2": "/src/dir2",
21 | "@file": "/src/file.js",
22 | "@file2": "/src/file2.js",
23 | }
24 | `;
25 |
26 | exports[`normalize-aliases should correctly normalize aliases 3`] = `
27 | Object {
28 | "$dir3": Array [
29 | "/fallback_test/dir3",
30 | "/fallback_test/fallback/dir3",
31 | ],
32 | "@file2": Array [
33 | "/fallback_test/file2.js",
34 | ],
35 | }
36 | `;
37 |
--------------------------------------------------------------------------------
/plugin/extract-aliases/index.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const path = require('path')
3 | const normalizePluginOptions = require('../normalize-plugin-options')
4 | const normalizeAliases = require('./normalize-aliases')
5 |
6 | const extractAliasesFromConfig = ({ configPath, absoluteBaseUrl }) => {
7 | const configFileContents = fs.readFileSync(configPath)
8 | const config = JSON.parse(configFileContents)
9 |
10 | const { compilerOptions } = config
11 |
12 | const standardAliases = {}
13 |
14 | for (const aliasName in compilerOptions.paths) {
15 | const alias = compilerOptions.paths[aliasName]
16 | if (typeof alias === 'string') {
17 | const [aliasPath] = compilerOptions.paths[aliasName]
18 | standardAliases[aliasName.replace('/*', '')] = aliasPath.replace('/*', '')
19 | } else {
20 | const aliasPath = []
21 | alias.forEach((anAlias) => {
22 | const sanitized = anAlias.replace('/*', '')
23 | if (!aliasPath.includes(sanitized)) aliasPath.push(sanitized)
24 | })
25 | standardAliases[aliasName.replace('/*', '')] =
26 | aliasPath.length > 1 ? aliasPath : aliasPath[0]
27 | }
28 | }
29 |
30 | return normalizeAliases({
31 | absoluteBaseUrl,
32 | aliases: standardAliases,
33 | })
34 | }
35 |
36 | const extractAliases = ({ pluginOptions, context: { paths } }) => {
37 | const options = normalizePluginOptions(pluginOptions)
38 |
39 | const { appPath } = paths
40 | const { baseUrl } = options
41 |
42 | const absoluteBaseUrl = path.resolve(appPath, baseUrl)
43 |
44 | if (options.source === 'jsconfig')
45 | return extractAliasesFromConfig({
46 | configPath: paths.appJsConfig,
47 | absoluteBaseUrl,
48 | })
49 |
50 | if (options.source === 'tsconfig')
51 | return extractAliasesFromConfig({
52 | configPath: options.tsConfigPath,
53 | absoluteBaseUrl,
54 | })
55 |
56 | if (options.source === 'options')
57 | return normalizeAliases({
58 | absoluteBaseUrl,
59 | aliases: options.aliases,
60 | })
61 | }
62 |
63 | module.exports = extractAliases
64 |
--------------------------------------------------------------------------------
/plugin/extract-aliases/index.test.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const extractAliases = require('.')
3 |
4 | describe('extract-aliases', () => {
5 | const appPath = path.resolve(__dirname, '../..')
6 | const appJsConfig = path.resolve(appPath, 'mocks/jsconfig.json')
7 | const tsConfigPath = path.resolve(appPath, 'mocks/tsconfig.paths.json')
8 |
9 | const context = {
10 | paths: {
11 | appPath,
12 | appJsConfig,
13 | },
14 | }
15 |
16 | const inputs = {
17 | fromOptions: {
18 | pluginOptions: {
19 | source: 'options',
20 | aliases: {
21 | '@file': './src/file.js',
22 | '@file2': 'src/file2.js',
23 | '@dir': './src/dir',
24 | '@dir2': '././src/dir2/',
25 | '$dir3': 'src/dir3',
26 | 'my-package': './node_modules/some-package',
27 | 'external-package': '/absolute_path/external-package',
28 | '@material-ui': 'node_modules/@material-ui/ie-10/ie-10.js',
29 | },
30 | },
31 | context,
32 | },
33 | fromJsConfig: {
34 | pluginOptions: {
35 | source: 'jsconfig',
36 | },
37 | context,
38 | },
39 | fromTsConfig: {
40 | pluginOptions: {
41 | source: 'tsconfig',
42 | tsConfigPath,
43 | },
44 | context,
45 | },
46 | }
47 |
48 | const snapshotName = 'extract-aliases/snap-1'
49 |
50 | test('should correctly extract aliases from options', () => {
51 | expect(extractAliases(inputs.fromOptions)).toMatchSnapshot(snapshotName)
52 | })
53 |
54 | test('should correctly extract aliases from jsconfig', () => {
55 | expect(extractAliases(inputs.fromJsConfig)).toMatchSnapshot(snapshotName)
56 | })
57 |
58 | test('should correctly extract aliases from tsconfig', () => {
59 | expect(extractAliases(inputs.fromTsConfig)).toMatchSnapshot(snapshotName)
60 | })
61 | })
62 |
--------------------------------------------------------------------------------
/plugin/extract-aliases/normalize-aliases.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | const normalizeAliases = ({ absoluteBaseUrl, aliases }) => {
4 | const result = {}
5 |
6 | function resolve(alias) {
7 | // remove trailing slash
8 | const cleanAlias = alias.replace(/\/$/, '')
9 |
10 | // make alias path absolute
11 | return path.resolve(absoluteBaseUrl, cleanAlias)
12 | }
13 |
14 | for (const aliasName in aliases) {
15 | const alias = aliases[aliasName]
16 | if (typeof alias === 'string') {
17 | result[aliasName] = resolve(alias)
18 | } else {
19 | const results = []
20 | alias.forEach((anAlias) => {
21 | results.push(resolve(anAlias))
22 | })
23 | result[aliasName] = results
24 | }
25 | }
26 |
27 | return result
28 | }
29 |
30 | module.exports = normalizeAliases
31 |
--------------------------------------------------------------------------------
/plugin/extract-aliases/normalize-aliases.test.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable sonarjs/no-duplicate-string */
2 | const path = require('path')
3 |
4 | const normalizeAliases = require('./normalize-aliases')
5 |
6 | describe('normalize-aliases', () => {
7 | const appPath = path.resolve(__dirname, '../..')
8 |
9 | test('should correctly normalize aliases', () => {
10 | expect(
11 | normalizeAliases({
12 | absoluteBaseUrl: path.resolve(appPath, '.'),
13 | aliases: {
14 | '@file': './src/file.js',
15 | '@file2': 'src/file2.js',
16 | '@dir': './src/dir',
17 | '@dir2': '././src/dir2/',
18 | '$dir3': 'src/dir3',
19 | 'my-package': './node_modules/some-package',
20 | 'external-package': '/absolute_path/external-package',
21 | '@material-ui': 'node_modules/@material-ui/ie-10/ie-10.js',
22 | },
23 | })
24 | ).toMatchSnapshot()
25 |
26 | expect(
27 | normalizeAliases({
28 | absoluteBaseUrl: path.resolve(appPath, './src'),
29 | aliases: {
30 | '@file': './file.js',
31 | '@file2': 'file2.js',
32 | '@dir': './dir',
33 | '@dir2': '././dir2/',
34 | '$dir3': 'dir3',
35 | },
36 | })
37 | ).toMatchSnapshot()
38 |
39 | expect(
40 | normalizeAliases({
41 | absoluteBaseUrl: path.resolve(appPath, './fallback_test'),
42 | aliases: {
43 | '$dir3': ['dir3', 'fallback/dir3'],
44 | '@file2': ['file2.js'],
45 | },
46 | })
47 | ).toMatchSnapshot()
48 | })
49 | })
50 |
--------------------------------------------------------------------------------
/plugin/filter-aliases.js:
--------------------------------------------------------------------------------
1 | function fromEntries(entries) {
2 | const result = {}
3 | for (const [key, value] of entries) {
4 | result[key] = value
5 | }
6 | return result
7 | }
8 |
9 | function filterAliases(aliases, filter) {
10 | const entries = Object.entries(aliases)
11 | const filteredEntries = entries.filter(filter)
12 | return fromEntries(filteredEntries)
13 | }
14 |
15 | exports.filterAliases = filterAliases
16 |
--------------------------------------------------------------------------------
/plugin/filter-aliases.test.js:
--------------------------------------------------------------------------------
1 | const { filterAliases } = require('./filter-aliases')
2 |
3 | test('filter-aliases', () => {
4 | expect(
5 | filterAliases(
6 | {
7 | '@my-alias-one': 'sdfsdfsdf',
8 | '@my-alias-two': '123123',
9 | '@my-alias-three:': '321',
10 | },
11 | ([key, value]) => {
12 | if (key === '@my-alias-one') return false
13 | if (value === '321') return false
14 | return true
15 | }
16 | )
17 | ).toEqual({
18 | '@my-alias-two': '123123',
19 | })
20 | })
21 |
--------------------------------------------------------------------------------
/plugin/generate-module-name-mapper.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const escapeStringForRegExp = require('./helpers/escape-string-for-regexp')
3 |
4 | const getModuleNameMapper = ({ aliases }) => {
5 | const moduleNameMapper = {}
6 |
7 | for (const unescapedAliasName in aliases) {
8 | const aliasName = escapeStringForRegExp(unescapedAliasName)
9 | const aliasPath = aliases[unescapedAliasName]
10 |
11 | const isFile = path.extname(aliasPath).length > 0
12 |
13 | if (isFile) {
14 | moduleNameMapper[`^${aliasName}$`] = aliasPath
15 | } else {
16 | moduleNameMapper[`^${aliasName}$`] = aliasPath
17 | moduleNameMapper[`^${aliasName}/(.*)$`] = `${aliasPath}/$1`
18 | }
19 | }
20 |
21 | return moduleNameMapper
22 | }
23 |
24 | module.exports = getModuleNameMapper
25 |
--------------------------------------------------------------------------------
/plugin/generate-module-name-mapper.test.js:
--------------------------------------------------------------------------------
1 | const generateModuleNameMapper = require('./generate-module-name-mapper')
2 |
3 | describe('generate-module-name-mapper', () => {
4 | const paths = {
5 | file: '/some/absolute/path/to/file.js',
6 | dir: '/some/absolute/path/to/dir',
7 | }
8 |
9 | test('should correctly generate moduleNameMapper', () => {
10 | const moduleNameMapper = generateModuleNameMapper({
11 | aliases: {
12 | '@file': paths.file,
13 | '@dir': paths.dir,
14 | },
15 | })
16 |
17 | expect(moduleNameMapper).toMatchSnapshot()
18 | })
19 |
20 | test('should correctly generate moduleNameMapper when alias name have special RegExp characters', () => {
21 | const moduleNameMapper = generateModuleNameMapper({
22 | aliases: {
23 | $file: paths.file,
24 | $dir: paths.dir,
25 | },
26 | })
27 |
28 | expect(moduleNameMapper).toMatchSnapshot()
29 | })
30 | })
31 |
--------------------------------------------------------------------------------
/plugin/helpers/escape-string-for-regexp.js:
--------------------------------------------------------------------------------
1 | module.exports = function escapeStringForRegExp(string) {
2 | return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
3 | }
4 |
--------------------------------------------------------------------------------
/plugin/index.js:
--------------------------------------------------------------------------------
1 | const createOverrider = require('./create-overrider')
2 | const generateModuleNameMapper = require('./generate-module-name-mapper')
3 |
4 | module.exports = {
5 | overrideWebpackConfig: createOverrider(
6 | ({ aliases, options }, { webpackConfig }) => ({
7 | ...webpackConfig,
8 | resolve: {
9 | ...webpackConfig.resolve,
10 | alias: {
11 | ...webpackConfig.resolve.alias,
12 | ...aliases,
13 | },
14 | plugins: options.unsafeAllowModulesOutsideOfSrc
15 | ? webpackConfig.resolve.plugins.filter(({ constructor }) => {
16 | if (!constructor) return true
17 | return constructor.name !== 'ModuleScopePlugin'
18 | })
19 | : webpackConfig.resolve.plugins,
20 | },
21 | }),
22 | {
23 | name: 'Webpack Config',
24 | aliasesPath: 'resolve.alias',
25 | }
26 | ),
27 | overrideJestConfig: createOverrider(
28 | ({ aliases }, { jestConfig }) => ({
29 | ...jestConfig,
30 | moduleNameMapper: {
31 | ...jestConfig.moduleNameMapper,
32 | ...generateModuleNameMapper({ aliases }),
33 | },
34 | }),
35 | {
36 | name: 'Jest Config',
37 | aliasesPath: 'moduleNameMapper',
38 | }
39 | ),
40 | }
41 |
--------------------------------------------------------------------------------
/plugin/normalize-plugin-options.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @typedef NormalizedConfig
3 | * @type {object}
4 | * @property {'jsconfig' | 'tsconfig' | 'options'} source
5 | * @property {string} baseUrl
6 | * @property {Object.} aliases
7 | * @property {string} [tsConfigPath]
8 | * @property {() => true} [filter]
9 | * @property {boolean} [unsafeAllowModulesOutsideOfSrc]
10 | */
11 |
12 | /**
13 | * @param {any} originalOptions
14 | * @returns {NormalizedConfig}
15 | */
16 | const normalizePluginOptions = (originalOptions) => {
17 | if (!originalOptions)
18 | return {
19 | source: 'options',
20 | baseUrl: './',
21 | aliases: null,
22 | debug: false,
23 | filter: () => true,
24 | unsafeAllowModulesOutsideOfSrc: false,
25 | }
26 |
27 | const {
28 | source = 'options',
29 | baseUrl = './',
30 | tsConfigPath,
31 | aliases = null,
32 | debug = false,
33 | filter = () => true,
34 | unsafeAllowModulesOutsideOfSrc = false,
35 | } = originalOptions
36 |
37 | if (source === 'jsconfig')
38 | return {
39 | source,
40 | baseUrl,
41 | debug,
42 | filter,
43 | unsafeAllowModulesOutsideOfSrc,
44 | }
45 |
46 | if (source === 'tsconfig')
47 | return {
48 | source,
49 | baseUrl,
50 | tsConfigPath,
51 | debug,
52 | filter,
53 | unsafeAllowModulesOutsideOfSrc,
54 | }
55 |
56 | return {
57 | source,
58 | baseUrl,
59 | aliases,
60 | debug,
61 | filter,
62 | unsafeAllowModulesOutsideOfSrc,
63 | }
64 | }
65 |
66 | module.exports = normalizePluginOptions
67 |
--------------------------------------------------------------------------------
/plugin/normalize-plugin-options.test.js:
--------------------------------------------------------------------------------
1 | const normalize = require('./normalize-plugin-options')
2 |
3 | describe('normalize-plugin-options', () => {
4 | test('should return default config', () => {
5 | expect(normalize(undefined)).toMatchSnapshot()
6 | expect(normalize({})).toMatchSnapshot()
7 | })
8 |
9 | test('should return jsconfig-specific config', () => {
10 | expect(
11 | normalize({
12 | source: 'jsconfig',
13 | baseUrl: './',
14 | aliases: {},
15 | })
16 | ).toMatchSnapshot()
17 | })
18 |
19 | test('should return tsconfig-specific config', () => {
20 | expect(
21 | normalize({
22 | source: 'tsconfig',
23 | tsConfigPath: 'tsconfig.paths.json',
24 | aliases: {},
25 | })
26 | ).toMatchSnapshot()
27 | })
28 |
29 | test('should return the same as an input', () => {
30 | expect(
31 | normalize({
32 | source: 'options',
33 | baseUrl: './src',
34 | aliases: {
35 | '@file': './file.js',
36 | },
37 | filter: () => true,
38 | unsafeAllowModulesOutsideOfSrc: true,
39 | })
40 | ).toMatchSnapshot()
41 | })
42 | })
43 |
--------------------------------------------------------------------------------
/plugin/pre-check/check-config-contents.js:
--------------------------------------------------------------------------------
1 | const checkConfigContents = ({
2 | unparsedConfig,
3 | configFileName,
4 | handleError,
5 | }) => {
6 | let config
7 |
8 | try {
9 | config = JSON.parse(unparsedConfig)
10 | } catch (error) {
11 | return handleError(
12 | `Cannot parse ${configFileName}. Please validate it on https://jsonformatter.curiousconcept.com.`
13 | )
14 | }
15 |
16 | if (!config.compilerOptions)
17 | return handleError(
18 | `Property "compilerOptions" is missing in ${configFileName}`
19 | )
20 |
21 | if (!config.compilerOptions.paths)
22 | return handleError(
23 | `Property "compilerOptions.paths" is missing in ${configFileName}`
24 | )
25 | }
26 |
27 | module.exports = checkConfigContents
28 |
--------------------------------------------------------------------------------
/plugin/pre-check/check-config-contents.test.js:
--------------------------------------------------------------------------------
1 | const check = require('./check-config-contents')
2 |
3 | describe('check-config-contents', () => {
4 | const handleErrorMock = jest.fn(() => {})
5 |
6 | const configFileName = 'some-config.json'
7 |
8 | const mockedCheck = ({ unparsedConfig }) =>
9 | check({
10 | unparsedConfig,
11 | configFileName,
12 | handleError: handleErrorMock,
13 | })
14 |
15 | test('should try to parse config contents', () => {
16 | mockedCheck({
17 | unparsedConfig: 'invalid-json',
18 | })
19 |
20 | expect(handleErrorMock).toHaveBeenLastCalledWith(
21 | `Cannot parse ${configFileName}. Please validate it on https://jsonformatter.curiousconcept.com.`
22 | )
23 | })
24 |
25 | const handyMockedCheck = (parsedConfig) =>
26 | mockedCheck({
27 | unparsedConfig: JSON.stringify(parsedConfig, null, 2),
28 | })
29 |
30 | test('should check config contents', () => {
31 | handyMockedCheck({})
32 |
33 | expect(handleErrorMock).toHaveBeenLastCalledWith(
34 | `Property "compilerOptions" is missing in ${configFileName}`
35 | )
36 |
37 | handyMockedCheck({
38 | compilerOptions: {
39 | baseUrl: 'src',
40 | },
41 | })
42 |
43 | expect(handleErrorMock).toHaveBeenLastCalledWith(
44 | `Property "compilerOptions.paths" is missing in ${configFileName}`
45 | )
46 | })
47 | })
48 |
--------------------------------------------------------------------------------
/plugin/pre-check/check-config-existence.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 |
3 | const checkConfigExistence = ({
4 | configName,
5 | configFileName,
6 | configPath,
7 | handleError,
8 | }) => {
9 | const isExist = fs.existsSync(configPath)
10 |
11 | if (!isExist)
12 | return handleError(
13 | `The "source" option is set to "${configName}",` +
14 | ` but no ${configFileName} was found in the project`
15 | )
16 | }
17 |
18 | module.exports = checkConfigExistence
19 |
--------------------------------------------------------------------------------
/plugin/pre-check/check-config-existence.test.js:
--------------------------------------------------------------------------------
1 | const check = require('./check-config-existence')
2 |
3 | describe('check-config-existence', () => {
4 | const handleErrorMock = jest.fn(() => {})
5 |
6 | test('should check config existence', () => {
7 | const configName = 'some-config'
8 | const configFileName = 'path.json'
9 | const configPath = 'some/config/path.json'
10 |
11 | check({
12 | configName,
13 | configFileName,
14 | configPath,
15 | handleError: handleErrorMock,
16 | })
17 |
18 | expect(handleErrorMock).toHaveBeenLastCalledWith(
19 | `The "source" option is set to "${configName}",` +
20 | ` but no ${configFileName} was found in the project`
21 | )
22 | })
23 | })
24 |
--------------------------------------------------------------------------------
/plugin/pre-check/check-config.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const path = require('path')
3 |
4 | const exitWithError = require('../exit-with-error')
5 | const checkConfigExistence = require('./check-config-existence')
6 | const checkConfigContents = require('./check-config-contents')
7 |
8 | const checkConfig = ({ configName, configPath }) => {
9 | const configFileName = path.basename(configPath)
10 |
11 | checkConfigExistence({
12 | configName,
13 | configFileName,
14 | configPath,
15 | handleError: exitWithError,
16 | })
17 |
18 | const unparsedConfig = fs.readFileSync(configPath)
19 |
20 | checkConfigContents({
21 | unparsedConfig,
22 | configFileName,
23 | handleError: exitWithError,
24 | })
25 | }
26 |
27 | module.exports = checkConfig
28 |
--------------------------------------------------------------------------------
/plugin/pre-check/check-options.js:
--------------------------------------------------------------------------------
1 | const normalizePluginOptions = require('../normalize-plugin-options')
2 |
3 | const checkOptions = ({ pluginOptions, handleError }) => {
4 | if (typeof pluginOptions === 'undefined') {
5 | return handleError('Plugin options should be specified')
6 | }
7 |
8 | if (typeof pluginOptions !== 'object') {
9 | return handleError('Plugin options should be an object')
10 | }
11 |
12 | const options = normalizePluginOptions(pluginOptions)
13 |
14 | const availableSources = ['jsconfig', 'tsconfig', 'options']
15 |
16 | if (!availableSources.includes(options.source)) {
17 | const availableSourcesString = availableSources
18 | .map((s) => `"${s}"`)
19 | .join(', ')
20 |
21 | return handleError(
22 | 'You have provided an invalid aliases source.' +
23 | ` Available sources are: ${availableSourcesString}`
24 | )
25 | }
26 |
27 | if (
28 | options.source === 'tsconfig' &&
29 | typeof options.tsConfigPath !== 'string'
30 | ) {
31 | return handleError(
32 | 'The "source" option is set to "tsconfig",' +
33 | ' but option "tsConfigPath" is missing or has incorrect value'
34 | )
35 | }
36 |
37 | if (options.source === 'options') {
38 | if (typeof options.baseUrl !== 'string') {
39 | return handleError('The "baseUrl" option should be a string')
40 | }
41 |
42 | if (typeof options.aliases !== 'object' || options.aliases === null)
43 | return handleError(
44 | 'The "source" option is set to "options",' +
45 | ' but option "aliases" is missing or has incorrect value'
46 | )
47 | }
48 |
49 | if (typeof options.debug !== 'boolean') {
50 | return handleError('The "debug" option should be a boolean value')
51 | }
52 |
53 | if (typeof options.filter !== 'function') {
54 | return handleError('The "filter" option should be a function')
55 | }
56 |
57 | if (typeof options.unsafeAllowModulesOutsideOfSrc !== 'boolean') {
58 | return handleError(
59 | 'The "unsafeAllowModulesOutsideOfSrc" option should be a boolean value'
60 | )
61 | }
62 | }
63 |
64 | module.exports = checkOptions
65 |
--------------------------------------------------------------------------------
/plugin/pre-check/check-options.test.js:
--------------------------------------------------------------------------------
1 | const check = require('./check-options')
2 |
3 | let handleErrorMock = jest.fn(() => {})
4 |
5 | beforeEach(() => {
6 | handleErrorMock = jest.fn(() => {})
7 | })
8 |
9 | describe('check-options', () => {
10 | const mockedCheck = (pluginOptions) =>
11 | check({
12 | pluginOptions,
13 | handleError: handleErrorMock,
14 | })
15 |
16 | test('should check pluginOptions type', () => {
17 | mockedCheck(undefined)
18 |
19 | expect(handleErrorMock).toHaveBeenLastCalledWith(
20 | 'Plugin options should be specified'
21 | )
22 |
23 | mockedCheck(123)
24 |
25 | expect(handleErrorMock).toHaveBeenLastCalledWith(
26 | 'Plugin options should be an object'
27 | )
28 | })
29 |
30 | test('should not fail on a valid config', () => {
31 | mockedCheck({
32 | aliases: {},
33 | })
34 |
35 | mockedCheck({
36 | source: 'options',
37 | aliases: {},
38 | })
39 |
40 | mockedCheck({
41 | source: 'options',
42 | aliases: {},
43 | })
44 |
45 | mockedCheck({
46 | source: 'tsconfig',
47 | tsConfigPath: 'foo',
48 | })
49 |
50 | mockedCheck({
51 | source: 'jsconfig',
52 | })
53 |
54 | mockedCheck({
55 | aliases: {},
56 | filter: () => true,
57 | })
58 |
59 | mockedCheck({
60 | aliases: {},
61 | unsafeAllowModulesOutsideOfSrc: true,
62 | })
63 |
64 | expect(handleErrorMock).toHaveBeenCalledTimes(0)
65 | })
66 |
67 | test('should check pluginOptions.source', () => {
68 | mockedCheck({
69 | source: 'unknown-source',
70 | })
71 |
72 | const availableSources = ['jsconfig', 'tsconfig', 'options']
73 |
74 | const availableSourcesString = availableSources
75 | .map((s) => `"${s}"`)
76 | .join(', ')
77 |
78 | expect(handleErrorMock).toHaveBeenLastCalledWith(
79 | 'You have provided an invalid aliases source.' +
80 | ` Available sources are: ${availableSourcesString}`
81 | )
82 | })
83 |
84 | test('should check "tsConfigPath" when source is "tsconfig"', () => {
85 | mockedCheck({
86 | source: 'tsconfig',
87 | })
88 |
89 | expect(handleErrorMock).toHaveBeenLastCalledWith(
90 | 'The "source" option is set to "tsconfig",' +
91 | ' but option "tsConfigPath" is missing or has incorrect value'
92 | )
93 | })
94 |
95 | test('should check "baseUrl" when source is "options"', () => {
96 | mockedCheck({
97 | source: 'options',
98 | baseUrl: 345345,
99 | })
100 |
101 | expect(handleErrorMock).toHaveBeenLastCalledWith(
102 | 'The "baseUrl" option should be a string'
103 | )
104 | })
105 |
106 | test('should check "aliases" when source is "options"', () => {
107 | mockedCheck({
108 | source: 'options',
109 | })
110 |
111 | expect(handleErrorMock).toHaveBeenLastCalledWith(
112 | 'The "source" option is set to "options",' +
113 | ' but option "aliases" is missing or has incorrect value'
114 | )
115 |
116 | mockedCheck({
117 | source: 'options',
118 | aliases: 123,
119 | })
120 |
121 | expect(handleErrorMock).toHaveBeenLastCalledWith(
122 | 'The "source" option is set to "options",' +
123 | ' but option "aliases" is missing or has incorrect value'
124 | )
125 | })
126 |
127 | test('should check "filter"', () => {
128 | mockedCheck({
129 | source: 'options',
130 | aliases: {},
131 | filter: 35345,
132 | })
133 |
134 | expect(handleErrorMock).toHaveBeenLastCalledWith(
135 | 'The "filter" option should be a function'
136 | )
137 | })
138 |
139 | test('should check "unsafeAllowModulesOutsideOfSrc"', () => {
140 | mockedCheck({
141 | source: 'options',
142 | aliases: {},
143 | unsafeAllowModulesOutsideOfSrc: 35345,
144 | })
145 |
146 | expect(handleErrorMock).toHaveBeenLastCalledWith(
147 | 'The "unsafeAllowModulesOutsideOfSrc" option should be a boolean value'
148 | )
149 | })
150 |
151 | test('should check "debug"', () => {
152 | mockedCheck({
153 | source: 'options',
154 | aliases: {},
155 | debug: 35345,
156 | })
157 |
158 | expect(handleErrorMock).toHaveBeenLastCalledWith(
159 | 'The "debug" option should be a boolean value'
160 | )
161 | })
162 | })
163 |
--------------------------------------------------------------------------------
/plugin/pre-check/index.js:
--------------------------------------------------------------------------------
1 | const normalizePluginOptions = require('../normalize-plugin-options')
2 | const exitWithError = require('../exit-with-error')
3 | const checkOptions = require('./check-options')
4 | const checkConfig = require('./check-config')
5 |
6 | const preCheck = ({ pluginOptions, context: { paths } }) => {
7 | checkOptions({
8 | pluginOptions,
9 | handleError: exitWithError,
10 | })
11 |
12 | const options = normalizePluginOptions(pluginOptions)
13 |
14 | if (options.source === 'jsconfig')
15 | checkConfig({
16 | configName: 'jsconfig',
17 | configPath: paths.appJsConfig,
18 | })
19 |
20 | if (options.source === 'tsconfig')
21 | checkConfig({
22 | configName: 'tsconfig',
23 | configPath: options.tsConfigPath,
24 | })
25 | }
26 |
27 | module.exports = preCheck
28 |
--------------------------------------------------------------------------------