UNPKG

fec-builder

Version:

通用的前端构建工具,屏蔽业务无关的细节配置,开箱即用

130 lines (129 loc) 6.1 kB
"use strict"; /** * @file serve as dev server * @author nighca <nighca@live.cn> */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const fs_1 = __importDefault(require("fs")); const url_1 = __importDefault(require("url")); const webpack_1 = __importDefault(require("webpack")); const webpack_dev_server_1 = __importDefault(require("webpack-dev-server")); const logger_1 = __importDefault(require("./utils/logger")); const utils_1 = require("./utils"); const webpack_2 = require("./webpack"); const build_conf_1 = require("./utils/build-conf"); const lodash_1 = require("lodash"); const paths_1 = require("./utils/paths"); // 业务项目的配置文件,变更时需要重启 server const projectConfigFiles = [ 'tsconfig.json' ]; function serve(port) { return __awaiter(this, void 0, void 0, function* () { let stopDevServer = yield runDevServer(port); function restartDevServer() { return __awaiter(this, void 0, void 0, function* () { yield (stopDevServer === null || stopDevServer === void 0 ? void 0 : stopDevServer()); stopDevServer = yield runDevServer(port); }); } const disposers = []; disposers.push(build_conf_1.watchBuildConfig(() => __awaiter(this, void 0, void 0, function* () { logger_1.default.info('Detected build config change, restarting server...'); restartDevServer(); }))); projectConfigFiles.forEach(file => { const filePath = paths_1.abs(file); if (fs_1.default.existsSync(filePath)) { disposers.push(utils_1.watchFile(filePath, () => __awaiter(this, void 0, void 0, function* () { logger_1.default.info(`Detected ${file} change, restarting server...`); restartDevServer(); }))); } }); process.on('exit', () => { disposers.forEach(disposer => disposer()); }); }); } function runDevServer(port) { return __awaiter(this, void 0, void 0, function* () { const buildConfig = yield build_conf_1.findBuildConfig(); const webpackConfig = yield webpack_2.getServeConfig(); logger_1.default.debug('webpack config:', webpackConfig); const devServerConfig = { hotOnly: true, // 方便开发调试 disableHostCheck: true, // devServer 中的 public 字段会被拿去计算得到 hot module replace 相关请求的 URI // 这里用 0.0.0.0:0 可以让插到页面的 client 脚本自动依据 window.location 去获得 host // 从而正确地建立 hot module replace 依赖的 ws 链接及其它请求,逻辑见: // 这里之所以要求使用页面的 window.location 信息,是因为 builder 在容器中 serve 时端口会被转发, // 即可能配置 port 为 80,在(宿主机)浏览器中通过 8080 端口访问 public: '0.0.0.0:0', publicPath: utils_1.getPathFromUrl(buildConfig.publicUrl), stats: 'errors-only', proxy: getProxyConfig(buildConfig.devProxy), historyApiFallback: { rewrites: getHistoryApiFallbackRewrites(buildConfig) } }; const compiler = webpack_1.default(webpackConfig); const server = new webpack_dev_server_1.default(compiler, devServerConfig); const host = '0.0.0.0'; server.listen(port, host, () => { logger_1.default.info(`Server started on ${host}:${port}`); }); return () => new Promise(resolve => { server.close(resolve); }); }); } exports.default = utils_1.logLifecycle('Serve', serve, logger_1.default); const defaultProxyConfig = { changeOrigin: true, onProxyReq(proxyReq) { // add header `X-Real-IP` const origin = proxyReq.getHeader('origin'); if (origin) { proxyReq.setHeader("X-Real-IP", url_1.default.parse(origin).hostname); } // fix `referer` to avoid csrf detect const referer = proxyReq.getHeader('referer'); if (referer) { proxyReq.setHeader('referer', referer.replace(url_1.default.parse(referer).host, proxyReq.getHeader('host'))); } }, onProxyRes(proxyRes) { // 干掉 set-cookie 中的 secure 设置,因为本地开发 server 是 http 的 // TODO: 考虑支持 https dev server? if (proxyRes.headers['set-cookie']) { proxyRes.headers['set-cookie'] = proxyRes.headers['set-cookie'].map(cookie => cookie.replace('; Secure', '')); } } }; function getProxyConfig(devProxy) { return lodash_1.mapValues(devProxy, target => (Object.assign(Object.assign({}, defaultProxyConfig), { target }))); } // get rewrites for devServerConfig.historyApiFallback function getHistoryApiFallbackRewrites(buildConfig) { const prefix = utils_1.getPathFromUrl(buildConfig.publicUrl, false); return lodash_1.entries(buildConfig.pages).map(([name, { path }]) => ({ from: new RegExp(path), to: '/' + (prefix ? `${prefix}/${utils_1.getPageFilename(name)}` : utils_1.getPageFilename(name)) })); }