UNPKG

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
'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();