diff --git a/docusaurus/docs/importing-a-component.md b/docusaurus/docs/importing-a-component.md index 7f23cb9c7d..0331bf767e 100644 --- a/docusaurus/docs/importing-a-component.md +++ b/docusaurus/docs/importing-a-component.md @@ -73,4 +73,34 @@ Now that you've configured your project to support absolute imports, if you want import Button from 'components/Button'; ``` +### Path Mapping + +If you require more fine-grained import paths you can set up extra path mappings. This enables you to create shorter import paths to file locations that may normally require long paths. Below is an example `jsconfig.json` showing how you could do this: + +```json +{ + "compilerOptions": { + "baseUrl": "src", + "paths": { + "base/*": ["./components/base/*"], + "pages/*": ["./components/pages/*"], + "actions/*": ["./state/actions/*"] + } + }, + "include": ["src"] +} +``` + +> Even though `jsconfig.json` and `tsconfig.json` allow using multiple locations as "fallbacks", this feature is not currently available in `create-react-app`. + +Setting up your `jsconfig.json` or `tsconfig.json` as above enables you to do imports like this: + +```js +import Button from 'components/Button'; +import MainPage from 'pages/Main'; +import addUser from 'actions/addUser'; +``` + +The import for `Button` still works as the `baseUrl` is still set as `src`. However, we now have more paths available to reach modules that may be quite a few folders deep in our project. This may be useful for larger projects that have more elaborate filesystem layouts. + For more information on these configuration files, see the [jsconfig.json reference](https://code.visualstudio.com/docs/languages/jsconfig) and [tsconfig.json reference](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) documentation. diff --git a/package-lock.json b/package-lock.json index ea5b4f049f..533b9f1d6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29948,15 +29948,6 @@ "cross-env": "^7.0.3", "eslint": "^8.3.0", "eslint-config-react-app": "^7.0.0", -<<<<<<< HEAD -======= -======= -<<<<<<< HEAD ->>>>>>> f21e2137 (Publish) -======= ->>>>>>> 9f8d75e5 (chore(lint): lint all files) ->>>>>>> fb003998 (chore(lint): lint all files) ->>>>>>> f301bfe4 (chore(lint): lint all files) "flow-bin": "^0.116.0", "html-entities": "^2.3.2", "jest": "^27.4.3", @@ -47337,19 +47328,8 @@ "chalk": "^4.1.2", "chokidar": "^3.5.2", "cross-env": "^7.0.3", -<<<<<<< HEAD "eslint": "^8.3.0", "eslint-config-react-app": "^7.0.0", -======= -<<<<<<< HEAD -======= -<<<<<<< HEAD - "eslint": "^8.3.0", - "eslint-config-react-app": "^7.0.0", -======= ->>>>>>> 9f8d75e5 (chore(lint): lint all files) ->>>>>>> fb003998 (chore(lint): lint all files) ->>>>>>> f301bfe4 (chore(lint): lint all files) "flow-bin": "^0.116.0", "html-entities": "^2.3.2", "jest": "^27.4.3", diff --git a/packages/react-scripts/config/modules.js b/packages/react-scripts/config/modules.js index 22820993a2..b692d6ee11 100644 --- a/packages/react-scripts/config/modules.js +++ b/packages/react-scripts/config/modules.js @@ -63,19 +63,34 @@ function getAdditionalModulePaths(options = {}) { * @param {*} options */ function getWebpackAliases(options = {}) { - const baseUrl = options.baseUrl; + const { baseUrl, paths: configPaths } = options; if (!baseUrl) { return {}; } + const aliases = {}; const baseUrlResolved = path.resolve(paths.appPath, baseUrl); if (path.relative(paths.appPath, baseUrlResolved) === '') { - return { - src: paths.appSrc, - }; + aliases.src = paths.appSrc; + } + + if (configPaths != null) { + Object.entries(configPaths).forEach(([key, [value]]) => { + aliases[replaceGlobs(key)] = path.resolve( + paths.appPath, + baseUrl, + replaceGlobs(value) + ); + }); } + + return aliases; +} + +function replaceGlobs(path) { + return path.replace(/(\/\*\*)*\/\*$/, ''); } /** @@ -84,19 +99,31 @@ function getWebpackAliases(options = {}) { * @param {*} options */ function getJestAliases(options = {}) { - const baseUrl = options.baseUrl; + const { baseUrl, paths: configPaths } = options; if (!baseUrl) { return {}; } + const aliases = {}; const baseUrlResolved = path.resolve(paths.appPath, baseUrl); if (path.relative(paths.appPath, baseUrlResolved) === '') { - return { - '^src/(.*)$': '/src/$1', - }; + aliases['^src/(.*)$'] = '/src/$1'; } + + if (configPaths != null) { + const prefix = `/${baseUrl.replace(/^\.\//, '')}`; + + Object.entries(configPaths).forEach(([key, [value]]) => { + aliases[`^${key.replace(/\*/, '(.*)')}`] = `${prefix}/${value.replace( + /\*/, + '$1' + )}`; + }); + } + + return aliases; } function getModules() { diff --git a/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js b/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js index cdc2d77a88..a19cd03434 100644 --- a/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js +++ b/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js @@ -156,7 +156,6 @@ function verifyTypeScriptSetup() { : 'react', reason: 'to support the new JSX transform in React 17', }, - paths: { value: undefined, reason: 'aliased imports are not supported' }, }; const formatDiagnosticHost = { @@ -165,6 +164,7 @@ function verifyTypeScriptSetup() { getNewLine: () => os.EOL, }; + const errors = []; const messages = []; let appTsConfig; let parsedTsConfig; @@ -260,6 +260,44 @@ function verifyTypeScriptSetup() { ); } + if (appTsConfig.compilerOptions.paths != null) { + if ( + semver.lt(ts.version, '4.1.0') && + appTsConfig.compilerOptions.baseUrl == null + ) { + errors.push( + `${chalk.cyan('paths')} requires ${chalk.cyan('baseUrl')} to be set` + ); + } + // Webpack 4 cannot support multiple locations + for (const path of Object.keys(appTsConfig.compilerOptions.paths)) { + const values = appTsConfig.compilerOptions.paths[path]; + + if (!Array.isArray(values) || values.length > 1) { + errors.push( + `Each path in ${chalk.cyan( + 'paths' + )} must have an array with only one location` + ); + break; + } + } + } + + if (errors.length > 0) { + console.error( + chalk.bold( + 'The following errors need fixing in your', + chalk.cyan('tsconfig.json') + ) + ); + errors.forEach(error => { + console.error(' - ' + error); + }); + console.error(); + process.exit(1); + } + if (messages.length > 0) { if (firstTimeSetup) { console.log(