From 8ef74f400d3cd83bc069b6517b95205618cd703c Mon Sep 17 00:00:00 2001 From: Bruno Lemos Date: Sun, 29 Jul 2018 16:46:04 -0300 Subject: [PATCH 1/9] Bump babel 7 to beta 56 --- .../package.json | 2 +- packages/babel-preset-react-app/create.js | 1 - packages/babel-preset-react-app/package.json | 26 +++++++++---------- packages/react-dev-utils/package.json | 2 +- packages/react-error-overlay/package.json | 6 ++--- .../kitchensink/.template.dependencies.json | 6 ++--- packages/react-scripts/package.json | 4 +-- 7 files changed, 23 insertions(+), 24 deletions(-) diff --git a/packages/babel-plugin-named-asset-import/package.json b/packages/babel-plugin-named-asset-import/package.json index eec4516f8ac..3a9a8c45d35 100644 --- a/packages/babel-plugin-named-asset-import/package.json +++ b/packages/babel-plugin-named-asset-import/package.json @@ -12,6 +12,6 @@ "index.js" ], "peerDependencies": { - "@babel/core": "7.0.0-beta.46" + "@babel/core": "7.0.0-beta.56" } } diff --git a/packages/babel-preset-react-app/create.js b/packages/babel-preset-react-app/create.js index 2ead09570f6..273446ed4c6 100644 --- a/packages/babel-preset-react-app/create.js +++ b/packages/babel-preset-react-app/create.js @@ -107,7 +107,6 @@ module.exports = function(api, opts, env) { require('@babel/plugin-transform-runtime').default, { helpers: false, - polyfill: false, regenerator: true, }, ], diff --git a/packages/babel-preset-react-app/package.json b/packages/babel-preset-react-app/package.json index 27b70195299..69fb3c86a9a 100644 --- a/packages/babel-preset-react-app/package.json +++ b/packages/babel-preset-react-app/package.json @@ -16,19 +16,19 @@ "test.js" ], "dependencies": { - "@babel/core": "7.0.0-beta.46", - "@babel/plugin-proposal-class-properties": "7.0.0-beta.46", - "@babel/plugin-proposal-object-rest-spread": "7.0.0-beta.46", - "@babel/plugin-syntax-dynamic-import": "7.0.0-beta.46", - "@babel/plugin-transform-classes": "7.0.0-beta.46", - "@babel/plugin-transform-destructuring": "7.0.0-beta.46", - "@babel/plugin-transform-react-constant-elements": "7.0.0-beta.46", - "@babel/plugin-transform-react-display-name": "7.0.0-beta.46", - "@babel/plugin-transform-regenerator": "7.0.0-beta.46", - "@babel/plugin-transform-runtime": "7.0.0-beta.46", - "@babel/preset-env": "7.0.0-beta.46", - "@babel/preset-flow": "7.0.0-beta.46", - "@babel/preset-react": "7.0.0-beta.46", + "@babel/core": "7.0.0-beta.56", + "@babel/plugin-proposal-class-properties": "7.0.0-beta.56", + "@babel/plugin-proposal-object-rest-spread": "7.0.0-beta.56", + "@babel/plugin-syntax-dynamic-import": "7.0.0-beta.56", + "@babel/plugin-transform-classes": "7.0.0-beta.56", + "@babel/plugin-transform-destructuring": "7.0.0-beta.56", + "@babel/plugin-transform-react-constant-elements": "7.0.0-beta.56", + "@babel/plugin-transform-react-display-name": "7.0.0-beta.56", + "@babel/plugin-transform-regenerator": "7.0.0-beta.56", + "@babel/plugin-transform-runtime": "7.0.0-beta.56", + "@babel/preset-env": "7.0.0-beta.56", + "@babel/preset-flow": "7.0.0-beta.56", + "@babel/preset-react": "7.0.0-beta.56", "babel-plugin-macros": "2.2.1", "babel-plugin-transform-dynamic-import": "2.0.0", "babel-plugin-transform-react-remove-prop-types": "0.4.13" diff --git a/packages/react-dev-utils/package.json b/packages/react-dev-utils/package.json index 6e49955e453..86a809ae521 100644 --- a/packages/react-dev-utils/package.json +++ b/packages/react-dev-utils/package.json @@ -38,7 +38,7 @@ "workspaceUtils.js" ], "dependencies": { - "@babel/code-frame": "7.0.0-beta.46", + "@babel/code-frame": "7.0.0-beta.56", "address": "1.0.3", "browserslist": "3.2.6", "chalk": "2.4.1", diff --git a/packages/react-error-overlay/package.json b/packages/react-error-overlay/package.json index 6e7e1f3e885..cb627f1b5b7 100644 --- a/packages/react-error-overlay/package.json +++ b/packages/react-error-overlay/package.json @@ -30,9 +30,9 @@ "lib/index.js" ], "devDependencies": { - "@babel/code-frame": "7.0.0-beta.46", - "@babel/core": "7.0.0-beta.46", - "@babel/runtime": "7.0.0-beta.46", + "@babel/code-frame": "7.0.0-beta.56", + "@babel/core": "7.0.0-beta.56", + "@babel/runtime": "7.0.0-beta.56", "anser": "1.4.6", "babel-core": "^7.0.0-bridge.0", "babel-eslint": "^8.2.2", diff --git a/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json b/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json index 0a42b7d4cd1..37de4733209 100644 --- a/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json +++ b/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json @@ -1,8 +1,8 @@ { "dependencies": { - "@babel/plugin-transform-modules-commonjs": "7.0.0-beta.46", - "@babel/polyfill": "7.0.0-beta.46", - "@babel/register": "7.0.0-beta.46", + "@babel/plugin-transform-modules-commonjs": "7.0.0-beta.56", + "@babel/polyfill": "7.0.0-beta.56", + "@babel/register": "7.0.0-beta.56", "bootstrap": "4.1.0", "chai": "3.5.0", "jsdom": "9.8.3", diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index 60fe099d74b..a5ff73ed8b2 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -21,8 +21,8 @@ "react-scripts": "./bin/react-scripts.js" }, "dependencies": { - "@babel/core": "7.0.0-beta.46", - "@babel/runtime": "7.0.0-beta.46", + "@babel/core": "7.0.0-beta.56", + "@babel/runtime": "7.0.0-beta.56", "autoprefixer": "8.5.0", "babel-core": "7.0.0-bridge.0", "babel-eslint": "8.2.3", From 9df90ef67fe14625ac4c5cb81dc168740cd5a21f Mon Sep 17 00:00:00 2001 From: Bruno Lemos Date: Sun, 29 Jul 2018 18:02:35 -0300 Subject: [PATCH 2/9] [TypeScript] TypeScript support --- README.md | 3 +- packages/babel-preset-react-app/README.md | 21 +++--- packages/babel-preset-react-app/create.js | 6 ++ packages/babel-preset-react-app/package.json | 1 + packages/react-error-overlay/.babelrc | 2 +- .../webpack.config.iframe.js | 4 +- .../react-error-overlay/webpack.config.js | 2 +- packages/react-scripts/config/paths.js | 27 +++++-- .../config/webpack.config.dev.js | 25 +++++-- .../config/webpack.config.prod.js | 21 ++++-- .../fixtures/kitchensink/.flowconfig | 8 -- .../scripts/utils/createJestConfig.js | 16 ++-- packages/react-scripts/template/README.md | 75 ++++++++++++++++++- .../react-scripts/template/src/App.test.js | 2 + 14 files changed, 164 insertions(+), 49 deletions(-) delete mode 100644 packages/react-scripts/fixtures/kitchensink/.flowconfig diff --git a/README.md b/README.md index ee9deb69693..2e02d5ba0f4 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,7 @@ The [User Guide](https://github.com/facebook/create-react-app/blob/master/packag - [Using Global Variables](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#using-global-variables) - [Adding Bootstrap](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-bootstrap) - [Adding Flow](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-flow) +- [Adding TypeScript](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-typescript) - [Adding a Router](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-a-router) - [Adding Custom Environment Variables](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-custom-environment-variables) - [Can I Use Decorators?](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#can-i-use-decorators) @@ -216,7 +217,7 @@ Here’s a few common cases where you might want to try something else: * If your website is **mostly static** (for example, a portfolio or a blog), consider using [Gatsby](https://www.gatsbyjs.org/) instead. Unlike Create React App, it pre-renders the website into HTML at the build time. -* If you want to use **TypeScript**, consider using [create-react-app-typescript](https://github.com/wmonk/create-react-app-typescript). +* If you want an alternative way to use **TypeScript**, consider using [create-react-app-typescript](https://github.com/wmonk/create-react-app-typescript). * If you want to use **Parcel** instead of **Webpack** as your bundler, consider using [create-react-app-parcel](https://github.com/sw-yx/create-react-app-parcel). diff --git a/packages/babel-preset-react-app/README.md b/packages/babel-preset-react-app/README.md index dbf7c600515..43f027f7059 100644 --- a/packages/babel-preset-react-app/README.md +++ b/packages/babel-preset-react-app/README.md @@ -32,19 +32,22 @@ Then create a file named `.babelrc` with following contents in the root folder o This preset uses the `useBuiltIns` option with [transform-object-rest-spread](http://babeljs.io/docs/plugins/transform-object-rest-spread/) and [transform-react-jsx](http://babeljs.io/docs/plugins/transform-react-jsx/), which assumes that `Object.assign` is available or polyfilled. -## Usage with TypeScript +## Usage with Flow + +To use this package with [`@babel/preset-flow`]https://www.npmjs.com/package/@babel/preset-flow), edit `.babelrc` to the following: + +``` +{ + "presets": [["react-app", { "flow": true }]] +} +``` -To use this package with [`@babel/preset-typescript`](https://www.npmjs.com/package/@babel/preset-typescript), you need to disable `@babel/preset-flow` first. +## Usage with TypeScript -You can achieve this by doing: +To use this package with [`@babel/preset-typescript`]https://www.npmjs.com/package/@babel/preset-typescript), edit `.babelrc` to the following: ``` { - "presets": [ - ["react-app", { - "flow": false - }], - "@babel/typescript" - ] + "presets": [["react-app", { "typescript": true }]] } ``` diff --git a/packages/babel-preset-react-app/create.js b/packages/babel-preset-react-app/create.js index 273446ed4c6..1301257f0b1 100644 --- a/packages/babel-preset-react-app/create.js +++ b/packages/babel-preset-react-app/create.js @@ -27,6 +27,11 @@ module.exports = function(api, opts, env) { var isEnvProduction = env === 'production'; var isEnvTest = env === 'test'; var isFlowEnabled = validateBoolOption('flow', opts.flow, true); + var isTypeScriptEnabled = validateBoolOption( + 'typescript', + opts.typescript, + true + ); if (!isEnvDevelopment && !isEnvProduction && !isEnvTest) { throw new Error( @@ -75,6 +80,7 @@ module.exports = function(api, opts, env) { }, ], isFlowEnabled && [require('@babel/preset-flow').default], + isTypeScriptEnabled && [require('@babel/preset-typescript').default], ].filter(Boolean), plugins: [ // Experimental macros support. Will be documented after it's had some time diff --git a/packages/babel-preset-react-app/package.json b/packages/babel-preset-react-app/package.json index 69fb3c86a9a..3ed9cf3b730 100644 --- a/packages/babel-preset-react-app/package.json +++ b/packages/babel-preset-react-app/package.json @@ -29,6 +29,7 @@ "@babel/preset-env": "7.0.0-beta.56", "@babel/preset-flow": "7.0.0-beta.56", "@babel/preset-react": "7.0.0-beta.56", + "@babel/preset-typescript": "7.0.0-beta.56", "babel-plugin-macros": "2.2.1", "babel-plugin-transform-dynamic-import": "2.0.0", "babel-plugin-transform-react-remove-prop-types": "0.4.13" diff --git a/packages/react-error-overlay/.babelrc b/packages/react-error-overlay/.babelrc index c14b2828d16..ea303b756a3 100644 --- a/packages/react-error-overlay/.babelrc +++ b/packages/react-error-overlay/.babelrc @@ -1,3 +1,3 @@ { - "presets": ["react-app"] + "presets": [["react-app", { "flow": true }]] } diff --git a/packages/react-error-overlay/webpack.config.iframe.js b/packages/react-error-overlay/webpack.config.iframe.js index 3a806f362db..4552c9e34d8 100644 --- a/packages/react-error-overlay/webpack.config.iframe.js +++ b/packages/react-error-overlay/webpack.config.iframe.js @@ -23,7 +23,7 @@ module.exports = { oneOf: [ // Source { - test: /\.js$/, + test: /\.(js)|(tsx?)$/, include: [path.resolve(__dirname, './src')], use: { loader: 'babel-loader', @@ -31,7 +31,7 @@ module.exports = { }, // Dependencies { - test: /\.js$/, + test: /\.(js)|(tsx?)$/, use: { loader: 'babel-loader', options: { diff --git a/packages/react-error-overlay/webpack.config.js b/packages/react-error-overlay/webpack.config.js index dce51e0f9e9..498207e6eaa 100644 --- a/packages/react-error-overlay/webpack.config.js +++ b/packages/react-error-overlay/webpack.config.js @@ -24,7 +24,7 @@ module.exports = { use: 'raw-loader', }, { - test: /\.js$/, + test: /\.(js)|(tsx?)$/, include: path.resolve(__dirname, './src'), use: 'babel-loader', }, diff --git a/packages/react-scripts/config/paths.js b/packages/react-scripts/config/paths.js index 32665e9b76c..c3e30d436d3 100644 --- a/packages/react-scripts/config/paths.js +++ b/packages/react-scripts/config/paths.js @@ -47,6 +47,9 @@ function getServedPath(appPackageJson) { return ensureSlash(servedUrl, true); } +const isFlow = fs.existsSync(resolveApp('.flowconfig')); +const isTypeScript = fs.existsSync(resolveApp('tsconfig.json')); + // config after eject: we're in ./config/ module.exports = { dotenv: resolveApp('.env'), @@ -54,10 +57,12 @@ module.exports = { appBuild: resolveApp('build'), appPublic: resolveApp('public'), appHtml: resolveApp('public/index.html'), - appIndexJs: resolveApp('src/index.js'), + appIndexJs: resolveApp(isTypeScript ? 'src/index.tsx' : 'src/index.js'), appPackageJson: resolveApp('package.json'), appSrc: resolveApp('src'), - testsSetup: resolveApp('src/setupTests.js'), + testsSetup: resolveApp( + isTypeScript ? 'src/setupTests.ts' : 'src/setupTests.js' + ), appNodeModules: resolveApp('node_modules'), publicUrl: getPublicUrl(resolveApp('package.json')), servedPath: getServedPath(resolveApp('package.json')), @@ -75,10 +80,12 @@ module.exports = { appBuild: resolveApp('build'), appPublic: resolveApp('public'), appHtml: resolveApp('public/index.html'), - appIndexJs: resolveApp('src/index.js'), + appIndexJs: resolveApp(isTypeScript ? 'src/index.tsx' : 'src/index.js'), appPackageJson: resolveApp('package.json'), appSrc: resolveApp('src'), - testsSetup: resolveApp('src/setupTests.js'), + testsSetup: resolveApp( + isTypeScript ? 'src/setupTests.ts' : 'src/setupTests.js' + ), appNodeModules: resolveApp('node_modules'), publicUrl: getPublicUrl(resolveApp('package.json')), servedPath: getServedPath(resolveApp('package.json')), @@ -100,10 +107,14 @@ if (useTemplate) { appBuild: resolveOwn('../../build'), appPublic: resolveOwn('template/public'), appHtml: resolveOwn('template/public/index.html'), - appIndexJs: resolveOwn('template/src/index.js'), + appIndexJs: resolveOwn( + isTypeScript ? 'template/src/index.tsx' : 'template/src/index.js' + ), appPackageJson: resolveOwn('package.json'), appSrc: resolveOwn('template/src'), - testsSetup: resolveOwn('template/src/setupTests.js'), + testsSetup: resolveOwn( + isTypeScript ? 'template/src/setupTests.ts' : 'template/src/setupTests.js' + ), appNodeModules: resolveOwn('node_modules'), publicUrl: getPublicUrl(resolveOwn('package.json')), servedPath: getServedPath(resolveOwn('package.json')), @@ -116,6 +127,10 @@ if (useTemplate) { module.exports.srcPaths = [module.exports.appSrc]; +module.exports.isFlow = isFlow; + +module.exports.isTypeScript = isTypeScript; + module.exports.useYarn = fs.existsSync( path.join(module.exports.appPath, 'yarn.lock') ); diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index db371dda6e8..511ec848e22 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -76,7 +76,7 @@ const getStyleLoaders = (cssOptions, preProcessor) => { module.exports = { mode: 'development', // You may want 'eval' instead if you prefer to see the compiled output in DevTools. - // See the discussion in https://github.com/facebook/create-react-app/issues/343 + // See the discussion in https://github.com/facebook/create-react-app/issues/343. devtool: 'cheap-module-source-map', // These are the "entry points" to our application. // This means they will be the "root" imports that are included in JS bundle. @@ -143,7 +143,18 @@ module.exports = { // https://github.com/facebook/create-react-app/issues/290 // `web` extension prefixes have been added for better support // for React Native Web. - extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'], + extensions: [ + '.web.js', + '.mjs', + '.js', + '.json', + '.web.jsx', + '.jsx', + '.web.ts', + '.web.tsx', + '.ts', + '.tsx', + ], alias: { // @remove-on-eject-begin // Resolve Babel runtime relative to react-scripts. @@ -216,7 +227,7 @@ module.exports = { // Process application JS with Babel. // The preset includes JSX, Flow, and some ESnext features. { - test: /\.(js|jsx|mjs)$/, + test: /\.(js|jsx|mjs|tsx?)$/, include: paths.srcPaths, exclude: [/[/\\\\]node_modules[/\\\\]/], use: [ @@ -225,7 +236,7 @@ module.exports = { { loader: require.resolve('thread-loader'), options: { - poolTimeout: Infinity // keep workers alive for more effective watch mode + poolTimeout: Infinity, // keep workers alive for more effective watch mode }, }, { @@ -259,14 +270,14 @@ module.exports = { // Process any JS outside of the app with Babel. // Unlike the application JS, we only compile the standard ES features. { - test: /\.js$/, + test: /\.(js)|(tsx?)$/, use: [ // This loader parallelizes code compilation, it is optional but // improves compile time on larger projects { loader: require.resolve('thread-loader'), options: { - poolTimeout: Infinity // keep workers alive for more effective watch mode + poolTimeout: Infinity, // keep workers alive for more effective watch mode }, }, { @@ -344,7 +355,7 @@ module.exports = { // its runtime that would otherwise be processed through "file" loader. // Also exclude `html` and `json` extensions so they get processed // by webpacks internal loaders. - exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/], + exclude: [/\.(js|jsx|mjs|tsx?)$/, /\.html$/, /\.json$/], loader: require.resolve('file-loader'), options: { name: 'static/media/[name].[hash:8].[ext]', diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js index 86869748c66..6b84754f58f 100644 --- a/packages/react-scripts/config/webpack.config.prod.js +++ b/packages/react-scripts/config/webpack.config.prod.js @@ -181,7 +181,18 @@ module.exports = { // https://github.com/facebook/create-react-app/issues/290 // `web` extension prefixes have been added for better support // for React Native Web. - extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'], + extensions: [ + '.web.js', + '.mjs', + '.js', + '.json', + '.web.jsx', + '.jsx', + '.web.ts', + '.web.tsx', + '.ts', + '.tsx', + ], alias: { // @remove-on-eject-begin // Resolve Babel runtime relative to react-scripts. @@ -253,9 +264,9 @@ module.exports = { }, }, // Process application JS with Babel. - // The preset includes JSX, Flow, and some ESnext features. + // The preset includes JSX, Flow, TypeScript and some ESnext features. { - test: /\.(js|jsx|mjs)$/, + test: /\.(js|jsx|mjs|tsx?)$/, include: paths.srcPaths, exclude: [/[/\\\\]node_modules[/\\\\]/], use: [ @@ -290,7 +301,7 @@ module.exports = { // Process any JS outside of the app with Babel. // Unlike the application JS, we only compile the standard ES features. { - test: /\.js$/, + test: /\.(js)|(tsx?)$/, use: [ // This loader parallelizes code compilation, it is optional but // improves compile time on larger projects @@ -378,7 +389,7 @@ module.exports = { // it's runtime that would otherwise be processed through "file" loader. // Also exclude `html` and `json` extensions so they get processed // by webpacks internal loaders. - exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/], + exclude: [/\.(js|jsx|mjs|tsx?)$/, /\.html$/, /\.json$/], options: { name: 'static/media/[name].[hash:8].[ext]', }, diff --git a/packages/react-scripts/fixtures/kitchensink/.flowconfig b/packages/react-scripts/fixtures/kitchensink/.flowconfig deleted file mode 100644 index c658362285f..00000000000 --- a/packages/react-scripts/fixtures/kitchensink/.flowconfig +++ /dev/null @@ -1,8 +0,0 @@ -[ignore] -/node_modules/fbjs/.* - -[include] - -[libs] - -[options] diff --git a/packages/react-scripts/scripts/utils/createJestConfig.js b/packages/react-scripts/scripts/utils/createJestConfig.js index e884518d73a..f95ddfdae78 100644 --- a/packages/react-scripts/scripts/utils/createJestConfig.js +++ b/packages/react-scripts/scripts/utils/createJestConfig.js @@ -24,27 +24,27 @@ module.exports = (resolve, rootDir, srcRoots) => { // TODO: I don't know if it's safe or not to just use / as path separator // in Jest configs. We need help from somebody with Windows to determine this. const config = { - collectCoverageFrom: ['src/**/*.{js,jsx,mjs}'], + collectCoverageFrom: ['src/**/*.{js,jsx,mjs,ts,tsx}'], setupFiles: [resolve('config/polyfills.js')], setupTestFrameworkScriptFile: setupTestsFile, testMatch: [ - '**/__tests__/**/*.{js,jsx,mjs}', - '**/?(*.)(spec|test).{js,jsx,mjs}', + '**/__tests__/**/*.{js,jsx,mjs,ts,tsx}', + '**/?(*.)(spec|test).{js,jsx,mjs,ts,tsx}', ], // where to search for files/tests roots: srcRoots.map(toRelRootDir), testEnvironment: 'node', testURL: 'http://localhost', transform: { - '^.+\\.(js|jsx|mjs)$': resolve('config/jest/babelTransform.js'), + '^.+\\.(js|jsx|mjs|tsx?)$': resolve('config/jest/babelTransform.js'), '^.+\\.css$': resolve('config/jest/cssTransform.js'), '^.+\\.(graphql)$': resolve('config/jest/graphqlTransform.js'), - '^(?!.*\\.(js|jsx|mjs|css|json|graphql)$)': resolve( + '^(?!.*\\.(js|jsx|mjs|ts|tsx|css|json|graphql)$)': resolve( 'config/jest/fileTransform.js' ), }, transformIgnorePatterns: [ - '[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs)$', + '[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|tsx?)$', '^.+\\.module\\.(css|sass|scss)$', ], moduleNameMapper: { @@ -59,6 +59,10 @@ module.exports = (resolve, rootDir, srcRoots) => { 'jsx', 'node', 'mjs', + 'web.ts', + 'ts', + 'web.tsx', + 'tsx', ], }; if (rootDir) { diff --git a/packages/react-scripts/template/README.md b/packages/react-scripts/template/README.md index e60278e9cce..cbb1d84eb25 100644 --- a/packages/react-scripts/template/README.md +++ b/packages/react-scripts/template/README.md @@ -36,6 +36,7 @@ You can find the most recent version of this guide [here](https://github.com/fac - [Adding Bootstrap](#adding-bootstrap) - [Using a Custom Theme](#using-a-custom-theme) - [Adding Flow](#adding-flow) +- [Adding TypeScript](#adding-typescript) - [Adding a Router](#adding-a-router) - [Adding Custom Environment Variables](#adding-custom-environment-variables) - [Referencing Environment Variables in the HTML](#referencing-environment-variables-in-the-html) @@ -344,7 +345,7 @@ Next we add a 'lint-staged' field to the `package.json`, for example: // ... }, + "lint-staged": { -+ "src/**/*.{js,jsx,json,css}": [ ++ "src/**/*.{js,jsx,ts,tsx,json,css}": [ + "prettier --single-quote --write", + "git add" + ] @@ -352,7 +353,7 @@ Next we add a 'lint-staged' field to the `package.json`, for example: "scripts": { ``` -Now, whenever you make a commit, Prettier will format the changed files automatically. You can also run `./node_modules/.bin/prettier --single-quote --write "src/**/*.{js,jsx}"` to format your entire project for the first time. +Now, whenever you make a commit, Prettier will format the changed files automatically. You can also run `./node_modules/.bin/prettier --single-quote --write "src/**/*.{js,jsx,ts,tsx}"` to format your entire project for the first time. Next you might want to integrate Prettier in your favorite editor. Read the section on [Editor Integration](https://prettier.io/docs/en/editors.html) on the Prettier GitHub page. @@ -894,6 +895,13 @@ To add Flow to a Create React App project, follow these steps: 2. Add `"flow": "flow"` to the `scripts` section of your `package.json`. 3. Run `npm run flow init` (or `yarn flow init`) to create a [`.flowconfig` file](https://flowtype.org/docs/advanced-configuration.html) in the root directory. 4. Add `// @flow` to any files you want to type check (for example, to `src/App.js`). +5. Edit `.babelrc` with the following content: + +``` +{ + "presets": [["react-app", { "flow": true }]] +} +``` Now you can run `npm run flow` (or `yarn flow`) to check the files for type errors. You can optionally use an IDE like [Nuclide](https://nuclide.io/docs/languages/flow/) for a better integrated experience. @@ -901,6 +909,67 @@ In the future we plan to integrate it into Create React App even more closely. To learn more about Flow, check out [its documentation](https://flowtype.org/). +## Adding TypeScript + +TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. + +Recent versions of [TypeScript](https://www.typescriptlang.org/) work with Create React App projects out of the box thanks to Babel 7. Beware that Babel 7 TypeScript does not allow some features of TypeScript such as constant enum and namespaces. + +To add TypeScript to a Create React App project, follow these steps: + +1. Run `npm install --dev typescript @types/react @types/react-dom` (or `yarn add --dev typescript @types/react @types/react-dom`). +2. Add `"type-check": "tsc"` to the `scripts` section of your `package.json`. +2. Rename all `.js` files to `.tsx` if they have React components or `.ts` if not (e.g. `git mv src/index.js src/index.tsx`). +4. Edit `.babelrc` with the following content: + +``` +{ + "presets": [["react-app", { "typescript": true }]] +} +``` + +5. Add a `tsconfig.json` file with this content: + +```json +{ + "compilerOptions": { + "target": "ESNEXT", + "module": "ESNext", + "lib": ["es2017", "dom"], + "allowJs": true, + "skipLibCheck": true, + "jsx": "react", + "sourceMap": true, + "noEmit": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "moduleResolution": "node", + "baseUrl": "./", + "paths": { + "*": [ + "*.web", + "*" + ] + }, + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true + } +} +``` + +Now you can run `npm run type-check` (or `yarn type-check`) to check the files for type errors. +We recommend using [VSCode](https://code.visualstudio.com/) for a better integrated experience. + +To learn more about TypeScript, check out [its documentation](https://www.typescriptlang.org/). + ## Adding a Router Create React App doesn't prescribe a specific routing solution, but [React Router](https://reacttraining.com/react-router/) is the most popular one. @@ -1579,7 +1648,7 @@ Example package.json: "name": "your-package", "jest": { "collectCoverageFrom" : [ - "src/**/*.{js,jsx}", + "src/**/*.{js,jsx,ts,tsx}", "!/node_modules/", "!/path/to/dir/" ], diff --git a/packages/react-scripts/template/src/App.test.js b/packages/react-scripts/template/src/App.test.js index a754b201bf9..836c26f0367 100644 --- a/packages/react-scripts/template/src/App.test.js +++ b/packages/react-scripts/template/src/App.test.js @@ -1,5 +1,7 @@ +import 'jest'; import React from 'react'; import ReactDOM from 'react-dom'; + import App from './App'; it('renders without crashing', () => { From 78b6da21c6953bdafc0076ee65eeecceba481080 Mon Sep 17 00:00:00 2001 From: Bruno Lemos Date: Sun, 29 Jul 2018 19:25:48 -0300 Subject: [PATCH 3/9] [TypeScript] Automatically detect .flowconfig and tsconfig.json And prevents enabling both flow and typescript in the same project --- packages/babel-preset-react-app/README.md | 5 ++- packages/babel-preset-react-app/create.js | 21 ++++++++++- packages/react-scripts/template/README.md | 45 ++++++++++++++--------- 3 files changed, 49 insertions(+), 22 deletions(-) diff --git a/packages/babel-preset-react-app/README.md b/packages/babel-preset-react-app/README.md index 43f027f7059..5ef733fccee 100644 --- a/packages/babel-preset-react-app/README.md +++ b/packages/babel-preset-react-app/README.md @@ -34,7 +34,7 @@ This preset uses the `useBuiltIns` option with [transform-object-rest-spread](ht ## Usage with Flow -To use this package with [`@babel/preset-flow`]https://www.npmjs.com/package/@babel/preset-flow), edit `.babelrc` to the following: +To use this package with [`@babel/preset-flow`]https://www.npmjs.com/package/@babel/preset-flow), you only need to have a `.flowconfig` file at the root directory and it will be detected automatically. Alternatively, you can edit `.babelrc` with the following: ``` { @@ -42,9 +42,10 @@ To use this package with [`@babel/preset-flow`]https://www.npmjs.com/package/@ba } ``` + ## Usage with TypeScript -To use this package with [`@babel/preset-typescript`]https://www.npmjs.com/package/@babel/preset-typescript), edit `.babelrc` to the following: +To use this package with [`@babel/preset-typescript`]https://www.npmjs.com/package/@babel/preset-typescript), you only need to have a `tsconfig.json` file at the root directory and it will be detected automatically. Alternatively, you can edit `.babelrc` with the following: ``` { diff --git a/packages/babel-preset-react-app/create.js b/packages/babel-preset-react-app/create.js index 1301257f0b1..7903a9e22ed 100644 --- a/packages/babel-preset-react-app/create.js +++ b/packages/babel-preset-react-app/create.js @@ -6,6 +6,15 @@ */ 'use strict'; +const path = require('path'); +const fs = require('fs'); + +const appDirectory = fs.realpathSync(process.cwd()); +const resolveApp = relativePath => path.resolve(appDirectory, relativePath); + +const hasFlowConfig = fs.existsSync(resolveApp('.flowconfig')); +const hasTSConfig = fs.existsSync(resolveApp('tsconfig.json')); + const validateBoolOption = (name, value, defaultValue) => { if (typeof value === 'undefined') { value = defaultValue; @@ -26,11 +35,11 @@ module.exports = function(api, opts, env) { var isEnvDevelopment = env === 'development'; var isEnvProduction = env === 'production'; var isEnvTest = env === 'test'; - var isFlowEnabled = validateBoolOption('flow', opts.flow, true); + var isFlowEnabled = validateBoolOption('flow', opts.flow, hasFlowConfig); var isTypeScriptEnabled = validateBoolOption( 'typescript', opts.typescript, - true + hasTSConfig ); if (!isEnvDevelopment && !isEnvProduction && !isEnvTest) { @@ -43,6 +52,14 @@ module.exports = function(api, opts, env) { ); } + if (isFlowEnabled && isTypeScriptEnabled) { + throw new Error( + 'Cannot enable both flow and typescript support in the same project. ' + + "Please make sure you don't have both .flowconfig and tsconfig.json files. '" + + 'If using .babelrc, set one option to false and the other one to true.' + ); + } + return { presets: [ isEnvTest && [ diff --git a/packages/react-scripts/template/README.md b/packages/react-scripts/template/README.md index cbb1d84eb25..28303c36864 100644 --- a/packages/react-scripts/template/README.md +++ b/packages/react-scripts/template/README.md @@ -895,13 +895,6 @@ To add Flow to a Create React App project, follow these steps: 2. Add `"flow": "flow"` to the `scripts` section of your `package.json`. 3. Run `npm run flow init` (or `yarn flow init`) to create a [`.flowconfig` file](https://flowtype.org/docs/advanced-configuration.html) in the root directory. 4. Add `// @flow` to any files you want to type check (for example, to `src/App.js`). -5. Edit `.babelrc` with the following content: - -``` -{ - "presets": [["react-app", { "flow": true }]] -} -``` Now you can run `npm run flow` (or `yarn flow`) to check the files for type errors. You can optionally use an IDE like [Nuclide](https://nuclide.io/docs/languages/flow/) for a better integrated experience. @@ -917,18 +910,9 @@ 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 --dev typescript @types/react @types/react-dom` (or `yarn add --dev typescript @types/react @types/react-dom`). -2. Add `"type-check": "tsc"` to the `scripts` section of your `package.json`. +1. Run `npm install --dev @types/react @types/react-dom` (or `yarn add --dev @types/react @types/react-dom`). 2. Rename all `.js` files to `.tsx` if they have React components or `.ts` if not (e.g. `git mv src/index.js src/index.tsx`). -4. Edit `.babelrc` with the following content: - -``` -{ - "presets": [["react-app", { "typescript": true }]] -} -``` - -5. Add a `tsconfig.json` file with this content: +3. Create a `tsconfig.json` file with this content: ```json { @@ -965,6 +949,31 @@ To add TypeScript to a Create React App project, follow these steps: } ``` +4. [optional] Create a `tslint.json` file with the following content: + +``` +{ + "defaultSeverity": "warning", + "extends": ["tslint:recommended", "tslint-react", "tslint-config-prettier"], + "linterOptions": { + "exclude": [ + "node_modules/**", + "build/**" + ] + }, + "jsRules": { + "curly": true, + "no-console": false + }, + "rules": { + "curly": true, + "no-console": false, + "member-access": false + }, + "rulesDirectory": [] +} +``` + Now you can run `npm run type-check` (or `yarn type-check`) to check the files for type errors. We recommend using [VSCode](https://code.visualstudio.com/) for a better integrated experience. From 76121c8f153c603636ce8391308e3db83d5bb843 Mon Sep 17 00:00:00 2001 From: Bruno Lemos Date: Mon, 30 Jul 2018 18:23:43 -0300 Subject: [PATCH 4/9] [TypeScript] Add images typescript definition --- packages/react-scripts/template/index.d.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 packages/react-scripts/template/index.d.ts diff --git a/packages/react-scripts/template/index.d.ts b/packages/react-scripts/template/index.d.ts new file mode 100644 index 00000000000..c3e4c1f1497 --- /dev/null +++ b/packages/react-scripts/template/index.d.ts @@ -0,0 +1,12 @@ +declare module '*.jpg' + +declare module '*.png' + +declare module '*.svg' { + import * as React from 'react' + + export const ReactComponent: React.SFC> + + const src: string + export default src +} From 125dcb09e249965d6bdf911cbe726e635c9f824a Mon Sep 17 00:00:00 2001 From: Bruno Lemos Date: Mon, 30 Jul 2018 18:24:08 -0300 Subject: [PATCH 5/9] [TypeScript] Add serviceWorker typescript definition --- packages/react-scripts/template/src/serviceWorker.d.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 packages/react-scripts/template/src/serviceWorker.d.ts diff --git a/packages/react-scripts/template/src/serviceWorker.d.ts b/packages/react-scripts/template/src/serviceWorker.d.ts new file mode 100644 index 00000000000..e30541a5450 --- /dev/null +++ b/packages/react-scripts/template/src/serviceWorker.d.ts @@ -0,0 +1,8 @@ +export type Config = { + onSuccess?: (registration: ServiceWorkerRegistration) => void + onUpdate?: (registration: ServiceWorkerRegistration) => void +} + +export function register(config: Config): void + +export function unregister(): void From 1bbd9e20354fa202abb3786c581d22ff434f6580 Mon Sep 17 00:00:00 2001 From: Bruno Lemos Date: Thu, 9 Aug 2018 00:35:44 -0300 Subject: [PATCH 6/9] [TypeScript] Setup TypeScript type checking --- packages/react-scripts/config/webpack.config.dev.js | 9 ++++++++- packages/react-scripts/config/webpack.config.prod.js | 9 ++++++++- packages/react-scripts/package.json | 6 ++++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index 511ec848e22..a862413b82a 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -13,6 +13,7 @@ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); +const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin'); const eslintFormatter = require('react-dev-utils/eslintFormatter'); @@ -405,7 +406,13 @@ module.exports = { fileName: 'asset-manifest.json', publicPath: publicPath, }), - ], + // TypeScript type checking + paths.isTypeScript && + new ForkTsCheckerWebpackPlugin({ + async: false, + watch: paths.appSrc, + }), + ].filter(Boolean), // Some libraries import Node modules but don't use them in the browser. // Tell Webpack to provide empty mocks for them so importing them works. diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js index 6b84754f58f..4d5c1394be3 100644 --- a/packages/react-scripts/config/webpack.config.prod.js +++ b/packages/react-scripts/config/webpack.config.prod.js @@ -13,6 +13,7 @@ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); +const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const ManifestPlugin = require('webpack-manifest-plugin'); @@ -477,7 +478,13 @@ module.exports = { // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack // You can remove this if you don't use Moment.js: new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), - ], + // TypeScript type checking + paths.isTypeScript && + new ForkTsCheckerWebpackPlugin({ + async: false, + watch: paths.appSrc, + }), + ].filter(Boolean), // Some libraries import Node modules but don't use them in the browser. // Tell Webpack to provide empty mocks for them so importing them works. node: { diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index a5ff73ed8b2..949132d51c6 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -44,17 +44,18 @@ "eslint-plugin-jsx-a11y": "6.0.3", "eslint-plugin-react": "7.8.2", "file-loader": "1.1.11", + "fork-ts-checker-webpack-plugin": "^0.4.6", "fs-extra": "5.0.0", "graphql": "0.13.2", "graphql-tag": "2.9.2", "html-webpack-plugin": "3.2.0", "identity-obj-proxy": "3.0.0", - "loader-utils": "^1.1.0", "jest": "22.4.3", + "loader-utils": "^1.1.0", "mini-css-extract-plugin": "^0.4.0", "object-assign": "4.1.1", - "postcss-flexbugs-fixes": "3.3.1", "optimize-css-assets-webpack-plugin": "^4.0.1", + "postcss-flexbugs-fixes": "3.3.1", "postcss-loader": "2.1.5", "promise": "8.0.1", "raf": "3.4.0", @@ -65,6 +66,7 @@ "svgr": "1.9.2", "sw-precache-webpack-plugin": "0.11.5", "thread-loader": "1.1.5", + "typescript": "^3", "uglifyjs-webpack-plugin": "1.2.5", "url-loader": "1.0.1", "webpack": "4.8.3", From b343104cf9febe58db8c1f2a453aac987ac48cf6 Mon Sep 17 00:00:00 2001 From: Bruno Lemos Date: Thu, 9 Aug 2018 01:50:34 -0300 Subject: [PATCH 7/9] [TypeScript] Support TSLint --- packages/babel-preset-react-app/README.md | 2 + .../react-dev-utils/WebpackDevServerUtils.js | 5 +- packages/react-scripts/config/paths.js | 3 ++ .../config/webpack.config.dev.js | 5 +- .../config/webpack.config.prod.js | 5 +- packages/react-scripts/package.json | 1 + packages/react-scripts/scripts/build.js | 6 ++- packages/react-scripts/template/README.md | 53 ++++++++++--------- packages/react-scripts/template/src/App.js | 4 +- packages/react-scripts/template/src/index.js | 2 + 10 files changed, 55 insertions(+), 31 deletions(-) diff --git a/packages/babel-preset-react-app/README.md b/packages/babel-preset-react-app/README.md index 5ef733fccee..04c67f10f3d 100644 --- a/packages/babel-preset-react-app/README.md +++ b/packages/babel-preset-react-app/README.md @@ -52,3 +52,5 @@ To use this package with [`@babel/preset-typescript`]https://www.npmjs.com/packa "presets": [["react-app", { "typescript": true }]] } ``` + +TSLint is also automatically supported, you only need to create a `tslint.json` (and optionally a `tslint.prod.json`) file at the root directory. \ No newline at end of file diff --git a/packages/react-dev-utils/WebpackDevServerUtils.js b/packages/react-dev-utils/WebpackDevServerUtils.js index bc0378459c5..f3d03df860c 100644 --- a/packages/react-dev-utils/WebpackDevServerUtils.js +++ b/packages/react-dev-utils/WebpackDevServerUtils.js @@ -177,7 +177,7 @@ function createCompiler(webpack, config, appName, urls, useYarn) { console.log(chalk.yellow('Compiled with warnings.\n')); console.log(messages.warnings.join('\n\n')); - // Teach some ESLint tricks. + // Teach some ESLint / TSLint tricks. console.log( '\nSearch for the ' + chalk.underline(chalk.yellow('keywords')) + @@ -186,6 +186,9 @@ function createCompiler(webpack, config, appName, urls, useYarn) { console.log( 'To ignore, add ' + chalk.cyan('// eslint-disable-next-line') + + ' (or ' + + chalk.cyan('// tslint:disable-next-line') + + ' if TypeScript)' + ' to the line before.\n' ); } diff --git a/packages/react-scripts/config/paths.js b/packages/react-scripts/config/paths.js index c3e30d436d3..e5c2472adf6 100644 --- a/packages/react-scripts/config/paths.js +++ b/packages/react-scripts/config/paths.js @@ -49,6 +49,7 @@ function getServedPath(appPackageJson) { const isFlow = fs.existsSync(resolveApp('.flowconfig')); const isTypeScript = fs.existsSync(resolveApp('tsconfig.json')); +const useTSLint = isTypeScript && fs.existsSync(resolveApp('tslint.json')); // config after eject: we're in ./config/ module.exports = { @@ -131,6 +132,8 @@ module.exports.isFlow = isFlow; module.exports.isTypeScript = isTypeScript; +module.exports.useTSLint = useTSLint; + module.exports.useYarn = fs.existsSync( path.join(module.exports.appPath, 'yarn.lock') ); diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index a862413b82a..8c10a27352a 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -187,7 +187,7 @@ module.exports = { // First, run the linter. // It's important to do this before Babel processes the JS. - { + !paths.isTypeScript && { test: /\.(js|jsx|mjs)$/, enforce: 'pre', use: [ @@ -366,7 +366,7 @@ module.exports = { }, // ** STOP ** Are you adding a new loader? // Make sure to add the new loader(s) before the "file" loader. - ], + ].filter(Boolean), }, plugins: [ // Generates an `index.html` file with the