manifest-gen.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825
  1. const Path = require('path');
  2. const Fs = require('fire-fs');
  3. const CfgUtil = Editor.require('packages://hot-update-tools/core/CfgUtil.js');
  4. const Util = Editor.require('packages://hot-update-tools/core/Util.js');
  5. const OutPut = Editor.require('packages://hot-update-tools/core/OutPut.js');
  6. const GoogleAnalytics = Editor.require(
  7. 'packages://hot-update-tools/core/GoogleAnalytics.js',
  8. );
  9. const Electron = require('electron');
  10. const { write } = require('fs');
  11. const FsExtra = require('fs-extra');
  12. Vue.component('manifest-gen', {
  13. template: Fs.readFileSync(
  14. Editor.url('packages://hot-update-tools/panel/manifest-gen.html'),
  15. 'utf-8',
  16. ),
  17. mixins: [Editor.require('packages://hot-update-tools/panel/mixin.js')],
  18. data() {
  19. return {
  20. gameName: '',
  21. whiteList: '',
  22. version: '',
  23. serverRootDir: '',
  24. remoteServerVersion: '', // 远程服务器版本
  25. resourceRootDir: '',
  26. genManifestDir: '',
  27. // 热更资源服务器配置历史记录
  28. isShowUseAddrBtn: false,
  29. isShowDelAddrBtn: false,
  30. subPackArr: {},
  31. allProjectManifest: {}
  32. };
  33. },
  34. computed: {
  35. isValidResDir() {
  36. return !!(this.resourceRootDir && Fs.existsSync(this.resourceRootDir));
  37. },
  38. },
  39. created() {
  40. this.$nextTick(() => {
  41. let data = CfgUtil.cfgData;
  42. if (data) {
  43. this.gameName = data.gameName || 'assets';
  44. this.whiteList = data.whiteList || 'internal,main,resources';
  45. this.subPackArr = data.subPackArr || {};
  46. this.serverRootDir = data.serverRootDir;
  47. this.resourceRootDir = data.resourceRootDir;
  48. let keys = Object.keys(this.subPackArr);
  49. for (let i = 0; i < keys.length; i += 1) {
  50. let index = keys[i];
  51. let data = this.subPackArr[index];
  52. data.versionNumber = "正在从远端获取";
  53. this._getServerVersion(index, (version) => {
  54. data.versionNumber = version;
  55. this._saveConfig();
  56. });
  57. }
  58. }
  59. this.genManifestDir = OutPut.manifestDir;
  60. this._getServerVersion(this.gameName, (version) => {
  61. this.remoteServerVersion = version;
  62. });
  63. this._initResourceBuild();
  64. });
  65. },
  66. methods: {
  67. _initResourceBuild() {
  68. let projectDir = Editor.Project.path;
  69. let buildCfg = Path.join(projectDir, 'local/builder.json');
  70. if (Fs.existsSync(buildCfg)) {
  71. let buildData = JSON.parse(Fs.readFileSync(buildCfg, 'utf-8'));
  72. let buildDir = buildData.buildPath;
  73. let buildFullDir = Path.join(projectDir, buildDir);
  74. let packResourceDir = Path.join(
  75. buildFullDir,
  76. `jsb-${buildData.template}`,
  77. );
  78. if (!Fs.existsSync(packResourceDir)) {
  79. let platformDir = Path.join(buildFullDir, buildData.platform);
  80. if (Fs.existsSync(platformDir)) {
  81. packResourceDir = platformDir;
  82. }
  83. }
  84. this._checkResourceRootDir(packResourceDir);
  85. } else {
  86. this.log('发现没有构建项目, 使用前请先构建项目!');
  87. }
  88. },
  89. _isVersionPass(newVersion, baseVersion) {
  90. if (
  91. newVersion === undefined ||
  92. newVersion === null ||
  93. baseVersion === undefined ||
  94. baseVersion === null
  95. ) {
  96. return false;
  97. }
  98. let arrayA = newVersion.split('.');
  99. let arrayB = baseVersion.split('.');
  100. let len = arrayA.length > arrayB.length ? arrayA.length : arrayB.length;
  101. for (let i = 0; i < len; i++) {
  102. let itemA = arrayA[i];
  103. let itemB = arrayB[i];
  104. // 新版本1.2 老版本 1.2.3
  105. if (itemA === undefined && itemB !== undefined) {
  106. return false;
  107. }
  108. // 新版本1.2.1, 老版本1.2
  109. if (itemA !== undefined && itemB === undefined) {
  110. return true;
  111. }
  112. if (itemA && itemB && parseInt(itemA) > parseInt(itemB)) {
  113. return true;
  114. }
  115. }
  116. return false;
  117. },
  118. _updateShowUseAddrBtn() {
  119. // let selectURL = this.$els.address.value;
  120. // if (this.serverRootDir === selectURL) {
  121. // this.isShowUseAddrBtn = false;
  122. // }
  123. },
  124. _getServerVersion(gameName, callFunc) {
  125. if (this.serverRootDir.length <= 0) {
  126. console.log('远程资源服务器URL错误: ' + this.serverRootDir);
  127. return;
  128. }
  129. let versionUrl = this.serverRootDir + '/' + gameName;
  130. if (gameName != 'assets') {
  131. versionUrl += '/' + gameName + '_project.manifest';
  132. } else {
  133. versionUrl += '/' + 'project.manifest';
  134. }
  135. let xhr = new XMLHttpRequest();
  136. xhr.onreadystatechange = () => {
  137. if (xhr.readyState === 4 && xhr.status >= 200 && xhr.status < 400) {
  138. let text = xhr.responseText;
  139. let json = null;
  140. try {
  141. json = JSON.parse(text);
  142. } catch (e) {
  143. console.log(e);
  144. this.log('获取远程版本号失败!');
  145. return;
  146. }
  147. this.allProjectManifest[gameName] = json;
  148. callFunc && callFunc(json.version);
  149. } else if (xhr.status === 404 || xhr.status === 0) {
  150. callFunc && callFunc("远端地址错误");
  151. }
  152. };
  153. xhr.open('get', versionUrl, true);
  154. xhr.setRequestHeader('If-Modified-Since', '0'); // 不缓存
  155. xhr.send();
  156. },
  157. onClickGenCfg(event) {
  158. GoogleAnalytics.eventCustom('GenManifest');
  159. // 检查是否需要构建项目
  160. // let times = CfgUtil.getBuildTimeGenTime();
  161. // let genTime = times.genTime;
  162. // let buildTime = times.buildTime;
  163. // if (genTime === buildTime) {// 构建完版本之后没有生成manifest文件
  164. // CfgUtil.updateGenTime(new Date().getTime(), this.version);// 更新生成时间
  165. // } else {
  166. // this.log('[生成] 你需要重新构建项目,因为上次构建已经和版本关联:' + CfgUtil.cfgData.genVersion);
  167. // return;
  168. // }
  169. if (!this.serverRootDir || this.serverRootDir.length <= 0) {
  170. this.log('[生成] 服务器地址未填写');
  171. return;
  172. }
  173. // 检查resource目录
  174. if (this.resourceRootDir.length === 0) {
  175. this.log('[生成] 请先指定 <build项目资源文件目录>');
  176. return;
  177. }
  178. if (!this._checkResourceRootDir(this.resourceRootDir)) {
  179. return;
  180. }
  181. if (!this.genManifestDir || this.genManifestDir.length <= 0) {
  182. this.log('[生成] manifest文件生成地址未填写');
  183. return;
  184. }
  185. if (!Fs.existsSync(this.genManifestDir)) {
  186. this.log('[生成] manifest存储目录不存在: ' + this.genManifestDir);
  187. return;
  188. }
  189. this._saveConfig();
  190. this._genVersion2(
  191. this.serverRootDir,
  192. this.resourceRootDir,
  193. this.genManifestDir,
  194. );
  195. },
  196. onClickOpenVersionDir() {
  197. this.openDir(OutPut.versionsDir);
  198. },
  199. onOpenManifestDir() {
  200. this.openDir(this.genManifestDir);
  201. },
  202. onOpenResourceDir() {
  203. this.openDir(this.resourceRootDir);
  204. },
  205. onSelectResourceRootDir() {
  206. let res = Editor.Dialog.openFile({
  207. title: '选择构建后的根目录',
  208. defaultPath: Editor.projectInfo.path,
  209. properties: ['openDirectory'],
  210. });
  211. if (res !== -1) {
  212. let dir = res[0];
  213. if (this._checkResourceRootDir(dir)) {
  214. this.resourceRootDir = dir;
  215. this._saveConfig();
  216. }
  217. }
  218. },
  219. // 删除热更历史地址
  220. onBtnClickDelSelectedHotAddress() {
  221. let address = this.$els.address.value;
  222. },
  223. onBtnClickUseSelectedHotAddress() {
  224. this.$els;
  225. let address = this.$els.address.value;
  226. this.serverRootDir = address;
  227. this.onInPutUrlOver();
  228. this._updateShowUseAddrBtn();
  229. },
  230. onChangeSelectHotAddress(event) {
  231. console.log('change');
  232. GoogleAnalytics.eventCustom('ChangeSelectHotAddress');
  233. this.isShowUseAddrBtn = true;
  234. this.isShowDelAddrBtn = true;
  235. this._updateShowUseAddrBtn();
  236. },
  237. userLocalIP() {
  238. GoogleAnalytics.eventCustom('useLocalIP');
  239. const Util = Editor.require('packages://hot-update-tools/core/Util.js');
  240. let ip = Util.getLocalIP();
  241. if (ip.length > 0) {
  242. this.serverRootDir = 'http://' + ip;
  243. this.onInPutUrlOver(null);
  244. }
  245. },
  246. onInPutUrlOver(event) {
  247. let url = this.serverRootDir;
  248. if (
  249. url === 'http://' ||
  250. url === 'https://' ||
  251. url === 'http' ||
  252. url === 'https' ||
  253. url === 'http:' ||
  254. url === 'https:'
  255. ) {
  256. return;
  257. }
  258. let index1 = url.indexOf('http://');
  259. let index2 = url.indexOf('https://');
  260. if (index1 === -1 && index2 === -1) {
  261. let reg =
  262. /^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/)(([A-Za-z0-9-~]+)\.)+([A-Za-z0-9-~\/])+$/;
  263. if (!reg.test(url)) {
  264. this.log(
  265. url + ' 不是以http://https://开头,或者不是网址, 已经自动修改',
  266. );
  267. this.serverRootDir = 'http://' + this.serverRootDir;
  268. this._getServerVersion(this.gameName, (version) => {
  269. this.remoteServerVersion = version;
  270. });
  271. }
  272. } else {
  273. // 更新服务器版本
  274. this._getServerVersion(this.gameName, (version) => {
  275. this.remoteServerVersion = version;
  276. });
  277. }
  278. this._updateShowUseAddrBtn();
  279. this._saveConfig();
  280. },
  281. _saveConfig() {
  282. let data = {
  283. gameName: this.gameName,
  284. whiteList: this.whiteList,
  285. subPackArr: this.subPackArr,
  286. serverRootDir: this.serverRootDir,
  287. resourceRootDir: this.resourceRootDir,
  288. genManifestDir: OutPut.manifestDir,
  289. localServerPath: this.localServerPath,
  290. };
  291. CfgUtil.saveConfig(data);
  292. },
  293. onInputVersionOver() {
  294. let buildVersion = CfgUtil.cfgData.genVersion;
  295. let buildTime = CfgUtil.cfgData.buildTime;
  296. let genTime = CfgUtil.cfgData.genTime; // 生成manifest时间
  297. let remoteVersion = this.remoteServerVersion;
  298. if (remoteVersion !== null && remoteVersion !== undefined) {
  299. // 存在远程版本
  300. } else {
  301. // 未发现远程版本
  302. }
  303. // let nowTime = new Date().getTime();
  304. // if (nowTime !== buildTime) {
  305. // if (genVersion === this.version) {
  306. // this.log("版本一致,请构建项目");
  307. // } else {
  308. // }
  309. // }
  310. this._saveConfig();
  311. },
  312. onStopTouchEvent(event) {
  313. event.preventDefault();
  314. event.stopPropagation();
  315. },
  316. onBtnClickHelpDoc() {
  317. GoogleAnalytics.eventDoc();
  318. // let url = 'https://tidys.github.io/plugin-docs-oneself/docs/hot-update-tools/';
  319. const url = 'https://tidys.gitee.io/doc';
  320. Electron.shell.openExternal(url);
  321. },
  322. onBtnClickTellMe() {
  323. GoogleAnalytics.eventQQ();
  324. let url = 'http://wpa.qq.com/msgrd?v=3&uin=774177933&site=qq&menu=yes';
  325. Electron.shell.openExternal(url);
  326. },
  327. // serverUrl 必须以/结尾
  328. // genManifestDir 建议在assets目录下
  329. // buildResourceDir 默认为 build/jsb-default/
  330. // -v 10.1.1 -u http://192.168.191.1//cocos/remote-assets/ -s build/jsb-default/ -d assets
  331. _genVersion2(serverUrl, buildResourceDir, genManifestDir) {
  332. this.log('[Build] 开始生成manifest配置文件....');
  333. let dest = genManifestDir;
  334. let src = buildResourceDir;
  335. let readDir = (dir, obj, bundleName) => {
  336. let stat = Fs.statSync(dir);
  337. if (!stat.isDirectory()) {
  338. return;
  339. }
  340. let subpaths = Fs.readdirSync(dir);
  341. let subpath;
  342. let size;
  343. let md5;
  344. let compressed;
  345. let relative;
  346. for (let i = 0; i < subpaths.length; ++i) {
  347. if (subpaths[i][0] === '.') {
  348. continue;
  349. }
  350. subpath = Path.join(dir, subpaths[i]);
  351. stat = Fs.statSync(subpath);
  352. if (stat.isDirectory()) {
  353. readDir(subpath, obj, bundleName);
  354. } else if (stat.isFile()) {
  355. // Size in Bytes
  356. size = stat['size'];
  357. // let crypto = require('crypto');
  358. md5 = require('crypto')
  359. .createHash('md5')
  360. .update(Fs.readFileSync(subpath))
  361. .digest('hex');
  362. compressed = Path.extname(subpath).toLowerCase() === '.zip';
  363. relative = Path.relative(src, subpath);
  364. relative = relative.replace(/\\/g, '/');
  365. relative = encodeURI(relative);
  366. let splitData1 = relative.split("/");
  367. if (splitData1[1] == bundleName) {
  368. let splitData2 = relative.split("/" + splitData1[1]);
  369. relative = splitData2[0] + splitData2[1];
  370. }
  371. obj[relative] = {
  372. size: size,
  373. md5: md5,
  374. };
  375. if (compressed) {
  376. obj[relative].compressed = true;
  377. }
  378. }
  379. }
  380. };
  381. let mkdirSync = (path) => {
  382. try {
  383. Fs.mkdirSync(path);
  384. } catch (e) {
  385. if (e.code !== 'EEXIST') throw e;
  386. }
  387. };
  388. let createManifest = (version, serverUrl, gameName) => {
  389. let versionUrl = this.serverRootDir + '/' + gameName;
  390. let manifestUrl = this.serverRootDir + '/' + gameName;
  391. if (gameName != 'assets') {
  392. versionUrl += '/' + gameName + '_version.manifest';
  393. manifestUrl += '/' + gameName + '_project.manifest';
  394. } else {
  395. versionUrl += '/' + 'version.manifest';
  396. manifestUrl += '/' + 'project.manifest';
  397. }
  398. let manifest = {
  399. version: (version == "远端地址错误" ? "0" : (parseInt(version) + 1) + ''),
  400. packageUrl: serverUrl,
  401. remoteManifestUrl: manifestUrl,
  402. remoteVersionUrl: versionUrl,
  403. assets: {},
  404. searchPaths: [],
  405. };
  406. return manifest;
  407. }
  408. let checkMd5 = (manifest, name) => {
  409. let newAssets = manifest.assets;
  410. let oldMAssets = this.allProjectManifest[name] ? this.allProjectManifest[name].assets : {};
  411. let keys = Object.keys(newAssets);
  412. for (let i = 0; i < keys.length; i += 1) {
  413. let index = keys[i];
  414. let newFile = newAssets[index];
  415. let oldFile = oldMAssets[index];
  416. if (!oldFile) {
  417. return true;
  418. } if (newFile.md5 != oldFile.md5) {
  419. return true;
  420. }
  421. }
  422. return false;
  423. }
  424. let writeToManifest = (manifest, bundleName, underline) => {
  425. let path = Path.join(dest, bundleName);
  426. let destManifest = Path.join(path, bundleName + underline + 'project.manifest');
  427. let destVersion = Path.join(path, bundleName + underline + 'version.manifest');
  428. if (!Fs.existsSync(path)) {
  429. FsExtra.ensureDirSync(path);
  430. }
  431. mkdirSync(dest);
  432. // 生成project.manifest
  433. Fs.writeFileSync(destManifest, JSON.stringify(manifest));
  434. // 生成version.manifest
  435. delete manifest.assets;
  436. delete manifest.searchPaths;
  437. Fs.writeFileSync(destVersion, JSON.stringify(manifest));
  438. }
  439. //拆分bundle与assets
  440. let checkArr = this.whiteList.split(',')
  441. let bundlePath = [];
  442. //bundle
  443. let keys = Object.keys(this.subPackArr);
  444. for (let i = 0; i < keys.length; i += 1) {
  445. let name = keys[i];
  446. let data = this.subPackArr[name];
  447. let manifest = createManifest(data.versionNumber, data.subPackUrl, name);
  448. readDir(Path.join(src, Util.manifestResDir, name), manifest.assets, name);
  449. if (checkMd5(manifest, name)) {
  450. writeToManifest(manifest, name, "_");
  451. bundlePath.push(name);
  452. }
  453. }
  454. //assets src单独处理
  455. let manifest = createManifest(this.remoteServerVersion, this.serverRootDir + '/' + this.gameName, this.gameName);
  456. for (let i = 0; i < checkArr.length; i += 1) {
  457. readDir(Path.join(src, Util.manifestResDir, checkArr[i]), manifest.assets, "");
  458. }
  459. readDir(Path.join(src, 'src'), manifest.assets, "");
  460. writeToManifest(manifest, '', '');
  461. this._packageVersion2(bundlePath, checkArr);
  462. },
  463. _packageDir(rootPath, zip) {
  464. let dir = Fs.readdirSync(rootPath);
  465. for (let i = 0; i < dir.length; i++) {
  466. let itemDir = dir[i];
  467. let itemFullPath = Path.join(rootPath, itemDir);
  468. let stat = Fs.statSync(itemFullPath);
  469. if (stat.isFile()) {
  470. zip.file(itemDir, Fs.readFileSync(itemFullPath));
  471. } else if (stat.isDirectory()) {
  472. this._packageDir(itemFullPath, zip.folder(itemDir));
  473. }
  474. }
  475. },
  476. _packageVersion2(bundlePath, checkArr) {
  477. let { manifestResDir } = Util;
  478. let JSZip = Editor.require(
  479. 'packages://hot-update-tools/node_modules/jszip',
  480. );
  481. this.log('[Pack] 开始打包版本 ...');
  482. let packageManifest = (zip, bundleName, underline) => {
  483. // 打包manifest文件
  484. let manifestName = bundleName + underline;
  485. let path = Path.join(this.genManifestDir, bundleName);
  486. let version = Path.join(path, manifestName + 'version.manifest');
  487. let project = Path.join(path, manifestName + 'project.manifest');
  488. zip.file(manifestName + 'project.manifest', Fs.readFileSync(project));
  489. zip.file(manifestName + 'version.manifest', Fs.readFileSync(version));
  490. return version;
  491. }
  492. let packageSources = (zip, sources, local) => {
  493. // 要打包的资源
  494. let resPath = Path.join(this.resourceRootDir, manifestResDir, sources);
  495. this._packageDir(resPath, zip.folder(manifestResDir + local));
  496. }
  497. let packageZips = (zip, bundle, version) => {
  498. // 打包的文件名
  499. let versionData = Fs.readFileSync(version, 'utf-8');
  500. let versionJson = JSON.parse(versionData);
  501. let versionStr = versionJson.version; // 版本
  502. // 打包到目录,生成zip
  503. versionStr = versionStr.replace('.', '_');
  504. let zipName = 'ver_' + versionStr + '.zip';
  505. let zipDir = Path.join(OutPut.versionsDir, bundle);
  506. let zipFilePath = Path.join(zipDir, zipName);
  507. if (!Fs.existsSync(zipDir)) {
  508. FsExtra.ensureDirSync(zipDir);
  509. }
  510. if (Fs.existsSync(zipFilePath)) {
  511. // 存在该版本的zip
  512. Fs.unlinkSync(zipFilePath);
  513. this.log('[Pack] 发现该版本的zip, 已经删除!');
  514. } else {
  515. }
  516. zip
  517. .generateNodeStream({
  518. type: 'nodebuffer',
  519. streamFiles: true,
  520. })
  521. .pipe(Fs.createWriteStream(zipFilePath))
  522. .on('finish', () => {
  523. this.log('[Pack] 打包成功: ' + zipFilePath);
  524. })
  525. .on('error', (event) => {
  526. this.log('[Pack] 打包失败:' + event.message);
  527. });
  528. }
  529. //bundle打包
  530. for (let i = 0; i < bundlePath.length; i += 1) {
  531. let zip = new JSZip();
  532. let bundle = bundlePath[i];
  533. let version = packageManifest(zip, bundle, "_");
  534. packageSources(zip, bundle, '');
  535. packageZips(zip, bundle, version);
  536. }
  537. //assets src打包
  538. let zip = new JSZip();
  539. let version = packageManifest(zip, '', '');
  540. let srcPath = Path.join(this.resourceRootDir, 'src');
  541. this._packageDir(srcPath, zip.folder('src'));
  542. for (let i = 0; i < checkArr.length; i += 1) {
  543. let project = checkArr[i];
  544. packageSources(zip, project, "/" + project);
  545. }
  546. packageZips(zip, '', version);
  547. },
  548. _checkResourceRootDir(jsbDir) {
  549. if (Fs.existsSync(jsbDir)) {
  550. let srcPath = Path.join(jsbDir, 'src');
  551. if (!Fs.existsSync(srcPath)) {
  552. this.log(`没有发现 ${srcPath}, 请先构建项目.`);
  553. return false;
  554. }
  555. let resArray = ['res', 'assets'];
  556. for (let i = 0; i < resArray.length; i++) {
  557. let item = resArray[i];
  558. if (Fs.existsSync(Path.join(jsbDir, item))) {
  559. Util.manifestResDir = item;
  560. break;
  561. }
  562. }
  563. if (!Util.manifestResDir) {
  564. this.log(`没有发现资源目录${resArray.toString()}, 请先构建项目.`);
  565. return false;
  566. }
  567. this.resourceRootDir = jsbDir;
  568. this._saveConfig();
  569. return true;
  570. } else {
  571. this.log(`没有发现 ${jsbDir}, 请先构建项目.`);
  572. return false;
  573. }
  574. },
  575. changeSubPackDisabled(index) {
  576. Vue.set(this.subPackArr[index], "disabled", !this.subPackArr[index].disabled);
  577. this._saveConfig();
  578. },
  579. onInSubPackName(index) {
  580. this.oldSubPackName = index;
  581. },
  582. onLeaveSubPackName(index) {
  583. if (this.oldSubPackName == index) { return; }
  584. this.subPackArr[this.oldSubPackName].subPackUrl = this.serverRootDir + '/' + index;
  585. Vue.set(this.subPackArr, index, this.subPackArr[this.oldSubPackName]);
  586. Vue.delete(this.subPackArr, [this.oldSubPackName]);
  587. this._saveConfig();
  588. this._getServerVersion(index, (version) => {
  589. this.subPackArr[index].versionNumber = version;
  590. Vue.set(this.subPackArr, index, this.subPackArr[index]);
  591. this._saveConfig();
  592. })
  593. },
  594. addSubPack() {
  595. if (this.subPackArr["命名子包"]) {
  596. this.log('请先填写之前创建的子包信息');
  597. } else {
  598. Vue.set(this.subPackArr, "命名子包", {
  599. versionNumber: 0,
  600. disabled: false,
  601. subPackUrl: "请先输入子包名"
  602. })
  603. this._saveConfig();
  604. }
  605. },
  606. delSubPack(index) {
  607. Vue.delete(this.subPackArr, index);
  608. this._saveConfig();
  609. },
  610. onMoveJSBLinkGameRes() {
  611. let data = CfgUtil._getConfigData();
  612. this.log(JSON.stringify(data))
  613. if(data) {
  614. let subPackArr = data.subPackArr;
  615. let projectDir = data.resourceRootDir;
  616. Editor.log(`项目路径:${projectDir}`)
  617. let keys = Object.keys(subPackArr);
  618. Editor.log(keys)
  619. for(let i = 0; i < keys.length; ++i) {
  620. const name = keys[i];
  621. const element = subPackArr[name];
  622. Editor.log(name)
  623. Editor.log(JSON.stringify(element))
  624. if(element) {
  625. let destDir = Path.join(projectDir, 'assets\\'+name);
  626. Editor.log(destDir)
  627. if(Fs.existsSync(destDir)) {
  628. Editor.log(`${name} 存在`);
  629. let assetsDir = Path.join(destDir, 'assets');
  630. if(!Fs.existsSync(assetsDir)) {
  631. Fs.mkdirSync(assetsDir);
  632. }
  633. FsExtra.emptyDirSync(assetsDir);
  634. let impDir = Path.join(assetsDir, 'import');
  635. let natDir = Path.join(assetsDir, 'import');
  636. if(!Fs.existsSync(impDir)) {
  637. Fs.mkdirSync(impDir);
  638. }
  639. if(!Fs.existsSync(natDir)) {
  640. Fs.mkdirSync(natDir);
  641. }
  642. FsExtra.copySync(impDir, Path.join(destDir, 'import'));
  643. FsExtra.move(natDir, Path.join(destDir, 'native'));
  644. FsExtra.copyFileSync(Path.join(assetsDir, 'config.json'), Path.join(destDir, 'config.json'));
  645. }else{
  646. Editor.log(`${name} 不存在`);
  647. }
  648. }
  649. }
  650. }
  651. },
  652. onCopyFileToReduceDir() {
  653. GoogleAnalytics.eventCustom('onCopyFileToReduceDir');
  654. let { manifestResDir } = Util;
  655. let { manifestDir,reduceDir,versionsDir } = OutPut;
  656. let { resourceRootDir,serverRootDir } = CfgUtil.cfgData;
  657. // 检查资源目录
  658. let srcPath = Path.join(resourceRootDir, 'src');
  659. let resPath = Path.join(resourceRootDir, manifestResDir);
  660. if(!Fs.existsSync(resourceRootDir)) {
  661. this.log('资源目录不存在:'+resourceRootDir+',请构建项目');
  662. return;
  663. }
  664. if (!Fs.existsSync(srcPath)) {
  665. this.log(resourceRootDir + '不存在src目录, 无法拷贝文件');
  666. return;
  667. }
  668. if (!Fs.existsSync(resPath)) {
  669. this.log(resourceRootDir + '不存在res目录, 无法拷贝文件');
  670. return;
  671. }
  672. this.log('清空文件夹');
  673. this.delFile(reduceDir, reduceDir);
  674. // let versionUrl = this.serverRootDir + '/project.manifest';
  675. let versionUrl = this.serverRootDir + '/assets/project.manifest';
  676. let newUrl = manifestDir + '/project.manifest';
  677. let xhr = new XMLHttpRequest();
  678. xhr.onreadystatechange = () => {
  679. if (xhr.readyState === 4 && xhr.status >= 200 && xhr.status < 400) {
  680. let text = xhr.responseText;
  681. let json = null;
  682. try {
  683. json = JSON.parse(text);
  684. } catch (e) {
  685. console.log(e);
  686. this.log('获取远程版本号失败!');
  687. return;
  688. }
  689. this.log(json.version);
  690. if(Fs.existsSync(newUrl)) {
  691. // this.log('新manifest存在');
  692. Fs.readFile(newUrl, 'utf-8', (err, data)=>{
  693. if(!err) {
  694. // this.log('有数据');
  695. let newAssets = JSON.parse(data.toString()).assets;
  696. let oldAssets = json.assets;
  697. // this.log(newAssets);
  698. // this.log(oldAssets);
  699. let dataArray = [];
  700. for (var newKey in newAssets) {
  701. let newValue = newAssets[newKey];
  702. let newElementSize = newValue["size"];
  703. let newElementMd5 = newValue["md5"];
  704. let isFind = false;
  705. for (var oldKey in oldAssets) {
  706. let oldValue = oldAssets[oldKey];
  707. let oldElementSize = oldValue["size"];
  708. let oldElementMd5 = oldValue["md5"];
  709. if (oldKey == newKey && oldElementSize == newElementSize && oldElementMd5 == newElementMd5) {
  710. isFind = true;
  711. break;
  712. }
  713. }
  714. if (!isFind) {//没找到
  715. //cb("找到差异文件:" + newKey + " size:" + newElementSize + " md5:" + newElementMd5);
  716. dataArray.push(newKey);
  717. }
  718. }
  719. this.log('筛选成功,共'+dataArray.length+'个文件');
  720. for(let i = 0; i < dataArray.length; ++i) {
  721. let srcPath = Editor.Project.path + "\\build\\jsb-link\\" + dataArray[i];
  722. let destPath = reduceDir + "\\" + dataArray[i];
  723. this.copyFile(srcPath, destPath);
  724. }
  725. this.copyFile(manifestDir+'\\project.manifest', reduceDir+'\\project.manifest');
  726. this.copyFile(manifestDir+'\\version.manifest', reduceDir+'\\version.manifest');
  727. this.log('热更新资源检索完毕')
  728. }
  729. })
  730. }else{
  731. this.log('本地manifest不存在,请生成');
  732. }
  733. } else if (xhr.status === 404 || xhr.status === 0) {
  734. // this.log("远端地址错误");
  735. }
  736. };
  737. xhr.open('get', versionUrl, true);
  738. xhr.setRequestHeader('If-Modified-Since', '0'); // 不缓存
  739. xhr.send();
  740. },
  741. delFile(path, reservePath) {
  742. if(Fs.existsSync(path)) {
  743. if(Fs.statSync(path).isDirectory()) {
  744. let files = Fs.readdirSync(path);
  745. files.forEach((file, index) => {
  746. let currentPath = path + '/' + file;
  747. if(Fs.statSync(currentPath).isDirectory()) {
  748. this.delFile(currentPath, reservePath);
  749. }else{
  750. Fs.unlinkSync(currentPath);
  751. }
  752. });
  753. if(path != reservePath) {
  754. Fs.rmdirSync(path);
  755. }
  756. }else{
  757. Fs.unlinkSync(path);
  758. }
  759. }
  760. },
  761. copyFile(srcPath, destPath) {
  762. this.mkdirsSync(Path.dirname(destPath));
  763. Fs.writeFileSync(destPath, Fs.readFileSync(srcPath));
  764. },
  765. mkdirsSync(dirname) {
  766. if(Fs.existsSync(dirname)) {
  767. return true;
  768. }else{
  769. if(this.mkdirsSync(Path.dirname(dirname))) {
  770. Fs.mkdirSync(dirname);
  771. return true;
  772. }
  773. }
  774. },
  775. },
  776. });