mirror of
https://github.com/zhigang1992/create-react-app.git
synced 2026-04-23 20:51:12 +08:00
Set baseUrl from jsconfig.json/tsconfig.json (#6656)
* Set baseUrl from jsconfig.json/tsconfig.json * Resolve the path for loading modules * Add tests for jsconfig.json * Add jsconfig.json * Update packages/react-scripts/scripts/start.js * Move baseUrl test to config folder * Remove alias test * Use chalk from react-dev-utils * Add lost absolute file for typescript baseUrl test * Update packages/react-scripts/config/modules.js * Update other references of useTypeScript to hasTsConfig * Fix casing of TypeScript * Keep respecting NODE_PATH for now to support multiple module paths. * Add test for NODE_PATH * Add fallback if NODE_PATH is not set. * Fix node path behavior tests * Remove debugging code from behavior test suite * Remove more debugging code * Show NODE_PATH deprecation warning during build Co-authored-by: Ian Sutherland <ian@iansutherland.ca> Co-authored-by: Brody McKee <mrmckeb@users.noreply.github.com> Co-authored-by: Jack Zhao <jzhao@fb.com>
This commit is contained in:
committed by
Ian Sutherland
parent
ced3fd49ca
commit
e7a2d6168a
92
packages/react-scripts/config/modules.js
vendored
Normal file
92
packages/react-scripts/config/modules.js
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
// @remove-on-eject-begin
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
// @remove-on-eject-end
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const paths = require('./paths');
|
||||
const chalk = require('react-dev-utils/chalk');
|
||||
|
||||
/**
|
||||
* Get the baseUrl of a compilerOptions object.
|
||||
*
|
||||
* @param {Object} options
|
||||
*/
|
||||
function getAdditionalModulePaths(options = {}) {
|
||||
const baseUrl = options.baseUrl;
|
||||
|
||||
// We need to explicitly check for null and undefined (and not a falsy value) because
|
||||
// TypeScript treats an empty string as `.`.
|
||||
if (baseUrl == null) {
|
||||
// If there's no baseUrl set we respect NODE_PATH
|
||||
// Note that NODE_PATH is deprecated and will be removed
|
||||
// in the next major release of create-react-app.
|
||||
|
||||
const nodePath = process.env.NODE_PATH || '';
|
||||
return nodePath.split(path.delimiter).filter(Boolean);
|
||||
}
|
||||
|
||||
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
|
||||
|
||||
// We don't need to do anything if `baseUrl` is set to `node_modules`. This is
|
||||
// the default behavior.
|
||||
if (path.relative(paths.appNodeModules, baseUrlResolved) === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Allow the user set the `baseUrl` to `appSrc`.
|
||||
if (path.relative(paths.appSrc, baseUrlResolved) === '') {
|
||||
return [paths.appSrc];
|
||||
}
|
||||
|
||||
// Otherwise, throw an error.
|
||||
throw new Error(
|
||||
chalk.red.bold(
|
||||
"Your project's `baseUrl` can only be set to `src` or `node_modules`." +
|
||||
' Create React App does not support other values at this time.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function getModules() {
|
||||
// Check if TypeScript is setup
|
||||
const hasTsConfig = fs.existsSync(paths.appTsConfig);
|
||||
const hasJsConfig = fs.existsSync(paths.appJsConfig);
|
||||
|
||||
if (hasTsConfig && hasJsConfig) {
|
||||
throw new Error(
|
||||
'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.'
|
||||
);
|
||||
}
|
||||
|
||||
let config;
|
||||
|
||||
// If there's a tsconfig.json we assume it's a
|
||||
// TypeScript project and set up the config
|
||||
// based on tsconfig.json
|
||||
if (hasTsConfig) {
|
||||
config = require(paths.appTsConfig);
|
||||
// Otherwise we'll check if there is jsconfig.json
|
||||
// for non TS projects.
|
||||
} else if (hasJsConfig) {
|
||||
config = require(paths.appJsConfig);
|
||||
}
|
||||
|
||||
config = config || {};
|
||||
const options = config.compilerOptions || {};
|
||||
|
||||
const additionalModulePaths = getAdditionalModulePaths(options);
|
||||
|
||||
return {
|
||||
additionalModulePaths: additionalModulePaths,
|
||||
hasTsConfig,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = getModules();
|
||||
3
packages/react-scripts/config/paths.js
vendored
3
packages/react-scripts/config/paths.js
vendored
@@ -84,6 +84,7 @@ module.exports = {
|
||||
appPackageJson: resolveApp('package.json'),
|
||||
appSrc: resolveApp('src'),
|
||||
appTsConfig: resolveApp('tsconfig.json'),
|
||||
appJsConfig: resolveApp('jsconfig.json'),
|
||||
yarnLockFile: resolveApp('yarn.lock'),
|
||||
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
|
||||
proxySetup: resolveApp('src/setupProxy.js'),
|
||||
@@ -106,6 +107,7 @@ module.exports = {
|
||||
appPackageJson: resolveApp('package.json'),
|
||||
appSrc: resolveApp('src'),
|
||||
appTsConfig: resolveApp('tsconfig.json'),
|
||||
appJsConfig: resolveApp('jsconfig.json'),
|
||||
yarnLockFile: resolveApp('yarn.lock'),
|
||||
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
|
||||
proxySetup: resolveApp('src/setupProxy.js'),
|
||||
@@ -140,6 +142,7 @@ if (
|
||||
appPackageJson: resolveOwn('package.json'),
|
||||
appSrc: resolveOwn('template/src'),
|
||||
appTsConfig: resolveOwn('template/tsconfig.json'),
|
||||
appJsConfig: resolveOwn('template/jsconfig.json'),
|
||||
yarnLockFile: resolveOwn('template/yarn.lock'),
|
||||
testsSetup: resolveModule(resolveOwn, 'template/src/setupTests'),
|
||||
proxySetup: resolveOwn('template/src/setupProxy.js'),
|
||||
|
||||
@@ -28,6 +28,7 @@ const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeM
|
||||
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
|
||||
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
|
||||
const paths = require('./paths');
|
||||
const modules = require('./modules');
|
||||
const getClientEnvironment = require('./env');
|
||||
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
|
||||
const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin');
|
||||
@@ -268,10 +269,7 @@ module.exports = function(webpackEnv) {
|
||||
// We placed these paths second because we want `node_modules` to "win"
|
||||
// if there are any conflicts. This matches Node resolution mechanism.
|
||||
// https://github.com/facebook/create-react-app/issues/253
|
||||
modules: ['node_modules', paths.appNodeModules].concat(
|
||||
// It is guaranteed to exist because we tweak it in `env.js`
|
||||
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
|
||||
),
|
||||
modules: ['node_modules', paths.appNodeModules].concat(modules.additionalModulePaths || []),
|
||||
// These are the reasonable defaults supported by the Node ecosystem.
|
||||
// We also include JSX as a common component filename extension to support
|
||||
// some tools, although we do not recommend using it, see:
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import initDOM from './initDOM';
|
||||
|
||||
describe('Integration', () => {
|
||||
describe('jsconfig.json/tsconfig.json', () => {
|
||||
it('Supports setting baseUrl to src', async () => {
|
||||
const doc = await initDOM('base-url');
|
||||
|
||||
expect(doc.getElementById('feature-base-url').childElementCount).toBe(4);
|
||||
doc.defaultView.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -43,12 +43,6 @@ describe('Integration', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('NODE_PATH', async () => {
|
||||
doc = await initDOM('node-path');
|
||||
|
||||
expect(doc.getElementById('feature-node-path').childElementCount).toBe(4);
|
||||
});
|
||||
|
||||
it('PUBLIC_URL', async () => {
|
||||
doc = await initDOM('public-url');
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src"
|
||||
}
|
||||
}
|
||||
@@ -166,9 +166,6 @@ class App extends Component {
|
||||
this.setFeature(f.default)
|
||||
);
|
||||
break;
|
||||
case 'node-path':
|
||||
import('./features/env/NodePath').then(f => this.setFeature(f.default));
|
||||
break;
|
||||
case 'no-ext-inclusion':
|
||||
import('./features/webpack/NoExtInclusion').then(f =>
|
||||
this.setFeature(f.default)
|
||||
@@ -239,6 +236,11 @@ class App extends Component {
|
||||
this.setFeature(f.default)
|
||||
);
|
||||
break;
|
||||
case 'base-url':
|
||||
import('./features/config/BaseUrl').then(f =>
|
||||
this.setFeature(f.default)
|
||||
);
|
||||
break;
|
||||
default:
|
||||
this.setState({ error: `Missing feature "${feature}"` });
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export default class extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div id="feature-node-path">
|
||||
<div id="feature-base-url">
|
||||
{this.state.users.map(user => (
|
||||
<div key={user.id}>{user.name}</div>
|
||||
))}
|
||||
@@ -7,9 +7,9 @@
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import NodePath from './NodePath';
|
||||
import NodePath from './BaseUrl';
|
||||
|
||||
describe('NODE_PATH', () => {
|
||||
describe('BASE_URL', () => {
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
return new Promise(resolve => {
|
||||
12
packages/react-scripts/scripts/build.js
vendored
12
packages/react-scripts/scripts/build.js
vendored
@@ -136,6 +136,18 @@ checkBrowsers(paths.appPath, isInteractive)
|
||||
|
||||
// Create the production build and print the deployment instructions.
|
||||
function build(previousFileSizes) {
|
||||
// We used to support resolving modules according to `NODE_PATH`.
|
||||
// This now has been deprecated in favor of jsconfig/tsconfig.json
|
||||
// This lets you use absolute paths in imports inside large monorepos:
|
||||
if (process.env.NODE_PATH) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
|
||||
)
|
||||
);
|
||||
console.log();
|
||||
}
|
||||
|
||||
console.log('Creating an optimized production build...');
|
||||
|
||||
const compiler = webpack(config);
|
||||
|
||||
13
packages/react-scripts/scripts/start.js
vendored
13
packages/react-scripts/scripts/start.js
vendored
@@ -129,6 +129,19 @@ checkBrowsers(paths.appPath, isInteractive)
|
||||
if (isInteractive) {
|
||||
clearConsole();
|
||||
}
|
||||
|
||||
// We used to support resolving modules according to `NODE_PATH`.
|
||||
// This now has been deprecated in favor of jsconfig/tsconfig.json
|
||||
// This lets you use absolute paths in imports inside large monorepos:
|
||||
if (process.env.NODE_PATH) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
|
||||
)
|
||||
);
|
||||
console.log();
|
||||
}
|
||||
|
||||
console.log(chalk.cyan('Starting the development server...\n'));
|
||||
openBrowser(urls.localUrlForBrowser);
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
const fs = require('fs');
|
||||
const chalk = require('react-dev-utils/chalk');
|
||||
const paths = require('../../config/paths');
|
||||
const modules = require('../../config/modules');
|
||||
|
||||
module.exports = (resolve, rootDir, isEjecting) => {
|
||||
// Use this instead of `paths.testsSetup` to avoid putting
|
||||
@@ -49,6 +50,7 @@ module.exports = (resolve, rootDir, isEjecting) => {
|
||||
'[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$',
|
||||
'^.+\\.module\\.(css|sass|scss)$',
|
||||
],
|
||||
modulePaths: modules.additionalModulePaths || [],
|
||||
moduleNameMapper: {
|
||||
'^react-native$': 'react-native-web',
|
||||
'^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy',
|
||||
|
||||
@@ -18,7 +18,10 @@ const immer = require('react-dev-utils/immer').produce;
|
||||
const globby = require('react-dev-utils/globby').sync;
|
||||
|
||||
function writeJson(fileName, object) {
|
||||
fs.writeFileSync(fileName, JSON.stringify(object, null, 2).replace(/\n/g, os.EOL) + os.EOL);
|
||||
fs.writeFileSync(
|
||||
fileName,
|
||||
JSON.stringify(object, null, 2).replace(/\n/g, os.EOL) + os.EOL
|
||||
);
|
||||
}
|
||||
|
||||
function verifyNoTypeScript() {
|
||||
@@ -124,12 +127,6 @@ function verifyTypeScriptSetup() {
|
||||
value: 'preserve',
|
||||
reason: 'JSX is compiled by Babel',
|
||||
},
|
||||
// We do not support absolute imports, though this may come as a future
|
||||
// enhancement
|
||||
baseUrl: {
|
||||
value: undefined,
|
||||
reason: 'absolute imports are not supported (yet)',
|
||||
},
|
||||
paths: { value: undefined, reason: 'aliased imports are not supported' },
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user