chameleon-tool
Version:
chameleon 脚手架工具
273 lines (255 loc) • 9.54 kB
JavaScript
var opn = require('opn')
var path = require('path')
var express = require('express')
var phpIndexMiddleWare = require('./php_cgi_middleware.js');
var utils = require('../../configs/utils.js');
var config = require('../../configs/config');
var dynamicApiMiddleware = require('./dynamicApiMiddleware');
var responseTime = require('./responseTime');
const liveLoadMiddleware = require('webpack-liveload-middleware');
const cmlUtils = require('chameleon-tool-utils');
const fse = require('fs-extra');
const tpl = require('chameleon-templates');
const proxy = require('chameleon-dev-proxy');
const url = require('url')
const {createRoutesReact} = require('./web-socket.js')
const http = require('http');
const bodyParser = require('body-parser')
const argv = require('minimist')(process.argv);
const nopreview = argv.nopreview || argv.n;
const {createProxyMiddleware} = require('http-proxy-middleware');
/**
* webpackConfig webpack的配置对象
* options media的配置
* compile webpack编译对象 如果存在则证明是要编译web端
*
*/
module.exports = function({webpackConfig, options, compiler}) {
// 执行web之前先更新server模板
updateServerTpl();
var port = utils.getFreePort().webServerPort;
var autoOpenBrowser = true;
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.text({ type: 'text/html' }))
app.use(responseTime());
dynamicApiMiddleware(app, options);
// web端增加跨域自定义配置
let devProxy = options && options.devProxy || [];
if (webpackConfig.name === 'web' && Array.isArray(devProxy)) {
devProxy.forEach((proxyConfig) => {
app.use(proxyConfig.path, createProxyMiddleware(proxyConfig.options))
})
}
if (compiler) {
if (options.hot === true) {
/* eslint-disable-next-line */
var hotMiddleware = require('webpack-hot-middleware')(compiler, {
heartbeat: 9000,
noInfo: true,
quiet: true,
log: function() {
// 禁止输出
}
})
app.use(hotMiddleware)
} else {
app.use(liveLoadMiddleware(compiler))
}
if (cml.config.get().templateType === 'html') {
// handle fallback for HTML5 history API
/* eslint-disable-next-line */
const {routerConfig} = cmlUtils.getRouterConfig();
let mpa = routerConfig.mpa;
if (mpa && mpa.webMpa && Array.isArray(mpa.webMpa) && routerConfig.mode === 'history') {
// hash模式在 preview页面进行了兼容,history在这里进行兼容
let webMpa = mpa.webMpa;
let rewrites = [];
routerConfig.routes.forEach((item) => {
for (var j = 0; j < webMpa.length ; j++) {
if (Array.isArray(webMpa[j].paths) && webMpa[j].paths.includes(item.path)) {
if (typeof webMpa[j].name === 'string') {
rewrites.push({
from: `${item.url}`,
to: `${webMpa[j].name}.html`
})
} else {
rewrites.push({
from: `${item.url}`,
to: `${utils.getEntryName()}${j}.html`
})
}
}
}
})
app.use(require('connect-history-api-fallback')({
rewrites
}))
} else { // hash和history模式下都是ok的
app.use(require('connect-history-api-fallback')({
index: `${utils.getEntryName()}.html`
}))
}
}
}
// serve pure static assets
var staticPath = webpackConfig.output.path;
app.use(express['static'](staticPath));
let dist = path.join(cml.projectRoot, 'dist');
app.use(express['static'](dist));
if (compiler && cml.config.get().templateType === 'smarty') {
// php-cgi
app.use(phpIndexMiddleWare(staticPath));
}
var _resolve
var readyPromise = new Promise(resolve => {
_resolve = resolve
})
// 创建project.json文件
fse.ensureFileSync(path.join(cml.utils.getDevServerPath(), 'json/project.json'))
fse.writeJsonSync(path.join(cml.utils.getDevServerPath(), 'json/project.json'), {projectName: cml.config.get().projectName})
if (compiler) {
let first = true;
let optimizeCML = cml.config.get().optimize;
let watchOptions = {
aggregateTimeout: 300,
poll: undefined
}
if (optimizeCML && !optimizeCML.watchNodeModules) {
watchOptions.ignored = /node_modules/;
}
compiler.watch(watchOptions, (err, stats) => {
if (first) {
startServer();
first = false;
}
if (err) {
throw err;
}
});
} else {
startServer();
}
function startServer() {
fse.copySync(path.join(cml.root, 'configs/preview-assets'), path.join(cml.utils.getDevServerPath(), 'preview-assets'))
// 保证webpack打包完再启动web服务,否则webpack打包卡住命令行无法关闭
// app.listen(port)
const server = http.createServer(app).listen(port);
var uri = `http://${config.ip}:${port}/`;
let subpath = '';
// 未启动web端编译
if (!compiler) {
subpath = 'web_empty.html';
fse.copyFileSync(path.join(cml.root, 'configs/web_empty.html'), path.join(cml.utils.getDevServerPath(), 'web_empty.html'))
}
uri += 'preview.html';
var entry = utils.getEntryName();
const {routerConfig} = cmlUtils.getRouterConfig();
let mpa = routerConfig.mpa;
let weexBundles = [];
if (mpa && mpa.weexMpa && Array.isArray(mpa.weexMpa)) { // 配置了weex多页面
let weexMpa = mpa.weexMpa;
for (let i = 0; i < weexMpa.length ; i++) {
if (typeof weexMpa[i].name === 'string') {
weexBundles.push({
bundle: `weex/${weexMpa[i].name}.js`,
paths: weexMpa[i].paths
})
} else {
weexBundles.push({
bundle: `weex/${entry}${i}.js`,
paths: weexMpa[i].paths
})
}
}
} else { // 兼容原来的没有配置的情况
let allPaths = routerConfig.routes.reduce((result, current) => {
result.push(current.path);
return result;
}, [])
weexBundles.push({
bundle: `weex/${entry}.js`,
paths: allPaths
})
}
// var jsbundle = `weex/${entry}.js`;
let staticParams = { entry, weexBundles, subpath, buildType: cml.activePlatform };
createRoutesReact({server, staticParams});
cml.log.notice('Listening at ' + uri);
// when env is testing, don't need open it
if (autoOpenBrowser && !nopreview) {
opn(uri)
}
let proxyObj = cml.config.get().proxy || {};
// 最后启动代理服务
if (proxyObj.enable) {
// 默认代理weex和web端的js和css文件
let defaultMap = [{
from: `/weex/${cml.config.get().projectName}_(.+).js`,
to: `http://${config.ip}:${port}/weex/${cml.config.get().projectName}.js`
}, {
from: `/web/static/js/${cml.config.get().projectName}_(.+).js`,
to: `http://${config.ip}:${port}/static/js/${cml.config.get().projectName}.js`
}, {
from: '/web/static/js/manifest_(.+).js',
to: `http://${config.ip}:${port}/static/js/manifest.js`
}, {
from: '/web/static/js/vender_(.+).js',
to: `http://${config.ip}:${port}/static/js/vender.js`
}, {
from: '/web/static/css/vender_(.+).css',
to: `http://${config.ip}:${port}/static/css/vender.css`
}, {
from: `/web/static/css/${cml.config.get().projectName}_(.+).css`,
to: `http://${config.ip}:${port}/static/css/${cml.config.get().projectName}.css`
}];
// 启动代理服务
proxy.createProxy({
devServer: `http://${config.ip}:${port}/getkey/`,
caCertPath: path.join(cml.utils.getDevServerPath(), 'getkey'),
sslConnectInterceptor: (req, cltSocket, head) => true,
requestInterceptor: (rOptions, req, res, ssl, next) => {
var fullUrl = rOptions.protocol + '//' + rOptions.hostname + rOptions.path;
var maplist = proxyObj.mapremote || [];
maplist = defaultMap.concat(maplist);
cml.log.notice(fullUrl + '\n');
if (maplist) {
maplist.forEach((item) => {
if (new RegExp(item.from).test(fullUrl)) {
/* eslint-disable-next-line */
var srcObj = url.parse(fullUrl);
/* eslint-disable-next-line */
var desObj = url.parse(item.to);
rOptions.host = desObj.host;
rOptions.hostname = desObj.hostname;
rOptions.port = desObj.port;
rOptions.protocol = desObj.protocol;
rOptions.path = desObj.pathname + '?' + (desObj.query || '') + '&' + (srcObj.query || '');
rOptions.pathname = desObj.pathname;
rOptions.agent = false;
}
});
}
next();
}
});
}
_resolve()
}
return readyPromise
}
function updateServerTpl() {
let serverPath = cml.utils.getDevServerPath();
let serverVersionFile = path.resolve(serverPath, '.server/server-tpl-verison.json');
/* eslint-disable-next-line */
let tplVersion = require(tpl['package']).version;
if (cml.utils.isFile(serverVersionFile)) {
let version = require(serverVersionFile).version;
if (version === tplVersion) {
return;
}
}
fse.copySync(tpl.serverTpl, serverPath);
fse.outputFileSync(serverVersionFile, `{"version": "${tplVersion}"}`);
}