Skip to content

WIP - Use Pundle as the module bundler #268

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

Closed
wants to merge 9 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion config/babel.dev.js
Original file line number Diff line number Diff line change
@@ -9,7 +9,6 @@

module.exports = {
babelrc: false,
cacheDirectory: true,
presets: [
'babel-preset-es2015',
'babel-preset-es2016',
103 changes: 0 additions & 103 deletions config/webpack.config.dev.js

This file was deleted.

144 changes: 0 additions & 144 deletions config/webpack.config.prod.js

This file was deleted.

19 changes: 5 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
@@ -30,7 +30,6 @@
"autoprefixer": "6.3.7",
"babel-core": "6.11.4",
"babel-eslint": "6.1.2",
"babel-loader": "6.2.4",
"babel-plugin-syntax-trailing-function-commas": "6.8.0",
"babel-plugin-transform-class-properties": "6.11.5",
"babel-plugin-transform-object-rest-spread": "6.8.0",
@@ -39,32 +38,24 @@
"babel-preset-es2015": "6.9.0",
"babel-preset-es2016": "6.11.3",
"babel-preset-react": "6.11.1",
"babel-pundle": "1.0.0",
"babel-runtime": "6.11.6",
"chalk": "1.1.3",
"cross-spawn": "4.0.0",
"css-loader": "0.23.1",
"detect-port": "0.1.4",
"eslint": "3.1.1",
"eslint-loader": "1.4.1",
"eslint-plugin-flow-vars": "0.5.0",
"eslint-plugin-import": "1.12.0",
"eslint-plugin-jsx-a11y": "2.0.1",
"eslint-plugin-react": "5.2.2",
"extract-text-webpack-plugin": "1.0.1",
"file-loader": "0.9.0",
"filesize": "3.3.0",
"fs-extra": "0.30.0",
"gzip-size": "3.0.0",
"html-webpack-plugin": "2.22.0",
"json-loader": "0.5.4",
"mkdirp": "^0.5.1",
"opn": "4.0.2",
"postcss-loader": "0.9.1",
"process-bootstrap": "1.0.0",
"promise": "7.1.1",
"pundle": "1.1.0",
"pundle-dev": "1.1.0",
"rimraf": "2.5.4",
"style-loader": "0.13.1",
"url-loader": "0.5.7",
"webpack": "1.13.1",
"webpack-dev-server": "1.14.1",
"whatwg-fetch": "1.0.0"
},
"devDependencies": {
65 changes: 41 additions & 24 deletions scripts/build.js
Original file line number Diff line number Diff line change
@@ -10,34 +10,50 @@
process.env.NODE_ENV = 'production';

var fs = require('fs');
var filesize = require('filesize');
var gzipSize = require('gzip-size');
var path = require('path');
var mkdirp = require('mkdirp')
var Pundle = require('pundle')
var rimrafSync = require('rimraf').sync;
var webpack = require('webpack');
var config = require('../config/webpack.config.prod');
var paths = require('../config/paths');

// Remove all content but keep the directory so that
// if you're in it, you don't end up in Trash
rimrafSync(paths.appBuild + '/*');

function logBuildSize(assets, extension) {
for (var i = 0; i < assets.length; i++) {
var asset = assets[i];
if (asset.name.endsWith('.' + extension)) {
var fileContents = fs.readFileSync(paths.appBuild + '/' + asset.name);
console.log('Size (gzipped) of ' + asset.name + ': ' + filesize(gzipSize.sync(fileContents)));
}
}
}

webpack(config).run(function(err, stats) {
if (err) {
console.error('Failed to create a production build. Reason:');
console.error(err.message || err);
process.exit(1);
}
var pundle = new Pundle({
entry: [require.resolve('../config/polyfills'), 'index.js'],
rootDirectory: path.normalize(path.join(__dirname, '../template/src')),
pathType: 'filePath',
moduleDirectories: ['node_modules'],
})

pundle.loadPlugins([
[require.resolve('babel-pundle'), {
config: require('../config/babel.prod'),
}],
]).then(function() {
return pundle.compile()
}).then(function() {
return new Promise(function(resolve, reject) {
mkdirp(paths.appBuild, function(error) {
if (error) {
reject(error)
} else {
resolve()
}
})
})
}).then(function() {
return new Promise(function(resolve, reject) {
fs.writeFile(path.join(paths.appBuild, 'bundle.js'), pundle.generate().contents, function(error) {
if (error) {
reject(error)
} else {
resolve()
}
})
})
}).then(function() {
var openCommand = process.platform === 'win32' ? 'start' : 'open';
var homepagePath = require(paths.appPackageJson).homepage;
console.log('Successfully generated a bundle in the build folder!');
@@ -61,9 +77,10 @@ webpack(config).run(function(err, stats) {
console.log(' pushstate-server build');
console.log(' ' + openCommand + ' http://localhost:9000');
console.log();
var assets = stats.toJson()['assets'];
logBuildSize(assets, 'js');
logBuildSize(assets, 'css');
}
console.log('The bundle is optimized and ready to be deployed to production.');
});
}, function(err) {
console.error('Failed to create a production build. Reason:');
console.error(err.message || err);
process.exitCode = 1
})
225 changes: 44 additions & 181 deletions scripts/start.js
Original file line number Diff line number Diff line change
@@ -9,184 +9,47 @@

process.env.NODE_ENV = 'development';

var path = require('path');
var chalk = require('chalk');
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var execSync = require('child_process').execSync;
var opn = require('opn');
var detect = require('detect-port');
var prompt = require('./utils/prompt');
var config = require('../config/webpack.config.dev');

// Tools like Cloud9 rely on this
var DEFAULT_PORT = process.env.PORT || 3000;
var compiler;

// TODO: hide this behind a flag and eliminate dead code on eject.
// This shouldn't be exposed to the user.
var handleCompile;
var isSmokeTest = process.argv.some(arg => arg.indexOf('--smoke-test') > -1);
if (isSmokeTest) {
handleCompile = function (err, stats) {
if (err || stats.hasErrors() || stats.hasWarnings()) {
process.exit(1);
} else {
process.exit(0);
}
};
}

var friendlySyntaxErrorLabel = 'Syntax error:';

function isLikelyASyntaxError(message) {
return message.indexOf(friendlySyntaxErrorLabel) !== -1;
}

// This is a little hacky.
// It would be easier if webpack provided a rich error object.

function formatMessage(message) {
return message
// Make some common errors shorter:
.replace(
// Babel syntax error
'Module build failed: SyntaxError:',
friendlySyntaxErrorLabel
)
.replace(
// Webpack file not found error
/Module not found: Error: Cannot resolve 'file' or 'directory'/,
'Module not found:'
)
// Internal stacks are generally useless so we strip them
.replace(/^\s*at\s.*:\d+:\d+[\s\)]*\n/gm, '') // at ... ...:x:y
// Webpack loader names obscure CSS filenames
.replace('./~/css-loader!./~/postcss-loader!', '');
}

function clearConsole() {
process.stdout.write('\x1bc');
}

function setupCompiler(port) {
compiler = webpack(config, handleCompile);

compiler.plugin('invalid', function() {
clearConsole();
console.log('Compiling...');
});

compiler.plugin('done', function(stats) {
clearConsole();
var hasErrors = stats.hasErrors();
var hasWarnings = stats.hasWarnings();
if (!hasErrors && !hasWarnings) {
console.log(chalk.green('Compiled successfully!'));
console.log();
console.log('The app is running at http://localhost:' + port + '/');
console.log();
return;
}

var json = stats.toJson();
var formattedErrors = json.errors.map(message =>
'Error in ' + formatMessage(message)
);
var formattedWarnings = json.warnings.map(message =>
'Warning in ' + formatMessage(message)
);

if (hasErrors) {
console.log(chalk.red('Failed to compile.'));
console.log();
if (formattedErrors.some(isLikelyASyntaxError)) {
// If there are any syntax errors, show just them.
// This prevents a confusing ESLint parsing error
// preceding a much more useful Babel syntax error.
formattedErrors = formattedErrors.filter(isLikelyASyntaxError);
}
formattedErrors.forEach(message => {
console.log(message);
console.log();
});
// If errors exist, ignore warnings.
return;
}

if (hasWarnings) {
console.log(chalk.yellow('Compiled with warnings.'));
console.log();
formattedWarnings.forEach(message => {
console.log(message);
console.log();
});

console.log('You may use special comments to disable some warnings.');
console.log('Use ' + chalk.yellow('// eslint-disable-next-line') + ' to ignore the next line.');
console.log('Use ' + chalk.yellow('/* eslint-disable */') + ' to ignore all warnings in a file.');
}
});
}

function openBrowser(port) {
if (process.platform === 'darwin') {
try {
// Try our best to reuse existing tab
// on OS X Google Chrome with AppleScript
execSync('ps cax | grep "Google Chrome"');
execSync(
'osascript ' +
path.resolve(__dirname, './utils/chrome.applescript') +
' http://localhost:' + port + '/'
);
return;
} catch (err) {
// Ignore errors.
}
}
// Fallback to opn
// (It will always open new tab)
opn('http://localhost:' + port + '/');
}

function runDevServer(port) {
new WebpackDevServer(compiler, {
historyApiFallback: true,
hot: true, // Note: only CSS is currently hot reloaded
publicPath: config.output.publicPath,
quiet: true
}).listen(port, (err, result) => {
if (err) {
return console.log(err);
}

clearConsole();
console.log(chalk.cyan('Starting the development server...'));
console.log();
openBrowser(port);
});
}

function run(port) {
setupCompiler(port);
runDevServer(port);
}

detect(DEFAULT_PORT).then(port => {
if (port === DEFAULT_PORT) {
run(port);
return;
}

clearConsole();
var question =
chalk.yellow('Something is already running at port ' + DEFAULT_PORT + '.') +
'\n\nWould you like to run the app at another port instead?';

prompt(question, true).then(shouldChangePort => {
if (shouldChangePort) {
run(port);
}
});
});
require('process-bootstrap')('react-app', 'REACT-APP')

var Path = require('path');
var debug = require('debug');
var Server = require('pundle-dev');
var debugInfo = debug('REACT-APP:Info');
var debugError = debug('REACT-APP:Error');

var port = process.env.PORT || 3000;

var server = new Server({
pundle: {
entry: [require.resolve('../config/polyfills'), 'index.js'],
rootDirectory: Path.normalize(Path.join(__dirname, '../template/src')),
pathType: 'filePath',
moduleDirectories: ['node_modules'],
},
server: {
port: port,
error: debugError,
ready() {
debugInfo('Ready')
},
generated() {
debugInfo('Regenerated')
},
hmr: true,
sourceMap: true,
sourceRoot: Path.normalize(Path.join(__dirname, '../template')),
},
watcher: {},
generator: {
sourceMap: true,
wrapper: 'hmr',
},
})

server.pundle.loadPlugins([
[require.resolve('babel-pundle'), {
config: require('../config/babel.dev'),
}],
]).then(function() {
return server.activate()
}).catch(debugError)
1 change: 1 addition & 0 deletions template/index.html
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@
</head>
<body>
<div id="root"></div>
<script src="/_/bundle.js"></script>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
4 changes: 1 addition & 3 deletions template/src/App.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<img className="App-logo" alt="logo" />
<h2>Welcome to React</h2>
</div>
<p className="App-intro">
1 change: 0 additions & 1 deletion template/src/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';

ReactDOM.render(
<App />,