-
-
Notifications
You must be signed in to change notification settings - Fork 27k
Set module resolution based on baseUrl in jsconfig/tsconfig.json #6116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 21 commits
07c1f95
ec03a78
ebd7650
4de79d5
839a2a3
91f7794
be87538
e0d3ebf
5d67018
bb49cf3
f5821c2
832cad2
401b500
234e361
97ea854
2aa5605
a0ed858
e52182d
bf5329f
2c19adc
ac8dd39
a9b60b1
637e404
830415a
09d9845
db01cb9
47b7099
50e9c60
229457a
216eaff
26d6bdb
74ad80a
116c9c8
ccc6b89
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| // @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 chalk = require('chalk'); | ||
| const paths = require('./paths'); | ||
|
|
||
| function isValidPath(path) { | ||
|
robertvansteen marked this conversation as resolved.
Outdated
|
||
| return ( | ||
| paths.relative(paths.appSrc, path) === '.' || | ||
| paths.relative(paths.appNodeModules, path) === '.' | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Get the baseUrl of a compilerOptions object. | ||
| * | ||
| * @param {Object} options | ||
| */ | ||
| function getBaseUrl(options = {}) { | ||
|
robertvansteen marked this conversation as resolved.
Outdated
robertvansteen marked this conversation as resolved.
Outdated
|
||
| const baseUrl = options.baseUrl; | ||
|
|
||
| if (!baseUrl) { | ||
| return null; | ||
| } | ||
|
|
||
| const baseUrlResolved = path.resolve(paths.appDirectory, baseUrl); | ||
|
|
||
| if (!isValidPath(baseUrlResolved)) { | ||
| console.error( | ||
| chalk.red.bold( | ||
| "You tried to set baseUrl to anything other than 'src' or 'node_modules'.This is not supported in create-react-app and will be ignored." | ||
| ) | ||
| ); | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| return path.resolve(paths.appDirectory, 'src'); | ||
| } | ||
|
|
||
| /** | ||
| * Get the alias of a compilerOptions object. | ||
| * | ||
| * @param {Object} options | ||
| */ | ||
| function getAlias(options = {}) { | ||
|
Timer marked this conversation as resolved.
Outdated
|
||
| const paths = options.paths || {}; | ||
|
|
||
| const alias = paths['@']; | ||
|
|
||
| const others = Object.keys(paths).filter(function(value) { | ||
| return value !== '@'; | ||
| }); | ||
|
|
||
| if (others.length) { | ||
| console.error( | ||
| chalk.red.bold( | ||
| 'You tried to set one or more paths with an alias other than "@", this is currently not supported in create-react-app and will be ignored.' | ||
| ) | ||
| ); | ||
| } | ||
|
|
||
| if (!alias) { | ||
| return {}; | ||
| } | ||
|
|
||
| if (alias.toString() !== 'src') { | ||
| console.error( | ||
| chalk.red.bold( | ||
| "You tried to set a path with alias '@' to anything other than ['src']. This is not supported in create-react-app and will be ignored." | ||
| ) | ||
| ); | ||
| } | ||
|
|
||
| return { | ||
| '@': path.resolve(paths.appDirectory, 'src'), | ||
| }; | ||
| } | ||
|
|
||
| function getConfig() { | ||
| // Check if TypeScript is setup | ||
| const useTypeScript = fs.existsSync(paths.appTsConfig); | ||
| const hasJsConfig = fs.existsSync(paths.appJsConfig); | ||
|
robertvansteen marked this conversation as resolved.
Outdated
|
||
|
|
||
| if (useTypeScript && 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 (useTypeScript) { | ||
| 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 || {}; | ||
|
|
||
| return { | ||
| alias: getAlias(options), | ||
| baseUrl: getBaseUrl(options), | ||
| useTypeScript, | ||
| }; | ||
| } | ||
|
|
||
| module.exports = getConfig(); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,7 +9,6 @@ | |
| 'use strict'; | ||
|
|
||
| const fs = require('fs'); | ||
| const path = require('path'); | ||
| const paths = require('./paths'); | ||
|
|
||
| // Make sure that including paths.js after env.js will read .env variables. | ||
|
|
@@ -48,21 +47,14 @@ dotenvFiles.forEach(dotenvFile => { | |
| } | ||
| }); | ||
|
|
||
| // We support resolving modules according to `NODE_PATH`. | ||
| // 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: | ||
| // https://github.com/facebook/create-react-app/issues/253. | ||
| // It works similar to `NODE_PATH` in Node itself: | ||
| // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders | ||
| // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored. | ||
| // Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims. | ||
| // https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421 | ||
| // We also resolve them to make sure all tools using them work consistently. | ||
| const appDirectory = fs.realpathSync(process.cwd()); | ||
| process.env.NODE_PATH = (process.env.NODE_PATH || '') | ||
| .split(path.delimiter) | ||
| .filter(folder => folder && !path.isAbsolute(folder)) | ||
| .map(folder => path.resolve(appDirectory, folder)) | ||
| .join(path.delimiter); | ||
| if (process.env.NODE_PATH) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should move this check elsewhere imo -- probably in the script bin wrapper.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup!
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Timer in the bin wrapper the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, good point. And this just seems like an odd place to have the check (as a side effect of the file being evaluated).
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we can just add it to |
||
| console.log( | ||
| 'Setting NODE_PATH to resolves modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using Typescript) to achieve the same behaviour.' | ||
| ); | ||
| } | ||
|
|
||
| // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be | ||
| // injected into the application via DefinePlugin in Webpack configuration. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| /** | ||
| * 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(); | ||
| }); | ||
|
|
||
| it('Supports setting @ as alias to src', async () => { | ||
| const doc = await initDOM('alias'); | ||
|
|
||
| expect(doc.getElementById('feature-alias').childElementCount).toBe(4); | ||
| doc.defaultView.close(); | ||
| }); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| "compilerOptions": { | ||
| "baseUrl": "src", | ||
| "paths": { | ||
| "@": ["src"] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps add a test with
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For people that had their
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, I think it should be the below as a default, as that matches the Then, a user could do the following, if they chose.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I'm not sure if we should provide a default as the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand, by default there's no NODE_PATH. However, I think most people that use it would use it with As per most implementations that I've seen (based on Microsoft examples) you can leave
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that makes sense and that's something we should definitely recommend in the documentation. Especially if you are migrating from |
||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| /** | ||
| * 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 React, { Component } from 'react'; | ||
| import PropTypes from 'prop-types'; | ||
| import load from '@/absoluteLoad'; | ||
|
|
||
| export default class extends Component { | ||
| static propTypes = { | ||
| onReady: PropTypes.func.isRequired, | ||
| }; | ||
|
|
||
| constructor(props) { | ||
| super(props); | ||
| this.state = { users: [] }; | ||
| } | ||
|
|
||
| async componentDidMount() { | ||
| const users = load(); | ||
| this.setState({ users }); | ||
| } | ||
|
|
||
| componentDidUpdate() { | ||
| this.props.onReady(); | ||
| } | ||
|
|
||
| render() { | ||
| return ( | ||
| <div id="feature-alias"> | ||
| {this.state.users.map(user => ( | ||
| <div key={user.id}>{user.name}</div> | ||
| ))} | ||
| </div> | ||
| ); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 React from 'react'; | ||
| import ReactDOM from 'react-dom'; | ||
| import BaseUrl from './BaseUrl'; | ||
|
|
||
| describe('baseUrl', () => { | ||
| it('renders without crashing', () => { | ||
| const div = document.createElement('div'); | ||
| return new Promise(resolve => { | ||
| ReactDOM.render(<BaseUrl onReady={resolve} />, div); | ||
| }); | ||
| }); | ||
| }); |
Uh oh!
There was an error while loading. Please reload this page.