Commit dfecfea6 authored by Dan Abramov's avatar Dan Abramov Committed by GitHub
Browse files

Unroll indirection in paths (#191)

parent d69ee278
Showing with 134 additions and 102 deletions
+134 -102
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
// TODO: we can split this file into several files (pre-eject, post-eject, test)
// and use those instead. This way we don't need to branch here.
var path = require('path');
// True when used as a dependency, false after ejecting
var isInNodeModules = (
'node_modules' ===
path.basename(path.resolve(path.join(__dirname, '..', '..')))
);
// Are we developing create-react-app locally?
var isInCreateReactAppSource = (
process.argv.some(arg => arg.indexOf('--debug-template') > -1)
);
function resolve(relativePath) {
return path.resolve(__dirname, relativePath);
}
if (isInCreateReactAppSource) {
// create-react-app development: we're in ./config/
module.exports = {
appBuild: resolve('../build'),
appHtml: resolve('../template/index.html'),
appFavicon: resolve('../template/favicon.ico'),
appPackageJson: resolve('../package.json'),
appSrc: resolve('../template/src'),
appNodeModules: resolve('../node_modules'),
ownNodeModules: resolve('../node_modules')
};
} else if (isInNodeModules) {
// before eject: we're in ./node_modules/react-scripts/config/
module.exports = {
appBuild: resolve('../../../build'),
appHtml: resolve('../../../index.html'),
appFavicon: resolve('../../../favicon.ico'),
appPackageJson: resolve('../../../package.json'),
appSrc: resolve('../../../src'),
appNodeModules: resolve('../..'),
// this is empty with npm3 but node resolution searches higher anyway:
ownNodeModules: resolve('../node_modules')
};
} else {
// after eject: we're in ./config/
module.exports = {
appBuild: resolve('../build'),
appHtml: resolve('../index.html'),
appFavicon: resolve('../favicon.ico'),
appPackageJson: resolve('../package.json'),
appSrc: resolve('../src'),
appNodeModules: resolve('../node_modules'),
ownNodeModules: resolve('../node_modules')
};
}
...@@ -11,35 +11,18 @@ var path = require('path'); ...@@ -11,35 +11,18 @@ var path = require('path');
var autoprefixer = require('autoprefixer'); var autoprefixer = require('autoprefixer');
var webpack = require('webpack'); var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin'); var HtmlWebpackPlugin = require('html-webpack-plugin');
var paths = require('./paths');
// TODO: hide this behind a flag and eliminate dead code on eject.
// This shouldn't be exposed to the user.
var isInNodeModules = 'node_modules' ===
path.basename(path.resolve(path.join(__dirname, '..', '..')));
var relativePath = isInNodeModules ? '../../..' : '..';
var isInDebugMode = process.argv.some(arg =>
arg.indexOf('--debug-template') > -1
);
if (isInDebugMode) {
relativePath = '../template';
}
var srcPath = path.resolve(__dirname, relativePath, 'src');
var rootNodeModulesPath = path.resolve(__dirname, relativePath, 'node_modules');
var nodeModulesPath = path.join(__dirname, '..', 'node_modules');
var indexHtmlPath = path.resolve(__dirname, relativePath, 'index.html');
var faviconPath = path.resolve(__dirname, relativePath, 'favicon.ico');
var buildPath = path.join(__dirname, isInNodeModules ? '../../..' : '..', 'build');
module.exports = { module.exports = {
devtool: 'eval', devtool: 'eval',
entry: [ entry: [
require.resolve('webpack-dev-server/client') + '?http://localhost:3000', require.resolve('webpack-dev-server/client') + '?http://localhost:3000',
require.resolve('webpack/hot/dev-server'), require.resolve('webpack/hot/dev-server'),
path.join(srcPath, 'index') path.join(paths.appSrc, 'index')
], ],
output: { output: {
// Next line is not used in dev but WebpackDevServer crashes without it: // Next line is not used in dev but WebpackDevServer crashes without it:
path: buildPath, path: paths.appBuild,
pathinfo: true, pathinfo: true,
filename: 'bundle.js', filename: 'bundle.js',
publicPath: '/' publicPath: '/'
...@@ -48,7 +31,7 @@ module.exports = { ...@@ -48,7 +31,7 @@ module.exports = {
extensions: ['', '.js'], extensions: ['', '.js'],
}, },
resolveLoader: { resolveLoader: {
root: nodeModulesPath, root: paths.ownNodeModules,
moduleTemplates: ['*-loader'] moduleTemplates: ['*-loader']
}, },
module: { module: {
...@@ -56,31 +39,34 @@ module.exports = { ...@@ -56,31 +39,34 @@ module.exports = {
{ {
test: /\.js$/, test: /\.js$/,
loader: 'eslint', loader: 'eslint',
include: srcPath, include: paths.appSrc,
} }
], ],
loaders: [ loaders: [
{ {
test: /\.js$/, test: /\.js$/,
include: srcPath, include: paths.appSrc,
loader: 'babel', loader: 'babel',
query: require('./babel.dev') query: require('./babel.dev')
}, },
{ {
test: /\.css$/, test: /\.css$/,
include: [srcPath, rootNodeModulesPath], include: [paths.appSrc, paths.appNodeModules],
loader: 'style!css!postcss' loader: 'style!css!postcss'
}, },
{ {
test: /\.json$/, test: /\.json$/,
include: [paths.appSrc, paths.appNodeModules],
loader: 'json' loader: 'json'
}, },
{ {
test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2)$/, test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2)$/,
include: [paths.appSrc, paths.appNodeModules],
loader: 'file', loader: 'file',
}, },
{ {
test: /\.(mp4|webm)$/, test: /\.(mp4|webm)$/,
include: [paths.appSrc, paths.appNodeModules],
loader: 'url?limit=10000' loader: 'url?limit=10000'
} }
] ]
...@@ -95,8 +81,8 @@ module.exports = { ...@@ -95,8 +81,8 @@ module.exports = {
plugins: [ plugins: [
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
inject: true, inject: true,
template: indexHtmlPath, template: paths.appHtml,
favicon: faviconPath, favicon: paths.appFavicon,
}), }),
new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"development"' }), new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"development"' }),
// Note: only CSS is currently hot reloaded // Note: only CSS is currently hot reloaded
......
...@@ -13,22 +13,9 @@ var webpack = require('webpack'); ...@@ -13,22 +13,9 @@ var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin'); var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin'); var ExtractTextPlugin = require('extract-text-webpack-plugin');
var url = require('url'); var url = require('url');
var paths = require('./paths');
// TODO: hide this behind a flag and eliminate dead code on eject. var homepagePath = require(paths.appPackageJson).homepage;
// This shouldn't be exposed to the user.
var isInNodeModules = 'node_modules' ===
path.basename(path.resolve(path.join(__dirname, '..', '..')));
var relativePath = isInNodeModules ? '../../..' : '..';
if (process.argv[2] === '--debug-template') {
relativePath = '../template';
}
var srcPath = path.resolve(__dirname, relativePath, 'src');
var rootNodeModulesPath = path.resolve(__dirname, relativePath, 'node_modules');
var nodeModulesPath = path.join(__dirname, '..', 'node_modules');
var indexHtmlPath = path.resolve(__dirname, relativePath, 'index.html');
var faviconPath = path.resolve(__dirname, relativePath, 'favicon.ico');
var buildPath = path.join(__dirname, isInNodeModules ? '../../..' : '..', 'build');
var homepagePath = require(path.resolve(__dirname, relativePath, 'package.json')).homepage;
var publicPath = homepagePath ? url.parse(homepagePath).pathname : '/'; var publicPath = homepagePath ? url.parse(homepagePath).pathname : '/';
if (!publicPath.endsWith('/')) { if (!publicPath.endsWith('/')) {
// Prevents incorrect paths in file-loader // Prevents incorrect paths in file-loader
...@@ -38,9 +25,9 @@ if (!publicPath.endsWith('/')) { ...@@ -38,9 +25,9 @@ if (!publicPath.endsWith('/')) {
module.exports = { module.exports = {
bail: true, bail: true,
devtool: 'source-map', devtool: 'source-map',
entry: path.join(srcPath, 'index'), entry: path.join(paths.appSrc, 'index'),
output: { output: {
path: buildPath, path: paths.appBuild,
filename: '[name].[chunkhash].js', filename: '[name].[chunkhash].js',
chunkFilename: '[name].[chunkhash].chunk.js', chunkFilename: '[name].[chunkhash].chunk.js',
publicPath: publicPath publicPath: publicPath
...@@ -49,7 +36,7 @@ module.exports = { ...@@ -49,7 +36,7 @@ module.exports = {
extensions: ['', '.js'], extensions: ['', '.js'],
}, },
resolveLoader: { resolveLoader: {
root: nodeModulesPath, root: paths.ownNodeModules,
moduleTemplates: ['*-loader'] moduleTemplates: ['*-loader']
}, },
module: { module: {
...@@ -57,19 +44,19 @@ module.exports = { ...@@ -57,19 +44,19 @@ module.exports = {
{ {
test: /\.js$/, test: /\.js$/,
loader: 'eslint', loader: 'eslint',
include: srcPath include: paths.appSrc
} }
], ],
loaders: [ loaders: [
{ {
test: /\.js$/, test: /\.js$/,
include: srcPath, include: paths.appSrc,
loader: 'babel', loader: 'babel',
query: require('./babel.prod') query: require('./babel.prod')
}, },
{ {
test: /\.css$/, test: /\.css$/,
include: [srcPath, rootNodeModulesPath], include: [paths.appSrc, paths.appNodeModules],
// Disable autoprefixer in css-loader itself: // Disable autoprefixer in css-loader itself:
// https://github.com/webpack/css-loader/issues/281 // https://github.com/webpack/css-loader/issues/281
// We already have it thanks to postcss. // We already have it thanks to postcss.
...@@ -77,14 +64,17 @@ module.exports = { ...@@ -77,14 +64,17 @@ module.exports = {
}, },
{ {
test: /\.json$/, test: /\.json$/,
include: [paths.appSrc, paths.appNodeModules],
loader: 'json' loader: 'json'
}, },
{ {
test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2)$/, test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2)$/,
include: [paths.appSrc, paths.appNodeModules],
loader: 'file', loader: 'file',
}, },
{ {
test: /\.(mp4|webm)$/, test: /\.(mp4|webm)$/,
include: [paths.appSrc, paths.appNodeModules],
loader: 'url?limit=10000' loader: 'url?limit=10000'
} }
] ]
...@@ -101,8 +91,8 @@ module.exports = { ...@@ -101,8 +91,8 @@ module.exports = {
plugins: [ plugins: [
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
inject: true, inject: true,
template: indexHtmlPath, template: paths.appHtml,
favicon: faviconPath, favicon: paths.appFavicon,
minify: { minify: {
removeComments: true, removeComments: true,
collapseWhitespace: true, collapseWhitespace: true,
......
...@@ -9,20 +9,12 @@ ...@@ -9,20 +9,12 @@
process.env.NODE_ENV = 'production'; process.env.NODE_ENV = 'production';
var path = require('path');
var rimrafSync = require('rimraf').sync; var rimrafSync = require('rimraf').sync;
var webpack = require('webpack'); var webpack = require('webpack');
var config = require('../config/webpack.config.prod'); var config = require('../config/webpack.config.prod');
var paths = require('../config/paths');
var isInNodeModules = 'node_modules' === rimrafSync(paths.appBuild);
path.basename(path.resolve(path.join(__dirname, '..', '..')));
var relative = isInNodeModules ? '../../..' : '..';
if (process.argv[2] === '--debug-template') {
relative = '../template';
}
var packageJsonPath = path.join(__dirname, relative, 'package.json');
var buildPath = path.join(__dirname, relative, 'build');
rimrafSync(buildPath);
webpack(config).run(function(err, stats) { webpack(config).run(function(err, stats) {
if (err) { if (err) {
...@@ -32,7 +24,7 @@ webpack(config).run(function(err, stats) { ...@@ -32,7 +24,7 @@ webpack(config).run(function(err, stats) {
} }
var openCommand = process.platform === 'win32' ? 'start' : 'open'; var openCommand = process.platform === 'win32' ? 'start' : 'open';
var homepagePath = require(packageJsonPath).homepage; var homepagePath = require(paths.appPackageJson).homepage;
console.log('Successfully generated a bundle in the build folder!'); console.log('Successfully generated a bundle in the build folder!');
if (homepagePath) { if (homepagePath) {
console.log('You can now deploy it to ' + homepagePath + '.'); console.log('You can now deploy it to ' + homepagePath + '.');
......
...@@ -12,6 +12,7 @@ var path = require('path'); ...@@ -12,6 +12,7 @@ var path = require('path');
var rl = require('readline'); var rl = require('readline');
var rimrafSync = require('rimraf').sync; var rimrafSync = require('rimraf').sync;
var spawnSync = require('cross-spawn').sync; var spawnSync = require('cross-spawn').sync;
var paths = require('../config/paths');
var prompt = function(question, cb) { var prompt = function(question, cb) {
var rlInterface = rl.createInterface({ var rlInterface = rl.createInterface({
...@@ -37,14 +38,15 @@ prompt('Are you sure you want to eject? This action is permanent. [y/N]', functi ...@@ -37,14 +38,15 @@ prompt('Are you sure you want to eject? This action is permanent. [y/N]', functi
console.log('Ejecting...'); console.log('Ejecting...');
console.log(); console.log();
var selfPath = path.join(__dirname, '..'); var ownPath = path.join(__dirname, '..');
var hostPath = path.join(selfPath, '..', '..'); var appPath = path.join(ownPath, '..', '..');
var files = [ var files = [
path.join('config', 'babel.dev.js'), path.join('config', 'babel.dev.js'),
path.join('config', 'babel.prod.js'), path.join('config', 'babel.prod.js'),
path.join('config', 'flow', 'css.js.flow'), path.join('config', 'flow', 'css.js.flow'),
path.join('config', 'flow', 'file.js.flow'), path.join('config', 'flow', 'file.js.flow'),
path.join('config', 'eslint.js'), path.join('config', 'eslint.js'),
path.join('config', 'paths.js'),
path.join('config', 'webpack.config.dev.js'), path.join('config', 'webpack.config.dev.js'),
path.join('config', 'webpack.config.prod.js'), path.join('config', 'webpack.config.prod.js'),
path.join('scripts', 'build.js'), path.join('scripts', 'build.js'),
...@@ -52,9 +54,9 @@ prompt('Are you sure you want to eject? This action is permanent. [y/N]', functi ...@@ -52,9 +54,9 @@ prompt('Are you sure you want to eject? This action is permanent. [y/N]', functi
path.join('scripts', 'openChrome.applescript') path.join('scripts', 'openChrome.applescript')
]; ];
// Ensure that the host folder is clean and we won't override any files // Ensure that the app folder is clean and we won't override any files
files.forEach(function(file) { files.forEach(function(file) {
if (fs.existsSync(path.join(hostPath, file))) { if (fs.existsSync(path.join(appPath, file))) {
console.error( console.error(
'`' + file + '` already exists in your app folder. We cannot ' + '`' + file + '` already exists in your app folder. We cannot ' +
'continue as you would lose all the changes in that file or directory. ' + 'continue as you would lose all the changes in that file or directory. ' +
...@@ -66,58 +68,58 @@ prompt('Are you sure you want to eject? This action is permanent. [y/N]', functi ...@@ -66,58 +68,58 @@ prompt('Are you sure you want to eject? This action is permanent. [y/N]', functi
}); });
// Copy the files over // Copy the files over
fs.mkdirSync(path.join(hostPath, 'config')); fs.mkdirSync(path.join(appPath, 'config'));
fs.mkdirSync(path.join(hostPath, 'config', 'flow')); fs.mkdirSync(path.join(appPath, 'config', 'flow'));
fs.mkdirSync(path.join(hostPath, 'scripts')); fs.mkdirSync(path.join(appPath, 'scripts'));
files.forEach(function(file) { files.forEach(function(file) {
console.log('Copying ' + file + ' to ' + hostPath); console.log('Copying ' + file + ' to ' + appPath);
var content = fs var content = fs
.readFileSync(path.join(selfPath, file), 'utf8') .readFileSync(path.join(ownPath, file), 'utf8')
// Remove license header from JS // Remove license header from JS
.replace(/^\/\*\*(\*(?!\/)|[^*])*\*\//, '') .replace(/^\/\*\*(\*(?!\/)|[^*])*\*\//, '')
// Remove license header from AppleScript // Remove license header from AppleScript
.replace(/^--.*\n/gm, '') .replace(/^--.*\n/gm, '')
.trim() + '\n'; .trim() + '\n';
fs.writeFileSync(path.join(hostPath, file), content); fs.writeFileSync(path.join(appPath, file), content);
}); });
console.log(); console.log();
var selfPackage = require(path.join(selfPath, 'package.json')); var ownPackage = require(path.join(ownPath, 'package.json'));
var hostPackage = require(path.join(hostPath, 'package.json')); var appPackage = require(path.join(appPath, 'package.json'));
console.log('Removing dependency: react-scripts'); console.log('Removing dependency: react-scripts');
delete hostPackage.devDependencies['react-scripts']; delete appPackage.devDependencies['react-scripts'];
Object.keys(selfPackage.dependencies).forEach(function (key) { Object.keys(ownPackage.dependencies).forEach(function (key) {
// For some reason optionalDependencies end up in dependencies after install // For some reason optionalDependencies end up in dependencies after install
if (selfPackage.optionalDependencies[key]) { if (ownPackage.optionalDependencies[key]) {
return; return;
} }
console.log('Adding dependency: ' + key); console.log('Adding dependency: ' + key);
hostPackage.devDependencies[key] = selfPackage.dependencies[key]; appPackage.devDependencies[key] = ownPackage.dependencies[key];
}); });
console.log('Updating scripts'); console.log('Updating scripts');
Object.keys(hostPackage.scripts).forEach(function (key) { Object.keys(appPackage.scripts).forEach(function (key) {
hostPackage.scripts[key] = 'node ./scripts/' + key + '.js' appPackage.scripts[key] = 'node ./scripts/' + key + '.js'
}); });
delete hostPackage.scripts['eject']; delete appPackage.scripts['eject'];
// explicitly specify ESLint config path for editor plugins // explicitly specify ESLint config path for editor plugins
hostPackage.eslintConfig = { appPackage.eslintConfig = {
extends: './config/eslint.js', extends: './config/eslint.js',
}; };
console.log('Writing package.json'); console.log('Writing package.json');
fs.writeFileSync( fs.writeFileSync(
path.join(hostPath, 'package.json'), path.join(appPath, 'package.json'),
JSON.stringify(hostPackage, null, 2) JSON.stringify(appPackage, null, 2)
); );
console.log(); console.log();
console.log('Running npm install...'); console.log('Running npm install...');
rimrafSync(selfPath); rimrafSync(ownPath);
spawnSync('npm', ['install'], {stdio: 'inherit'}); spawnSync('npm', ['install'], {stdio: 'inherit'});
console.log('Ejected successfully!'); console.log('Ejected successfully!');
console.log(); console.log();
......
...@@ -11,40 +11,40 @@ var fs = require('fs-extra'); ...@@ -11,40 +11,40 @@ var fs = require('fs-extra');
var path = require('path'); var path = require('path');
var spawn = require('cross-spawn'); var spawn = require('cross-spawn');
module.exports = function(hostPath, appName, verbose, originalDirectory) { module.exports = function(appPath, appName, verbose, originalDirectory) {
var selfPath = path.join(hostPath, 'node_modules', 'react-scripts'); var ownPath = path.join(appPath, 'node_modules', 'react-scripts');
var hostPackage = require(path.join(hostPath, 'package.json')); var appPackage = require(path.join(appPath, 'package.json'));
var selfPackage = require(path.join(selfPath, 'package.json')); var ownPackage = require(path.join(ownPath, 'package.json'));
// Copy over some of the devDependencies // Copy over some of the devDependencies
hostPackage.dependencies = hostPackage.dependencies || {}; appPackage.dependencies = appPackage.dependencies || {};
['react', 'react-dom'].forEach(function (key) { ['react', 'react-dom'].forEach(function (key) {
hostPackage.dependencies[key] = selfPackage.devDependencies[key]; appPackage.dependencies[key] = ownPackage.devDependencies[key];
}); });
// Setup the script rules // Setup the script rules
hostPackage.scripts = {}; appPackage.scripts = {};
['start', 'build', 'eject'].forEach(function(command) { ['start', 'build', 'eject'].forEach(function(command) {
hostPackage.scripts[command] = 'react-scripts ' + command; appPackage.scripts[command] = 'react-scripts ' + command;
}); });
// explicitly specify ESLint config path for editor plugins // explicitly specify ESLint config path for editor plugins
hostPackage.eslintConfig = { appPackage.eslintConfig = {
extends: './node_modules/react-scripts/config/eslint.js', extends: './node_modules/react-scripts/config/eslint.js',
}; };
fs.writeFileSync( fs.writeFileSync(
path.join(hostPath, 'package.json'), path.join(appPath, 'package.json'),
JSON.stringify(hostPackage, null, 2) JSON.stringify(appPackage, null, 2)
); );
// Copy the files for the user // Copy the files for the user
fs.copySync(path.join(selfPath, 'template'), hostPath); fs.copySync(path.join(ownPath, 'template'), appPath);
// Rename gitignore after the fact to prevent npm from renaming it to .npmignore // Rename gitignore after the fact to prevent npm from renaming it to .npmignore
// See: https://github.com/npm/npm/issues/1862 // See: https://github.com/npm/npm/issues/1862
fs.move(path.join(hostPath, 'gitignore'), path.join(hostPath, '.gitignore'), []); fs.move(path.join(appPath, 'gitignore'), path.join(appPath, '.gitignore'), []);
// Run another npm install for react and react-dom // Run another npm install for react and react-dom
console.log('Installing react and react-dom from npm...'); console.log('Installing react and react-dom from npm...');
...@@ -65,13 +65,13 @@ module.exports = function(hostPath, appName, verbose, originalDirectory) { ...@@ -65,13 +65,13 @@ module.exports = function(hostPath, appName, verbose, originalDirectory) {
// backward compatibility with old global-cli's. // backward compatibility with old global-cli's.
var cdpath; var cdpath;
if (originalDirectory && if (originalDirectory &&
path.join(originalDirectory, appName) === hostPath) { path.join(originalDirectory, appName) === appPath) {
cdpath = appName; cdpath = appName;
} else { } else {
cdpath = hostPath; cdpath = appPath;
} }
console.log('Success! Created ' + appName + ' at ' + hostPath + '.'); console.log('Success! Created ' + appName + ' at ' + appPath + '.');
console.log('Inside that directory, you can run several commands:'); console.log('Inside that directory, you can run several commands:');
console.log(); console.log();
console.log(' * npm start: Starts the development server.'); console.log(' * npm start: Starts the development server.');
......
...@@ -20,9 +20,7 @@ var opn = require('opn'); ...@@ -20,9 +20,7 @@ var opn = require('opn');
// TODO: hide this behind a flag and eliminate dead code on eject. // TODO: hide this behind a flag and eliminate dead code on eject.
// This shouldn't be exposed to the user. // This shouldn't be exposed to the user.
var handleCompile; var handleCompile;
var isSmokeTest = process.argv.some(arg => var isSmokeTest = process.argv.some(arg => arg.indexOf('--smoke-test') > -1);
arg.indexOf('--smoke-test') > -1
);
if (isSmokeTest) { if (isSmokeTest) {
handleCompile = function (err, stats) { handleCompile = function (err, stats) {
if (err || stats.hasErrors() || stats.hasWarnings()) { if (err || stats.hasErrors() || stats.hasWarnings()) {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment