1 | const webpack = require('webpack');
|
2 | const devServer = require('./web/dev-server.js');
|
3 | const getConfig = require('../configs/index.js');
|
4 | const {startServer: startWeexLiveLoad, broadcast} = require('./weex/socket-server.js');
|
5 | const previewSocket = require('./web/web-socket.js');
|
6 | const cmlLinter = require('chameleon-linter');
|
7 | const watch = require('glob-watcher');
|
8 | const fse = require('fs-extra');
|
9 | const path = require('path');
|
10 | const fs = require('fs');
|
11 | const crypto = require('crypto');
|
12 | const cmlUtils = require('chameleon-tool-utils');
|
13 | const {getEntryName} = require('../configs/utils.js');
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 | exports.getBuildPromise = async function (media, type) {
|
21 |
|
22 | let options = exports.getOptions(media, type);
|
23 | let webpackConfig = await getConfig(options);
|
24 |
|
25 |
|
26 | if (!~['web', 'weex'].indexOf(type) && options.increase !== true) {
|
27 |
|
28 | var outputpath = webpackConfig.output.path;
|
29 | if (outputpath) {
|
30 | await new Promise(function(resolve, reject) {
|
31 | fse.remove(outputpath, function(err) {
|
32 | if (err) {
|
33 | reject(err);
|
34 | }
|
35 | resolve();
|
36 | })
|
37 | })['catch'](e => {
|
38 | let message = `clear file error! please remove direction ${outputpath} by yourself!`
|
39 | cml.log.error(message);
|
40 | throw new Error(e)
|
41 | })
|
42 | }
|
43 |
|
44 | }
|
45 |
|
46 | let projectConfig = options && options.projectConfig;
|
47 | let destDir = path.resolve(cml.projectRoot, `dist/${type}`);
|
48 |
|
49 | if (['wx', 'qq', 'tt'].includes(type) && projectConfig) {
|
50 | fse.outputJsonSync(path.join(destDir, 'project.config.json'), projectConfig, {spaces: 4})
|
51 | }
|
52 | if (type === 'baidu' && projectConfig) {
|
53 | fse.outputJsonSync(path.join(destDir, 'project.swan.json'), projectConfig, {spaces: 4})
|
54 | }
|
55 |
|
56 | let siteMap = options && options.siteMap;
|
57 | if (type === 'wx' && siteMap) {
|
58 | fse.outputJsonSync(path.join(destDir, 'sitemap.json'), siteMap, {spaces: 4})
|
59 | }
|
60 | return new Promise(function(resolve, reject) {
|
61 |
|
62 | if (media === 'dev') {
|
63 | const compiler = webpack(webpackConfig);
|
64 | if (type === 'weex') {
|
65 | startWeexLiveLoad(options);
|
66 | }
|
67 | let optimizeCML = cml.config.get().optimize;
|
68 | let watchOptions = {
|
69 | aggregateTimeout: 300,
|
70 | poll: undefined
|
71 | }
|
72 | if (optimizeCML && !optimizeCML.watchNodeModules) {
|
73 | watchOptions.ignored = /node_modules/;
|
74 | }
|
75 | compiler.watch(watchOptions, (err, stats) => {
|
76 |
|
77 | if (type === 'weex') {
|
78 | if (!(stats && stats.compilation && stats.compilation.errors && stats.compilation.errors.length > 0)) {
|
79 | broadcast('weex_refresh');
|
80 | }
|
81 | previewSocket.broadcast('weex_refresh');
|
82 | }
|
83 | if (err) {
|
84 | reject(err);
|
85 | }
|
86 | resolve();
|
87 | });
|
88 | } else {
|
89 |
|
90 | webpack(webpackConfig, (err, stats) => {
|
91 | if (err) {
|
92 | reject(err);
|
93 | }
|
94 | resolve();
|
95 | });
|
96 | }
|
97 | })
|
98 | }
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 | exports.getOptions = function (media, type) {
|
106 | let chameleonConfig = (cml.config.get() && cml.config.get()[type] && cml.config.get()[type][media]) || {};
|
107 |
|
108 | if (!chameleonConfig) {
|
109 | cml.log.error(`在chameleon的config中未找到 ${media}的配置参数`);
|
110 | return;
|
111 | }
|
112 |
|
113 | let options = cml.utils.merge(
|
114 | chameleonConfig,
|
115 | {
|
116 | type: type,
|
117 | media,
|
118 | root: cml.projectRoot,
|
119 | buildType: 'weex'
|
120 | }
|
121 | )
|
122 |
|
123 | return options;
|
124 | }
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 | exports.getWebBuildPromise = async function (media, isCompile) {
|
132 | if (media === 'dev') {
|
133 | let options = exports.getOptions(media, 'web');
|
134 | let webpackConfig = await getConfig(options)
|
135 | let compiler;
|
136 | if (isCompile) {
|
137 | compiler = webpack(webpackConfig);
|
138 | }
|
139 | return devServer({webpackConfig, options, compiler});
|
140 | } else {
|
141 | if (isCompile) {
|
142 | return exports.getBuildPromise(media, 'web');
|
143 | } else {
|
144 | return Promise.resolve();
|
145 | }
|
146 | }
|
147 | }
|
148 |
|
149 |
|
150 |
|
151 |
|
152 |
|
153 |
|
154 | exports.startReleaseAll = async function (media) {
|
155 | if (media === 'build') {
|
156 | process.env.NODE_ENV = 'production';
|
157 | }
|
158 | let cmlConfig = cml.config.get();
|
159 | let allPlatform = cmlConfig.platforms;
|
160 | let offPlatform = [];
|
161 | let activePlatform = [];
|
162 | if (media === 'dev') {
|
163 | offPlatform = cmlConfig.devOffPlatform;
|
164 | } else if (media === 'build') {
|
165 | offPlatform = cmlConfig.buildOffPlatform;
|
166 | }
|
167 |
|
168 | for (let i = 0, j = allPlatform.length; i < j; i++) {
|
169 | let platform = allPlatform[i];
|
170 | if (!~offPlatform.indexOf(platform)) {
|
171 | activePlatform.push(platform)
|
172 | }
|
173 | }
|
174 |
|
175 |
|
176 | let isCompile = !!~activePlatform.indexOf('web');
|
177 |
|
178 | cml.activePlatform = activePlatform;
|
179 |
|
180 | for (let i = 0, j = activePlatform.length; i < j; i++) {
|
181 | let platform = activePlatform[i];
|
182 | if (platform !== 'web') {
|
183 | await exports.getBuildPromise(media, platform);
|
184 | }
|
185 | }
|
186 |
|
187 | await exports.getWebBuildPromise(media, isCompile);
|
188 | if (media === 'build') {
|
189 | exports.createConfigJson()
|
190 | }
|
191 | startCmlLinter(media);
|
192 | }
|
193 |
|
194 | exports.startReleaseOne = async function(media, type) {
|
195 | if (media === 'build') {
|
196 | process.env.NODE_ENV = 'production';
|
197 | }
|
198 |
|
199 | cml.activePlatform = [type];
|
200 | if (type === 'web') {
|
201 | await exports.getWebBuildPromise(media, true);
|
202 | } else {
|
203 | let build = exports.getBuildPromise(media, type);
|
204 |
|
205 | if (media === 'dev') {
|
206 | await build.then(res => {
|
207 | exports.getWebBuildPromise(media, false);
|
208 | })
|
209 | } else {
|
210 | await build;
|
211 | }
|
212 | }
|
213 | if (media === 'build') {
|
214 | exports.createConfigJson()
|
215 | }
|
216 | startCmlLinter(media);
|
217 |
|
218 | }
|
219 |
|
220 |
|
221 | function startCmlLinter(media) {
|
222 | if (cml.config.get().enableLinter === true && media === 'dev') {
|
223 | cmlLinter(cml.projectRoot);
|
224 |
|
225 | const watcher = watch([cml.projectRoot + '/src/**/**.**']);
|
226 | watcher.on('change', function(path, stat) {
|
227 | cmlLinter(cml.projectRoot);
|
228 | });
|
229 | }
|
230 | }
|
231 | exports.getMD5 = function(weexjs) {
|
232 | if (!weexjs) {
|
233 | return '';
|
234 | }
|
235 | const weexjsName = weexjs.split('/').pop();
|
236 | const weexjsPath = path.resolve(cml.projectRoot, 'dist/weex/', weexjsName);
|
237 |
|
238 | if (cml.utils.isFile(weexjsPath)) {
|
239 | const md5sum = crypto.createHash('md5');
|
240 | const buffer = fs.readFileSync(weexjsPath);
|
241 | md5sum.update(buffer);
|
242 | let md5str = md5sum.digest('hex').toUpperCase();
|
243 | return md5str
|
244 | }
|
245 | }
|
246 | exports.createConfigJson = function() {
|
247 | let configJsonPath = path.join(cml.projectRoot, 'dist/config.json');
|
248 | let configObj = {};
|
249 | if (cml.utils.isFile(configJsonPath)) {
|
250 | configObj = JSON.parse(fs.readFileSync(configJsonPath, {encoding: 'utf-8'}))
|
251 | }
|
252 |
|
253 |
|
254 | let config = cml.config.get();
|
255 | config.buildInfo = config.buildInfo || {};
|
256 | let {wxAppId = '', baiduAppId = '', alipayAppId = '', qqAppId = '', ttAppId = ''} = config.buildInfo;
|
257 | let extPlatform = config.extPlatform ;
|
258 | let extCommand = (typeof extPlatform === 'object') ? Object.keys(extPlatform)[0] : undefined;
|
259 | let extAppId = ''
|
260 | if (extCommand) {
|
261 | extAppId = config.buildInfo && config.buildInfo[`${extCommand}AppId`]
|
262 | }
|
263 | let {routerConfig, hasError} = cml.utils.getRouterConfig();
|
264 | let entryName = getEntryName();
|
265 | let weexjs = (configObj.entryName && configObj.entryName.js) || '';
|
266 | let md5str = exports.getMD5(weexjs);
|
267 | let mpa = routerConfig.mpa;
|
268 |
|
269 |
|
270 | if (hasError) {
|
271 | throw new Error('router.config.json格式不正确')
|
272 | }
|
273 |
|
274 | let result = [];
|
275 | if (routerConfig) {
|
276 | if (~cml.activePlatform.indexOf('web') && !routerConfig.domain) {
|
277 | throw new Error('router.config.json 中未设置web端需要的domain字段');
|
278 | }
|
279 | let {domain, mode} = routerConfig;
|
280 | routerConfig.routes.forEach(item => {
|
281 |
|
282 | if (mpa && mpa.weexMpa && Array.isArray(mpa.weexMpa)) {
|
283 | let weexMpa = mpa.weexMpa;
|
284 | for (let i = 0; i < weexMpa.length ; i++) {
|
285 | if (Array.isArray(weexMpa[i].paths) && weexMpa[i].paths.includes(item.path)) {
|
286 | weexjs = configObj[`${entryName}${i}`] && configObj[`${entryName}${i}`].js;
|
287 | md5str = exports.getMD5(weexjs);
|
288 | }
|
289 | }
|
290 | }
|
291 | let webUrl = domain;
|
292 | if (mode === 'history') {
|
293 | webUrl += item.url;
|
294 | } else if (mode === 'hash') {
|
295 | webUrl += ('#' + item.url);
|
296 | }
|
297 | let route = {
|
298 | wx: {
|
299 | appId: wxAppId,
|
300 | path: item.path
|
301 | },
|
302 | baidu: {
|
303 | appId: baiduAppId,
|
304 | path: item.path
|
305 | },
|
306 | alipay: {
|
307 | appId: alipayAppId,
|
308 | path: item.path
|
309 | },
|
310 | qq: {
|
311 | appId: qqAppId,
|
312 | path: item.path
|
313 | },
|
314 | tt: {
|
315 | appId: ttAppId,
|
316 | path: item.path
|
317 | },
|
318 | web: {
|
319 | url: webUrl
|
320 | },
|
321 | weex: {
|
322 | url: weexjs,
|
323 | md5: md5str,
|
324 | query: {
|
325 | path: item.path
|
326 | }
|
327 | }
|
328 | }
|
329 | if (item.extra) {
|
330 | route.extra = item.extra;
|
331 | }
|
332 | if (extCommand) {
|
333 | route[extCommand] = {
|
334 | appId: extAppId,
|
335 | path: item.path
|
336 | }
|
337 | }
|
338 | result.push(route);
|
339 | })
|
340 |
|
341 | let subProject = cml.config.get().subProject;
|
342 | if (subProject && subProject.length > 0) {
|
343 | subProject.forEach(function(item) {
|
344 | let npmName = cmlUtils.isString(item) ? item : item.npmName;
|
345 | let npmRouterConfig = cml.utils.readsubProjectRouterConfig(cml.projectRoot, npmName);
|
346 | npmRouterConfig.routes && npmRouterConfig.routes.forEach(item => {
|
347 | let cmlFilePath = path.join(cml.projectRoot, 'node_modules', npmName, 'src', item.path + '.cml');
|
348 | let routePath = cml.utils.getPureEntryName(cmlFilePath, '', cml.projectRoot);
|
349 | routePath = cml.utils.handleSpecialChar(routePath);
|
350 | let webUrl = domain;
|
351 | if (mode === 'history') {
|
352 | webUrl += item.url;
|
353 | } else if (mode === 'hash') {
|
354 | webUrl += ('#' + item.url);
|
355 | }
|
356 | if (routePath[0] !== '/') {
|
357 | routePath = '/' + routePath;
|
358 | }
|
359 | let route = {
|
360 | wx: {
|
361 | appId: wxAppId,
|
362 | path: routePath
|
363 | },
|
364 | baidu: {
|
365 | appId: baiduAppId,
|
366 | path: routePath
|
367 | },
|
368 | alipay: {
|
369 | appId: alipayAppId,
|
370 | path: routePath
|
371 | },
|
372 | qq: {
|
373 | appId: qqAppId,
|
374 | path: routePath
|
375 | },
|
376 | tt: {
|
377 | appId: ttAppId,
|
378 | path: item.path
|
379 | },
|
380 | web: {
|
381 | url: webUrl
|
382 | },
|
383 | weex: {
|
384 | url: weexjs,
|
385 | query: {
|
386 | path: routePath
|
387 | }
|
388 | }
|
389 | }
|
390 | if (item.extra) {
|
391 | route.extra = item.extra;
|
392 | }
|
393 | if (extCommand) {
|
394 | route[extCommand] = {
|
395 | appId: extAppId,
|
396 | path: item.path
|
397 | }
|
398 | }
|
399 | result.push(route);
|
400 | })
|
401 | })
|
402 | }
|
403 | }
|
404 |
|
405 | result.forEach(item => {
|
406 | Object.keys(item).forEach(key => {
|
407 | if (!~cml.activePlatform.indexOf(key) && key !== 'extra') {
|
408 | delete item[key]
|
409 | }
|
410 | })
|
411 | })
|
412 |
|
413 | cml.event.emit('config-json', result);
|
414 |
|
415 | fse.outputFileSync(configJsonPath, JSON.stringify(result, '', 4))
|
416 | }
|