silki
Version:
Cli tool for build react app, based on generator-react-multipage, create-react-app, roadhog. Support react multiple pages app develop.
372 lines (299 loc) • 11.8 kB
JavaScript
'use strict';
var _fs = require('fs');
var _fs2 = _interopRequireDefault(_fs);
var _crossSpawn = require('cross-spawn');
var _crossSpawn2 = _interopRequireDefault(_crossSpawn);
var _detectPort = require('detect-port');
var _detectPort2 = _interopRequireDefault(_detectPort);
var _clearConsole = require('react-dev-utils/clearConsole');
var _clearConsole2 = _interopRequireDefault(_clearConsole);
var _getProcessForPort = require('react-dev-utils/getProcessForPort');
var _getProcessForPort2 = _interopRequireDefault(_getProcessForPort);
var _formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
var _formatWebpackMessages2 = _interopRequireDefault(_formatWebpackMessages);
var _openBrowser = require('react-dev-utils/openBrowser');
var _openBrowser2 = _interopRequireDefault(_openBrowser);
var _prompt = require('react-dev-utils/prompt');
var _prompt2 = _interopRequireDefault(_prompt);
var _webpack = require('webpack');
var _webpack2 = _interopRequireDefault(_webpack);
var _connectHistoryApiFallback = require('connect-history-api-fallback');
var _connectHistoryApiFallback2 = _interopRequireDefault(_connectHistoryApiFallback);
var _webpackDevServer = require('webpack-dev-server');
var _webpackDevServer2 = _interopRequireDefault(_webpackDevServer);
var _chalk = require('chalk');
var _chalk2 = _interopRequireDefault(_chalk);
var _chokidar = require('chokidar');
var _chokidar2 = _interopRequireDefault(_chokidar);
var _paths = require('./utils/paths');
var _paths2 = _interopRequireDefault(_paths);
var _getConfig = require('./utils/getConfig');
var _getConfig2 = _interopRequireDefault(_getConfig);
var _applyWebpackConfig = require('./utils/applyWebpackConfig');
var _applyWebpackConfig2 = _interopRequireDefault(_applyWebpackConfig);
var _mock = require('./utils/mock');
var _glob = require('glob');
var _glob2 = _interopRequireDefault(_glob);
var _babel = require('./babel');
var _babel2 = _interopRequireDefault(_babel);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
process.env.NODE_ENV = 'development';
var DEFAULT_PORT = process.env.PORT ? parseInt(process.env.PORT, 10) : 8000;
var isInteractive = process.stdout.isTTY;
var cwd = process.cwd();
var paths = (0, _paths2.default)(cwd);
var compiler = void 0;
var argv = require('yargs').usage('Usage: silki server [options]').option('open', {
type: 'boolean',
describe: 'Open url in browser after started',
default: true
}).option('mod', {
type: 'boolean',
alias: 'm',
describe: 'create youku mod server',
default: false
}).help('h').argv;
var rcConfig = void 0;
var config = void 0;
var entries = void 0; // 页面入口,当页面入口发生变化时(新建页面时),重启服务器
var startTime = void 0; // 计算编译时间
function clearConsoleWrapped() {
if (process.env.CLEAR_CONSOLE !== 'NONE') {
(0, _clearConsole2.default)();
}
}
function readRcConfig() {
try {
rcConfig = (0, _getConfig2.default)(process.env.NODE_ENV, cwd);
} catch (e) {
console.log(_chalk2.default.red('Failed to parse .silkrc config.'));
console.log();
console.log(e.message);
process.exit(1);
}
}
function readWebpackConfig() {
if (argv.mod) {
config = (0, _applyWebpackConfig2.default)(require('./config/webpack.config.mod.dev')(rcConfig, cwd), process.env.NODE_ENV);
} else {
config = (0, _applyWebpackConfig2.default)(require('./config/webpack.config.dev')(rcConfig, cwd), process.env.NODE_ENV);
}
}
function setupCompiler(host, port, protocol) {
compiler = (0, _webpack2.default)(config);
// console.log('silk的webpack配置为===='+JSON.stringify(config));
compiler.plugin('invalid', function () {
if (isInteractive) {
clearConsoleWrapped();
}
console.log('Compiling...');
startTime = new Date().getTime();
});
var isFirstCompile = true;
compiler.plugin('done', function (stats) {
if (isInteractive) {
clearConsoleWrapped();
}
var messages = (0, _formatWebpackMessages2.default)(stats.toJson({}, true));
var isSuccessful = !messages.errors.length && !messages.warnings.length;
var showInstructions = isSuccessful && (isInteractive || isFirstCompile);
(0, _applyWebpackConfig.warnIfExists)();
if (isSuccessful) {
console.log(_chalk2.default.green('Compiled successfully!'));
// show compile time
console.log(_chalk2.default.yellow('Compile time: ' + (new Date().getTime() - startTime) / 1000 + 's'));
}
if (showInstructions) {
console.log();
console.log('The app is running at:');
console.log();
console.log(' ' + _chalk2.default.cyan(protocol + '://' + host + ':' + port + '/'));
console.log();
console.log('Note that the development build is not optimized.');
console.log('To create a production build, use ' + _chalk2.default.cyan('npm run build') + '.');
console.log();
isFirstCompile = false;
}
// If errors exist, only show errors.
if (messages.errors.length) {
console.log(_chalk2.default.red('Failed to compile.'));
console.log();
messages.errors.forEach(function (message) {
console.log(message);
console.log();
});
// Show warnings if no errors were found.
} else if (messages.warnings.length) {
console.log(_chalk2.default.yellow('Compiled with warnings.'));
console.log();
messages.warnings.forEach(function (message) {
console.log(message);
console.log();
});
// Teach some ESLint tricks.
console.log('You may use special comments to disable some warnings.');
console.log('Use ' + _chalk2.default.yellow('// eslint-disable-next-line') + ' to ignore the next line.');
console.log('Use ' + _chalk2.default.yellow('/* eslint-disable */') + ' to ignore all warnings in a file.');
console.log();
}
if (isInteractive) {
(0, _mock.outputError)();
}
});
}
function addMiddleware(devServer) {
var proxy = require(paths.appPackageJson).proxy;
devServer.use((0, _connectHistoryApiFallback2.default)({
disableDotRule: true,
htmlAcceptHeaders: proxy ? ['text/html'] : ['text/html', '*/*']
}));
// TODO: proxy index.html, ...
devServer.use(devServer.middleware);
}
function runDevServer(host, port, protocol) {
var devServer = new _webpackDevServer2.default(compiler, {
disableHostCheck: true, // fix invalid host error
compress: true,
clientLogLevel: 'none',
contentBase: paths.appPublic,
hot: true,
publicPath: config.output.publicPath,
quiet: true,
watchOptions: {
ignored: /node_modules/
},
https: protocol === 'https',
host: host,
proxy: rcConfig.proxy || {},
headers: { "Access-Control-Allow-Origin": "*" }
});
addMiddleware(devServer);
(0, _mock.applyMock)(devServer);
if (isInteractive) {
clearConsoleWrapped();
}
console.log(_chalk2.default.cyan('Starting the development server...'));
devServer.listen(port, '0.0.0.0', function (err) {
if (err) {
return console.log(err);
}
process.send('READY');
if (isInteractive) {
clearConsoleWrapped();
}
console.log(_chalk2.default.cyan('Starting the development server...'));
console.log();
if (isInteractive) {
(0, _mock.outputError)();
}
if (argv.open) {
(0, _openBrowser2.default)(protocol + '://' + host + ':' + port + '/');
}
});
setupWatch(devServer, port);
}
function setupWatch(devServer) {
var files = [paths.resolveApp('.silkrc'), paths.resolveApp('.silkrc.js'), paths.resolveApp('webpack.config.js')].concat(typeof rcConfig.theme === 'string' ? paths.resolveApp(rcConfig.theme) : []);
var watcher = _chokidar2.default.watch(files, {
ignored: /node_modules/,
persistent: true
});
watcher.on('change', function (path) {
console.log(_chalk2.default.green('File ' + path.replace(paths.appDirectory, '.') + ' changed, try to restart server'));
watcher.close();
devServer.close();
process.send('RESTART');
});
/**
* 监听是否有新建页面,(是否有新的页面入口)
* 如果有新建页面,则重启服务器
*/
var globPath = rcConfig.entry ? paths.appDirectory + '/' + rcConfig.entry : paths.appSrc + '/pages/*/index.js';
entries = _glob2.default.sync(globPath);
var pageDir = paths.resolveApp('./src/pages/');
_chokidar2.default.watch(pageDir).on('add', function (path) {
var curEntries = _glob2.default.sync(globPath);
var diff = false;
var len = curEntries.length;
if (len != entries.length) {
diff = true;
} else {
for (var i = 0; i < len; i++) {
if (curEntries[i] != entries[i]) {
diff = true;
break;
}
}
}
if (diff) {
console.log(_chalk2.default.green('Added ' + path.replace(paths.appDirectory, '.') + ' , try to restart server'));
watcher.close();
devServer.close();
process.send('RESTART');
entries = curEntries;
}
});
}
// watch 需要 babel 直接编译的文件
function watchBabel() {
var globPath = paths.appSrc + '/workers/**/*.js';
var babelCmd = rcConfig.babelCmd;
var watchDir = babelCmd ? babelCmd.split(/\s+/)[1] : 'src/workers';
if (babelCmd) {
globPath = watchDir + '/**/*.js';
}
entries = _glob2.default.sync(globPath);
watchDir = paths.resolveApp(watchDir);
var watcher = _chokidar2.default.watch(watchDir).on('change', function (path) {
console.log(_chalk2.default.green('Changed ' + path.replace(paths.appDirectory, '.') + ' , try to recompile'));
(0, _babel2.default)(rcConfig);
});
}
function run(port) {
startTime = new Date().getTime();
var protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
var host = process.env.HOST || 'localhost';
setupCompiler(host, port, protocol);
runDevServer(host, port, protocol);
}
function init() {
readRcConfig();
// 如果配置了dll功能,首先看是否存在了 vendor.dll.js 文件,如果不存在,则需要先运行 silk dll
if (rcConfig.dll) {
if (!_fs2.default.existsSync(paths.appPublic + '/vendor.dll.js')) {
console.log(' ️⚠️ ️You are using webpack dll. ');
console.log('Please run ' + _chalk2.default.yellow('silk dll') + ' first');
console.log('and add code ' + _chalk2.default.green('<script src="/vendor.dll.js"></script>') + ' to your template-dev.html ');
return;
}
}
readWebpackConfig();
// 处理需要直接用 babel 编译的文件,无需 webpack 处理
if (rcConfig.useBabelCmd) {
(0, _babel2.default)(rcConfig);
watchBabel();
}
// 命令行设置端口优先级最高,用户配置优先级次之,默认端口8000
DEFAULT_PORT = process.env.PORT ? parseInt(process.env.PORT, 10) : rcConfig.port;
(0, _detectPort2.default)(DEFAULT_PORT).then(function (port) {
// 当前端口未被占用
if (port == DEFAULT_PORT) {
run(port);
return;
}
// 当前端口被占用时,分配一个新的端口 port
if (isInteractive) {
clearConsoleWrapped();
var existingProcess = (0, _getProcessForPort2.default)(DEFAULT_PORT);
var question = _chalk2.default.yellow('Something is already running on port ' + DEFAULT_PORT + '.' + (existingProcess ? ' Probably:\n ' + existingProcess : '') + '\n\nWould you like to run the app on another port instead?');
(0, _prompt2.default)(question, true).then(function (shouldChangePort) {
if (shouldChangePort) {
run(port);
}
});
} else {
console.log(_chalk2.default.red('Something is already running on port ' + DEFAULT_PORT + '.'));
}
});
}
init();