Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use "paths" in tsconfig.json and jsconfig.json #10014

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
30 changes: 30 additions & 0 deletions docusaurus/docs/importing-a-component.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
20 changes: 0 additions & 20 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 35 additions & 8 deletions packages/react-scripts/config/modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(/(\/\*\*)*\/\*$/, '');
}

/**
Expand All @@ -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/(.*)$': '<rootDir>/src/$1',
};
aliases['^src/(.*)$'] = '<rootDir>/src/$1';
}

if (configPaths != null) {
const prefix = `<rootDir>/${baseUrl.replace(/^\.\//, '')}`;

Object.entries(configPaths).forEach(([key, [value]]) => {
aliases[`^${key.replace(/\*/, '(.*)')}`] = `${prefix}/${value.replace(
/\*/,
'$1'
)}`;
});
}

return aliases;
}

function getModules() {
Expand Down
40 changes: 39 additions & 1 deletion packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -165,6 +164,7 @@ function verifyTypeScriptSetup() {
getNewLine: () => os.EOL,
};

const errors = [];
Copy link
Contributor Author

@sheepsteak sheepsteak Nov 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the changes I've made in this file should equally be applied to any jsconfig.json that is being used. However, there is no file that does at the moment. Should we have a verifyJavaScriptSetup.js? 👀

const messages = [];
let appTsConfig;
let parsedTsConfig;
Expand Down Expand Up @@ -260,6 +260,44 @@ function verifyTypeScriptSetup() {
);
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should also probably warn if a user for whatever reason decides to use more than one path (eg: ["components/*", "layouts/*"]). What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I've just done this. I tried implementing multiple path support first but it seems like Webpack 4 does not support this. Webpack 5 does though so when CRA moves to that we can add this feature.

I've now created an errors array that I push strings into. I can't use the current messages array as they are just outputted as information that your tsconfig.json has been updated. If the baseUrl or paths are set up incorrectly I need to process.exit(1) as we shouldn't go any further.

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);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to allow for the fact that TypeScript 4.1 does not need baseUrl to use paths.


if (messages.length > 0) {
if (firstTimeSetup) {
console.log(
Expand Down