-
-
Notifications
You must be signed in to change notification settings - Fork 26.9k
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
Set module resolution based on baseUrl in jsconfig/tsconfig.json #6116
Closed
robertvansteen
wants to merge
34
commits into
facebook:master
from
robertvansteen:typescript-node-path
Closed
Changes from all commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
07c1f95
Set TS base url based on node_path
robertvansteen ec03a78
Remove TS messages for now
robertvansteen ebd7650
Resole baseUrl for tsconfig/jsconfig.json
robertvansteen 4de79d5
Add baseUrl test
robertvansteen 839a2a3
Add jsconfig.json to kitchensink
robertvansteen 91f7794
Add BaseUrl component
robertvansteen be87538
Add baseUrl as modulePath for jest
robertvansteen e0d3ebf
Remove NODE_PATH resolving and inform user about it’s deprecation.
robertvansteen 5d67018
Remove unused variable
robertvansteen bb49cf3
Remove accident typo
robertvansteen f5821c2
Remove unused tests & properly load baseUrl test
robertvansteen 832cad2
Fix wrong path to BaseUrl test
robertvansteen 401b500
Add support for aliasing @ to src
robertvansteen 234e361
Add integration test for alias
robertvansteen 97ea854
Log to debug tests
robertvansteen 2aa5605
Allow setting the baseUrl to node_modules
robertvansteen a0ed858
Remove unnecessary prefix for Jest module paths
robertvansteen e52182d
Whoops.
robertvansteen bf5329f
Call isValidPath method to check if the baseUrl is valid
robertvansteen 2c19adc
Use appDirectory from paths
robertvansteen ac8dd39
Throw error if there are both a tsconfig.json and jsconfig.json file
robertvansteen a9b60b1
Add better path checking & add support for different aliases for src
robertvansteen 637e404
Rename config to modules
robertvansteen 830415a
Fix old reference
robertvansteen 09d9845
Update baseUrl in Typescript compiler options
robertvansteen db01cb9
Log modules to debug
robertvansteen 47b7099
Correctly return prev in reducer
robertvansteen 50e9c60
Suffix alias with a forward slash to prevent conflicts with node_modu…
robertvansteen 229457a
Remove console.log
robertvansteen 216eaff
Attempt to test baseUrl for Typescript
robertvansteen 26d6bdb
Add test to verify aliases work in TypeScript as well
robertvansteen 74ad80a
Remove baseUrl overwrite
robertvansteen 116c9c8
Allow setting the path of an alias to a subfolder of src
robertvansteen ccc6b89
Work on matching jsconfig path behavior with webpack/jest
robertvansteen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
// @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'); | ||
|
||
/** | ||
* Get the baseUrl of a compilerOptions object. | ||
* | ||
* @param {Object} options | ||
*/ | ||
function getAdditionalModulePath(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) { | ||
return null; | ||
} | ||
|
||
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.' | ||
) | ||
); | ||
} | ||
|
||
/** | ||
* Get the alias of a compilerOptions object. | ||
* | ||
* @param {Object} options | ||
*/ | ||
function getAliases(options = {}) { | ||
robertvansteen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// This is an object with the alias as key | ||
// and an array of paths as value. | ||
// e.g. '@': ['src'] | ||
const aliases = options.paths || {}; | ||
|
||
return Object.keys(aliases).reduce(function(prev, alias) { | ||
// Value contains the paths of the alias. | ||
const value = aliases[alias]; | ||
|
||
// The value should be an array but we have to verify | ||
// that because it's user input. | ||
if (!Array.isArray(value) || value.length > 1) { | ||
throw new Error( | ||
chalk.red.bold( | ||
"Your project's `alias` can only be set to an array containing `src` or a subfolder of `src`." + | ||
' Create React App does not support other values at this time.' | ||
) | ||
); | ||
} | ||
|
||
const aliasPath = value[0]; | ||
|
||
// Alias paths are relative to the baseurl. | ||
// If there is no baseUrl set, it will default to the root of the app. | ||
const baseUrl = options.baseUrl | ||
? path.resolve(paths.appPath, options.baseUrl) | ||
: paths.appPath; | ||
const resolvedAliasPath = path.resolve(baseUrl, aliasPath); | ||
|
||
// We then check if the resolved alias path is src or a sub folder of src. | ||
const relativePath = path.relative(paths.appSrc, resolvedAliasPath); | ||
const isSrc = relativePath === ''; | ||
const isSubfolderOfSrc = | ||
!relativePath.startsWith('../') && !relativePath.startsWith('..\\'); | ||
|
||
if (!isSrc && !isSubfolderOfSrc) { | ||
throw new Error( | ||
chalk.red.bold( | ||
"Your project's `alias` can only be set to ['src'] or a subfolder of `src`." + | ||
' Create React App does not support other values at this time.' | ||
) | ||
); | ||
} | ||
|
||
prev[alias] = resolvedAliasPath; | ||
return prev; | ||
}, {}); | ||
} | ||
|
||
function getWebpackAliases(aliases) { | ||
return Object.keys(aliases).reduce(function(prev, alias) { | ||
let aliasPath = aliases[alias]; | ||
const endsWithWilcard = alias.endsWith('*'); | ||
// Remove trailing wildcards (/*) | ||
alias = alias.replace(/\/?\*$/, ''); | ||
aliasPath = aliasPath.replace(/\/\*$/, ''); | ||
// Webpack aliases work a little bit different than jsconfig/tsconfig.json paths | ||
// By default webpack aliases act as a wildcard and for an exact match you have | ||
// to suffix it with a dollar sign. | ||
// tsconfig/jsconfig.json work the other way around and are an exact match unless | ||
// suffixed by a wildcard. | ||
const webpackAlias = endsWithWilcard ? alias : alias + '$'; | ||
prev[webpackAlias] = aliasPath; | ||
return prev; | ||
}, {}); | ||
} | ||
|
||
function getJestAliases(aliases) { | ||
return Object.keys(aliases).reduce(function(prev, alias) { | ||
const endsWithWilcard = alias.endsWith('*'); | ||
let aliasPath = aliases[alias]; | ||
|
||
alias = alias.replace(/\/?\*$/, ''); | ||
const match = endsWithWilcard ? alias + '(.*)$' : alias; | ||
|
||
aliasPath = aliasPath.replace(/\*$/, ''); | ||
const relativeAliasPath = path.relative(paths.appPath, aliasPath); | ||
const target = '<rootDir>/' + relativeAliasPath; | ||
|
||
prev[match] = target + (endsWithWilcard ? '/$1' : ''); | ||
return prev; | ||
}, {}); | ||
} | ||
|
||
function getModules() { | ||
// Check if TypeScript is setup | ||
const useTypeScript = fs.existsSync(paths.appTsConfig); | ||
const hasJsConfig = fs.existsSync(paths.appJsConfig); | ||
|
||
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 || {}; | ||
|
||
const aliases = getAliases(options); | ||
const jestAliases = getJestAliases(aliases); | ||
const webpackAliases = getWebpackAliases(aliases); | ||
const additionalModulePath = getAdditionalModulePath(options); | ||
|
||
return { | ||
aliases: aliases, | ||
jestAliases: jestAliases, | ||
webpackAliases: webpackAliases, | ||
additionalModulePath: additionalModulePath, | ||
useTypeScript, | ||
}; | ||
} | ||
|
||
module.exports = getModules(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
packages/react-scripts/fixtures/kitchensink/integration/config.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"compilerOptions": { | ||
"baseUrl": "src", | ||
"paths": { | ||
"@*": ["*"] | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
packages/react-scripts/fixtures/kitchensink/src/features/config/Alias.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean this file? https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/bin/react-scripts.js
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Timer in the bin wrapper the
.env
files are not parsed yet, so that fails to check theNODE_PATH
properly. What's your concern with having it in this file?There was a problem hiding this comment.
Choose a reason for hiding this comment
The 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).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can just add it to
start.js
, omitting it from thebuild
andtest
scripts.