Skip to content

Commit

Permalink
Validate tsconfig when using TypeScript (facebook#5524)
Browse files Browse the repository at this point in the history
* Sanity check TypeScript config

* Check more options

* Set all defaults and suggestions

* Update docs

* Update doc notes

* Automatically copy react app declared types to project on start

* Remove note about loaders.d.ts
  • Loading branch information
Timer authored and nate770 committed Oct 30, 2018
1 parent 9b8a11e commit 0fc95dd
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 24 deletions.
26 changes: 2 additions & 24 deletions docusaurus/docs/adding-typescript.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,8 @@ 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.
3. Create a [`tsconfig.json` file](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) at the root directory with `{}` in it.
4. Restart your development server (if applicable). This will set sensible defaults and the required values in your [`tsconfig.json` file](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html).

Type errors will show up in the same console as the build one.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// @remove-file-on-eject
// Do not edit this file. It's replaced every time you launch a toolbox action.
// If you need to add additional declarations, please do so in a new file.

declare module '*.json' {
const value: any;
export default value;
Expand Down
2 changes: 2 additions & 0 deletions packages/react-scripts/scripts/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const verifyPackageTree = require('./utils/verifyPackageTree');
if (process.env.SKIP_PREFLIGHT_CHECK !== 'true') {
verifyPackageTree();
}
const verifyTypeScriptSetup = require('./utils/verifyTypeScriptSetup');
verifyTypeScriptSetup();
// @remove-on-eject-end

const jest = require('jest');
Expand Down
106 changes: 106 additions & 0 deletions packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@
const chalk = require('chalk');
const fs = require('fs');
const resolve = require('resolve');
const path = require('path');
const paths = require('../../config/paths');
const os = require('os');

function writeJson(fileName, object) {
fs.writeFileSync(fileName, JSON.stringify(object, null, 2) + os.EOL);
}

function verifyTypeScriptSetup() {
if (!fs.existsSync(paths.appTsConfig)) {
Expand Down Expand Up @@ -53,6 +59,106 @@ function verifyTypeScriptSetup() {
console.error();
process.exit(1);
}

const messages = [];
let tsconfig;
try {
tsconfig = require(paths.appTsConfig);
} catch (_) {
console.error(
chalk.red.bold(
'Could not parse',
chalk.cyan('tsconfig.json') + '.',
'Please make sure it contains syntactically correct JSON.'
)
);
process.exit(1);
}

if (tsconfig.compilerOptions == null) {
tsconfig.compilerOptions = {};
}

const compilerOptions = {
target: { suggested: 'es5' },
allowJs: { suggested: true },
skipLibCheck: { suggested: true },
module: { value: 'esnext', reason: 'for import() and import/export' },
moduleResolution: { value: 'node', reason: 'to match webpack resolution' },
isolatedModules: { value: true, reason: 'implementation limitation' },
noEmit: { value: true },
jsx: { value: 'preserve', reason: 'JSX is compiled by Babel' },
esModuleInterop: { value: true, reason: 'Babel compatibility' },
allowSyntheticDefaultImports: {
value: true,
reason: 'Babel compatibility',
},
strict: { suggested: true },
};

for (const option of Object.keys(compilerOptions)) {
const { value, suggested, reason } = compilerOptions[option];
if (suggested != null) {
if (tsconfig.compilerOptions[option] === undefined) {
tsconfig.compilerOptions[option] = suggested;
messages.push(
`${chalk.cyan('compilerOptions.' + option)} to be ${chalk.bold(
'suggested'
)} value: ${chalk.cyan.bold(suggested)} (this can be changed)`
);
}
} else if (tsconfig.compilerOptions[option] !== value) {
tsconfig.compilerOptions[option] = value;
messages.push(
`${chalk.cyan('compilerOptions.' + option)} ${chalk.bold(
'must'
)} be ${chalk.cyan.bold(value)}` +
(reason != null ? ` (${reason})` : '')
);
}
}

if (tsconfig.include == null) {
tsconfig.include = ['src'];
messages.push(
`${chalk.cyan('include')} should be ${chalk.cyan.bold('src')}`
);
}
if (tsconfig.exclude == null) {
tsconfig.exclude = ['**/__tests__/**', '**/?*(spec|test).*'];
messages.push(`${chalk.cyan('exclude')} should exclude test files`);
}

if (messages.length > 0) {
console.warn(
chalk.bold(
'The following changes are being made to your',
chalk.cyan('tsconfig.json'),
'file:'
)
);
messages.forEach(message => {
console.warn(' - ' + message);
});
console.warn();
writeJson(paths.appTsConfig, tsconfig);
}

// Copy type declarations associated with this version of `react-scripts`
const declaredTypes = path.resolve(
__dirname,
'..',
'..',
'config',
'react-app.d.ts'
);
const declaredTypesContent = fs
.readFileSync(declaredTypes, 'utf8')
.replace(/\/\/ @remove-file-on-eject\r?\n/, '');
fs.writeFileSync(
path.resolve(paths.appSrc, 'react-app.d.ts'),
declaredTypesContent
);
}

module.exports = verifyTypeScriptSetup;

0 comments on commit 0fc95dd

Please sign in to comment.