diff --git a/docusaurus/docs/adding-typescript.md b/docusaurus/docs/adding-typescript.md index 2e0b6a3c974..9eabb4f74b7 100644 --- a/docusaurus/docs/adding-typescript.md +++ b/docusaurus/docs/adding-typescript.md @@ -10,31 +10,12 @@ Recent versions of [TypeScript](https://www.typescriptlang.org/) work with Creat To add TypeScript to a Create React App project, follow these steps: 1. Run `npm install --save typescript @types/react @types/react-dom @types/jest` (or `yarn add typescript @types/react @types/react-dom @types/jest`). -2. Rename the `.js` files you want to convert: use `.tsx` if they use JSX or `.ts` if not (e.g. `git mv src/index.js src/index.tsx`). - -3. Create a [`tsconfig.json` file](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) at the root directory with the following content: - -```json -{ - "compilerOptions": { - "target": "es5", - "module": "esnext", - "moduleResolution": "node", - "lib": ["esnext", "dom", "dom.iterable"], - "allowJs": true, - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "isolatedModules": true, - "jsx": "preserve", - "noEmit": true, - "skipLibCheck": true, - "strict": true - }, - "include": ["src"] -} -``` - -4. Copy [loaders.d.ts](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/src/loaders.d.ts) from the template to your `src` directory. + +2. Rename the `.js` files you want to convert: use `.tsx` if they use JSX or `.ts` if not (e.g. `git mv src/index.js src/index.tsx`). **Note that the entrypoint must be named either `src/index.ts` or `src/index.tsx` to enable TypeScript in your project.** + +3. Copy [loaders.d.ts](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/src/loaders.d.ts) from the template to your `src` directory. + +4. Run `npm start` (or `yarn start`) and a `tsconfig.json` file will be generated and placed at the root of your project with the appropriate config required by Create React App. Type errors will show up in the same console as the build one. diff --git a/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js b/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js index 4b16a0b5a48..40b47a30423 100644 --- a/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js +++ b/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js @@ -11,20 +11,33 @@ const chalk = require('chalk'); const fs = require('fs'); const resolve = require('resolve'); +const path = require('path'); const paths = require('../../config/paths'); function verifyTypeScriptSetup() { - if (!fs.existsSync(paths.appTsConfig)) { + if (!paths.appIndexJs.endsWith('.ts') || !paths.appIndexJs.endsWith('.tsx')) { return; } + if (!fs.existsSync(paths.appTsConfig)) { + fs.writeFileSync( + paths.appTsConfig, + JSON.stringify(defaultTSConfig, null, 2), + 'utf-8' + ); + } + const isYarn = fs.existsSync(paths.yarnLockFile); - // Ensure typescript is installed - try { - resolve.sync('typescript', { + function findTypeScript() { + return resolve.sync('typescript', { basedir: paths.appNodeModules, }); + } + + // Ensure typescript is installed + try { + findTypeScript(); } catch (_) { console.error( chalk.red( @@ -53,6 +66,82 @@ function verifyTypeScriptSetup() { console.error(); process.exit(1); } + + const ts = require(findTypeScript()); + // Read the contents of the config file + // Note that this function doesn't merge in "extends" config + const result = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile); + + if (result.error) { + console.error( + 'There was an error while trying to parse your ' + + chalk.cyan('tsconfig.json') + + ' file. Please ensure the contents are valid.' + ); + console.error(); + console.error(chalk.red(result.error.messageText)); + console.error(); + process.exit(1); + } + + // Parse json, merge config extending others, ... + const parsedResult = ts.parseJsonConfigFileContent( + result.config, + ts.sys, + // TODO: paths.appPath is '/packages/react-scripts' when developing. Can we do something cleaner? + // A root directory to resolve relative path entries in the config + path.resolve(paths.appSrc, '..') + ); + + if (parsedResult.errors.length) { + console.error( + 'There were errors while trying to parse your ' + + chalk.cyan('tsconfig.json') + + ' file. Please ensure the contents are valid.' + ); + console.error(); + + for (const error of parsedResult.errors) { + console.error(chalk.red(error.messageText)); + console.error(); + } + + process.exit(1); + } + + const compilerOptions = parsedResult.options; + + if (compilerOptions.isolatedModules !== true) { + console.error( + 'We detected that ' + + chalk.bold('isolatedModules') + + ' was not set to ' + + chalk.bold('true') + + ' in your ' + + chalk.cyan('tsconfig.json') + + ' file. Please update your configuration and try again.' + ); + console.error(); + process.exit(1); + } } module.exports = verifyTypeScriptSetup; + +const defaultTSConfig = { + compilerOptions: { + target: 'es5', + module: 'esnext', + moduleResolution: 'node', + lib: ['esnext', 'dom', 'dom.iterable'], + allowJs: true, + allowSyntheticDefaultImports: true, + esModuleInterop: true, + isolatedModules: true, + jsx: 'preserve', + noEmit: true, + skipLibCheck: true, + strict: true, + }, + include: ['src'], +};