mirror of
https://github.com/zhigang1992/create-react-app.git
synced 2026-04-26 14:15:50 +08:00
TypeScript syntax support (#4837)
This commit is contained in:
44
packages/react-scripts/config/paths.js
vendored
44
packages/react-scripts/config/paths.js
vendored
@@ -46,6 +46,33 @@ function getServedPath(appPackageJson) {
|
||||
return ensureSlash(servedUrl, true);
|
||||
}
|
||||
|
||||
const moduleFileExtensions = [
|
||||
'web.mjs',
|
||||
'mjs',
|
||||
'web.js',
|
||||
'js',
|
||||
'web.ts',
|
||||
'ts',
|
||||
'web.tsx',
|
||||
'tsx',
|
||||
'json',
|
||||
'web.jsx',
|
||||
'jsx',
|
||||
];
|
||||
|
||||
// Resolve file paths in the same order as webpack
|
||||
const resolveModule = (resolveFn, filePath) => {
|
||||
const extension = moduleFileExtensions.find(extension =>
|
||||
fs.existsSync(resolveFn(`${filePath}.${extension}`))
|
||||
);
|
||||
|
||||
if (extension) {
|
||||
return resolveFn(`${filePath}.${extension}`);
|
||||
}
|
||||
|
||||
return resolveFn(`${filePath}.js`);
|
||||
};
|
||||
|
||||
// config after eject: we're in ./config/
|
||||
module.exports = {
|
||||
dotenv: resolveApp('.env'),
|
||||
@@ -53,11 +80,12 @@ module.exports = {
|
||||
appBuild: resolveApp('build'),
|
||||
appPublic: resolveApp('public'),
|
||||
appHtml: resolveApp('public/index.html'),
|
||||
appIndexJs: resolveApp('src/index.js'),
|
||||
appIndexJs: resolveModule(resolveApp, 'src/index'),
|
||||
appPackageJson: resolveApp('package.json'),
|
||||
appSrc: resolveApp('src'),
|
||||
appTsConfig: resolveApp('tsconfig.json'),
|
||||
yarnLockFile: resolveApp('yarn.lock'),
|
||||
testsSetup: resolveApp('src/setupTests.js'),
|
||||
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
|
||||
proxySetup: resolveApp('src/setupProxy.js'),
|
||||
appNodeModules: resolveApp('node_modules'),
|
||||
publicUrl: getPublicUrl(resolveApp('package.json')),
|
||||
@@ -74,11 +102,12 @@ module.exports = {
|
||||
appBuild: resolveApp('build'),
|
||||
appPublic: resolveApp('public'),
|
||||
appHtml: resolveApp('public/index.html'),
|
||||
appIndexJs: resolveApp('src/index.js'),
|
||||
appIndexJs: resolveModule(resolveApp, 'src/index'),
|
||||
appPackageJson: resolveApp('package.json'),
|
||||
appSrc: resolveApp('src'),
|
||||
appTsConfig: resolveApp('tsconfig.json'),
|
||||
yarnLockFile: resolveApp('yarn.lock'),
|
||||
testsSetup: resolveApp('src/setupTests.js'),
|
||||
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
|
||||
proxySetup: resolveApp('src/setupProxy.js'),
|
||||
appNodeModules: resolveApp('node_modules'),
|
||||
publicUrl: getPublicUrl(resolveApp('package.json')),
|
||||
@@ -105,11 +134,12 @@ if (
|
||||
appBuild: resolveOwn('../../build'),
|
||||
appPublic: resolveOwn('template/public'),
|
||||
appHtml: resolveOwn('template/public/index.html'),
|
||||
appIndexJs: resolveOwn('template/src/index.js'),
|
||||
appIndexJs: resolveModule(resolveOwn, 'template/src/index'),
|
||||
appPackageJson: resolveOwn('package.json'),
|
||||
appSrc: resolveOwn('template/src'),
|
||||
appTsConfig: resolveOwn('template/tsconfig.json'),
|
||||
yarnLockFile: resolveOwn('template/yarn.lock'),
|
||||
testsSetup: resolveOwn('template/src/setupTests.js'),
|
||||
testsSetup: resolveModule(resolveOwn, 'template/src/setupTests'),
|
||||
proxySetup: resolveOwn('template/src/setupProxy.js'),
|
||||
appNodeModules: resolveOwn('node_modules'),
|
||||
publicUrl: getPublicUrl(resolveOwn('package.json')),
|
||||
@@ -120,3 +150,5 @@ if (
|
||||
};
|
||||
}
|
||||
// @remove-on-eject-end
|
||||
|
||||
module.exports.moduleFileExtensions = moduleFileExtensions;
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
// @remove-on-eject-end
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const resolve = require('resolve');
|
||||
const webpack = require('webpack');
|
||||
const PnpWebpackPlugin = require('pnp-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
@@ -146,7 +148,7 @@ 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: ['.mjs', '.web.js', '.js', '.json', '.web.jsx', '.jsx'],
|
||||
extensions: paths.moduleFileExtensions.map(ext => `.${ext}`),
|
||||
alias: {
|
||||
// Support React Native Web
|
||||
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
|
||||
@@ -220,7 +222,7 @@ module.exports = {
|
||||
// Process application JS with Babel.
|
||||
// The preset includes JSX, Flow, and some ESnext features.
|
||||
{
|
||||
test: /\.(js|mjs|jsx)$/,
|
||||
test: /\.(js|mjs|jsx|ts|tsx)$/,
|
||||
include: paths.appSrc,
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
@@ -353,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|mjs|jsx)$/, /\.html$/, /\.json$/],
|
||||
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
|
||||
loader: require.resolve('file-loader'),
|
||||
options: {
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
@@ -406,7 +408,30 @@ module.exports = {
|
||||
fileName: 'asset-manifest.json',
|
||||
publicPath: publicPath,
|
||||
}),
|
||||
],
|
||||
// TypeScript type checking
|
||||
fs.existsSync(paths.appTsConfig) &&
|
||||
(() => {
|
||||
let ForkTsCheckerWebpackPlugin;
|
||||
try {
|
||||
ForkTsCheckerWebpackPlugin = require(resolve.sync(
|
||||
'fork-ts-checker-webpack-plugin',
|
||||
{ basedir: paths.appNodeModules }
|
||||
));
|
||||
} catch (e) {
|
||||
// Fail silently.
|
||||
// Type checking using this plugin is optional.
|
||||
// The user may decide to install `fork-ts-checker-webpack-plugin` or use `tsc -w`.
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ForkTsCheckerWebpackPlugin({
|
||||
async: false,
|
||||
checkSyntacticErrors: true,
|
||||
tsconfig: paths.appTsConfig,
|
||||
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.
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
// @remove-on-eject-end
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const resolve = require('resolve');
|
||||
const PnpWebpackPlugin = require('pnp-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
|
||||
@@ -220,7 +222,7 @@ 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: ['.mjs', '.web.js', '.js', '.json', '.web.jsx', '.jsx'],
|
||||
extensions: paths.moduleFileExtensions.map(ext => `.${ext}`),
|
||||
alias: {
|
||||
// Support React Native Web
|
||||
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
|
||||
@@ -293,9 +295,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|mjs|jsx)$/,
|
||||
test: /\.(js|mjs|jsx|ts|tsx)$/,
|
||||
include: paths.appSrc,
|
||||
|
||||
loader: require.resolve('babel-loader'),
|
||||
@@ -445,7 +447,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|mjs|jsx)$/, /\.html$/, /\.json$/],
|
||||
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
|
||||
options: {
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
@@ -476,7 +478,8 @@ module.exports = {
|
||||
}),
|
||||
// Inlines the webpack runtime script. This script is too small to warrant
|
||||
// a network request.
|
||||
shouldInlineRuntimeChunk && new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime~.+[.]js/]),
|
||||
shouldInlineRuntimeChunk &&
|
||||
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime~.+[.]js/]),
|
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
@@ -525,6 +528,29 @@ module.exports = {
|
||||
new RegExp('/[^/]+\\.[^/]+$'),
|
||||
],
|
||||
}),
|
||||
// TypeScript type checking
|
||||
fs.existsSync(paths.appTsConfig) &&
|
||||
(() => {
|
||||
let ForkTsCheckerWebpackPlugin;
|
||||
try {
|
||||
ForkTsCheckerWebpackPlugin = require(resolve.sync(
|
||||
'fork-ts-checker-webpack-plugin',
|
||||
{ basedir: paths.appNodeModules }
|
||||
));
|
||||
} catch (e) {
|
||||
// Fail silently.
|
||||
// Type checking using this plugin is optional.
|
||||
// The user may decide to install `fork-ts-checker-webpack-plugin` or use `tsc -w`.
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ForkTsCheckerWebpackPlugin({
|
||||
async: false,
|
||||
checkSyntacticErrors: true,
|
||||
tsconfig: paths.appTsConfig,
|
||||
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.
|
||||
|
||||
2
packages/react-scripts/scripts/test.js
vendored
2
packages/react-scripts/scripts/test.js
vendored
@@ -81,7 +81,7 @@ argv.push(
|
||||
|
||||
// This is a very dirty workaround for https://github.com/facebook/jest/issues/5913.
|
||||
// We're trying to resolve the environment ourselves because Jest does it incorrectly.
|
||||
// TODO: remove this (and the `resolve` dependency) as soon as it's fixed in Jest.
|
||||
// TODO: remove this as soon as it's fixed in Jest.
|
||||
const resolve = require('resolve');
|
||||
function resolveJestDefaultEnvironment(name) {
|
||||
const jestDir = path.dirname(
|
||||
|
||||
@@ -14,14 +14,17 @@ const paths = require('../../config/paths');
|
||||
module.exports = (resolve, rootDir, isEjecting) => {
|
||||
// Use this instead of `paths.testsSetup` to avoid putting
|
||||
// an absolute filename into configuration after ejecting.
|
||||
const setupTestsMatches = paths.testsSetup.match(/src\/setupTests\.(.+)/);
|
||||
const setupTestsFileExtension =
|
||||
(setupTestsMatches && setupTestsMatches[1]) || 'js';
|
||||
const setupTestsFile = fs.existsSync(paths.testsSetup)
|
||||
? '<rootDir>/src/setupTests.js'
|
||||
? `<rootDir>/src/setupTests.${setupTestsFileExtension}`
|
||||
: undefined;
|
||||
|
||||
// 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}'],
|
||||
collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}', '!src/**/*.d.ts'],
|
||||
|
||||
// TODO: this breaks Yarn PnP on eject.
|
||||
// But we can't simply emit this because it'll be an absolute path.
|
||||
@@ -38,27 +41,31 @@ module.exports = (resolve, rootDir, isEjecting) => {
|
||||
|
||||
setupTestFrameworkScriptFile: setupTestsFile,
|
||||
testMatch: [
|
||||
'<rootDir>/src/**/__tests__/**/*.{js,jsx}',
|
||||
'<rootDir>/src/**/?(*.)(spec|test).{js,jsx}',
|
||||
'<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}',
|
||||
'<rootDir>/src/**/?(*.)(spec|test).{js,jsx,ts,tsx}',
|
||||
],
|
||||
testEnvironment: 'jsdom',
|
||||
testURL: 'http://localhost',
|
||||
transform: {
|
||||
'^.+\\.(js|jsx)$': isEjecting
|
||||
'^.+\\.(js|jsx|ts|tsx)$': isEjecting
|
||||
? '<rootDir>/node_modules/babel-jest'
|
||||
: resolve('config/jest/babelTransform.js'),
|
||||
'^.+\\.css$': resolve('config/jest/cssTransform.js'),
|
||||
'^(?!.*\\.(js|jsx|css|json)$)': resolve('config/jest/fileTransform.js'),
|
||||
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': resolve(
|
||||
'config/jest/fileTransform.js'
|
||||
),
|
||||
},
|
||||
transformIgnorePatterns: [
|
||||
'[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$',
|
||||
'[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$',
|
||||
'^.+\\.module\\.(css|sass|scss)$',
|
||||
],
|
||||
moduleNameMapper: {
|
||||
'^react-native$': 'react-native-web',
|
||||
'^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy',
|
||||
},
|
||||
moduleFileExtensions: ['web.js', 'js', 'json', 'web.jsx', 'jsx', 'node'],
|
||||
moduleFileExtensions: [...paths.moduleFileExtensions, 'node'].filter(
|
||||
ext => !ext.includes('mjs')
|
||||
),
|
||||
};
|
||||
if (rootDir) {
|
||||
config.rootDir = rootDir;
|
||||
|
||||
53
packages/react-scripts/template/src/loaders.d.ts
vendored
Normal file
53
packages/react-scripts/template/src/loaders.d.ts
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
declare module '*.json' {
|
||||
const value: any;
|
||||
export default value;
|
||||
}
|
||||
|
||||
declare module '*.bmp' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.gif' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.jpg' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.jpeg' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.png' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.svg' {
|
||||
import React = require('react');
|
||||
|
||||
export const ReactComponent: React.SFC<React.SVGProps<SVGSVGElement>>;
|
||||
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.module.css' {
|
||||
const classes: { [key: string]: string };
|
||||
export default classes;
|
||||
}
|
||||
|
||||
declare module '*.module.scss' {
|
||||
const classes: { [key: string]: string };
|
||||
export default classes;
|
||||
}
|
||||
|
||||
declare module '*.module.sass' {
|
||||
const classes: { [key: string]: string };
|
||||
export default classes;
|
||||
}
|
||||
8
packages/react-scripts/template/src/serviceWorker.d.ts
vendored
Normal file
8
packages/react-scripts/template/src/serviceWorker.d.ts
vendored
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user