publish_vivogame.js 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003
  1. // v1.8.7
  2. const ideModuleDir = global.ideModuleDir;
  3. const workSpaceDir = global.workSpaceDir;
  4. //引用插件模块
  5. const gulp = require(ideModuleDir + "gulp");
  6. const fs = require("fs");
  7. const path = require("path");
  8. const childProcess = require("child_process");
  9. const del = require(ideModuleDir + "del");
  10. const iconv = require(ideModuleDir + "iconv-lite");
  11. const revCollector = require(ideModuleDir + 'gulp-rev-collector');
  12. const request = require(ideModuleDir + "request");
  13. const { getEngineVersion, canUsePluginEngine } = require("./pub_utils");
  14. let fullRemoteEngineList = ["laya.core.js", "laya.webgl.js", "laya.filter.js", "laya.ani.js", "laya.d3.js", "laya.html.js", "laya.particle.js", "laya.ui.js", "laya.d3Plugin.js", "bytebuffer.js", "laya.device.js", "laya.physics.js", "laya.physics3D.js", "laya.tiledmap.js", "worker.js", "workerloader.js"];
  15. let copyLibsTask = ["copyPlatformLibsJsFile"];
  16. let versiontask = ["version2"];
  17. let
  18. config,
  19. releaseDir,
  20. tempReleaseDir, // vivo临时拷贝目录
  21. projDir, // vivo快游戏工程目录
  22. isDealNoCompile = true,
  23. physicsLibsPathList = [],
  24. isExistEngineFolder = false; // bin目录下是否存在engine文件夹
  25. let projSrc;
  26. let versionCon; // 版本管理version.json
  27. let commandSuffix,
  28. opensslPath,
  29. layarepublicPath;
  30. // 创建vivo项目前,拷贝vivo引擎库、修改index.js
  31. gulp.task("preCreate_VIVO", copyLibsTask, function() {
  32. releaseDir = global.releaseDir;
  33. config = global.config;
  34. commandSuffix = global.commandSuffix;
  35. opensslPath = global.opensslPath;
  36. layarepublicPath = global.layarepublicPath;
  37. tempReleaseDir = global.tempReleaseDir;
  38. if (config.useMinJsLibs) {
  39. fullRemoteEngineList = fullRemoteEngineList.map((item, index) => {
  40. return item.replace(".js", ".min.js");
  41. })
  42. }
  43. });
  44. gulp.task("copyPlatformFile_VIVO", ["preCreate_VIVO"], function() {
  45. return;
  46. });
  47. // 检查是否全局安装了qgame
  48. gulp.task("createGlobalQGame_VIVO", versiontask, function() {
  49. releaseDir = path.dirname(releaseDir);
  50. projDir = path.join(releaseDir, config.vivoInfo.projName);
  51. projSrc = path.join(projDir, "src");
  52. // npm view @vivo-minigame/cli version
  53. // npm install -g @vivo-minigame/cli
  54. let remoteVersion, localVersion;
  55. let isGetRemote, isGetLocal;
  56. let isUpdateGlobalQGame = true;
  57. return new Promise((resolve, reject) => { // 远程版本号
  58. childProcess.exec("npm view @vivo-minigame/cli version", function(error, stdout, stderr) {
  59. if (!stdout) { // 获取 @vivo-minigame/cli 远程版本号失败
  60. console.log("Failed to get the remote version number");
  61. resolve();
  62. return;
  63. }
  64. remoteVersion = stdout;
  65. isGetRemote = true;
  66. if (isGetRemote && isGetLocal) {
  67. isUpdateGlobalQGame = remoteVersion != localVersion;
  68. console.log(`remoteVersion: ${remoteVersion}, localVersion: ${localVersion}`);
  69. resolve();
  70. }
  71. });
  72. childProcess.exec("mg -v", function(error, stdout, stderr) {
  73. if (!stdout) { // 获取 @vivo-minigame/cli 本地版本号失败
  74. console.log("Failed to get the local version number");
  75. resolve();
  76. return;
  77. }
  78. localVersion = stdout;
  79. isGetLocal = true;
  80. if (isGetRemote && isGetLocal) {
  81. isUpdateGlobalQGame = remoteVersion != localVersion;
  82. console.log(`remoteVersion: ${remoteVersion}, localVersion: ${localVersion}`);
  83. resolve();
  84. }
  85. });
  86. setTimeout(() => {
  87. // 如果获取到了本地版本号,但未获取到远程版本号,默认通过
  88. if (isGetLocal && !isGetRemote) {
  89. isUpdateGlobalQGame = false;
  90. console.log("Gets the version number timeout, does not get the remote version number, but the local version number exists, passes by default");
  91. resolve();
  92. return;
  93. }
  94. }, 10000);
  95. }).then(() => {
  96. return new Promise((resolve, reject) => {
  97. if (!isUpdateGlobalQGame) {
  98. resolve();
  99. return;
  100. }
  101. console.log("全局安装@vivo-minigame/cli");
  102. // npm install -g @vivo-minigame/cli
  103. let cmd = `npm${commandSuffix}`;
  104. let args = ["install", "@vivo-minigame/cli", "-g"];
  105. let opts = {
  106. shell: true
  107. };
  108. let cp = childProcess.spawn(cmd, args, opts);
  109. cp.stdout.on('data', (data) => {
  110. console.log(`stdout: ${data}`);
  111. });
  112. cp.stderr.on('data', (data) => {
  113. console.log(`stderr: ${data}`);
  114. // reject();
  115. });
  116. cp.on('close', (code) => {
  117. console.log(`2 end) npm install -g @vivo-minigame/cli:${code}`);
  118. resolve();
  119. });
  120. });
  121. }).catch((e) => {
  122. console.log("catch e", e);
  123. });
  124. });
  125. gulp.task("createProj_VIVO", ["createGlobalQGame_VIVO"], function() {
  126. // 如果有即存项目,不再新建
  127. let isProjExist = fs.existsSync(projDir + "/node_modules") &&
  128. fs.existsSync(projDir + "/sign");
  129. if (isProjExist) {
  130. // 检测是否需要升级
  131. let packageCon = fs.readFileSync(`${projDir}/package.json`, "utf8");
  132. let minigamePath = path.join(projDir, "minigame.config.js");
  133. if (packageCon.includes("@vivo-minigame/cli-service") && fs.existsSync(minigamePath)) {
  134. return;
  135. }
  136. }
  137. // 如果有即存项目,但是是旧的项目,删掉后重新创建
  138. return new Promise((resolve, reject) => {
  139. if (!fs.existsSync(projDir)) {
  140. return resolve();
  141. }
  142. let delList = [projDir];
  143. del(delList, { force: true }).then(paths => {
  144. resolve();
  145. });
  146. }).then(function() {
  147. // 在项目中创建vivo项目
  148. return new Promise((resolve, reject) => {
  149. console.log("(proj)开始创建vivo快游戏项目");
  150. // mg init <project-name>
  151. let cmd = `mg${commandSuffix}`;
  152. let args = ["init", config.vivoInfo.projName];
  153. let opts = {
  154. cwd: releaseDir,
  155. shell: true
  156. };
  157. let cp = childProcess.spawn(cmd, args, opts);
  158. cp.stdout.on('data', (data) => {
  159. console.log(`stdout: ${data}`);
  160. });
  161. cp.stderr.on('data', (data) => {
  162. console.log(`stderr: ${data}`);
  163. // reject();
  164. });
  165. cp.on('close', (code) => {
  166. cp = null;
  167. console.log(`子进程退出码:${code}`);
  168. resolve();
  169. });
  170. });
  171. });
  172. });
  173. // 检查是否安装了adapter
  174. gulp.task("createAdapter_VIVO", ["createProj_VIVO"], function() {
  175. // npm view @qgame/adapter version
  176. // npm i -S @qgame/adapter@latest
  177. let remoteVersion, localVersion;
  178. let isGetRemote, isGetLocal;
  179. let isUpdateAdapter = true;
  180. return new Promise((resolve, reject) => { // 远程版本号
  181. childProcess.exec("npm view @qgame/adapter version", function(error, stdout, stderr) {
  182. if (!stdout) { // 获取 @vivo-minigame/cli 远程版本号失败
  183. console.log("Failed to get the remote adapter version number");
  184. resolve();
  185. return;
  186. }
  187. remoteVersion = stdout.replace(/[\r\n]/g, "").trim();
  188. isGetRemote = true;
  189. if (isGetRemote && isGetLocal) {
  190. isUpdateAdapter = remoteVersion != localVersion;
  191. console.log(`remoteVersion: ${remoteVersion}, localVersion: ${localVersion}`);
  192. resolve();
  193. }
  194. });
  195. childProcess.exec("npm ls @qgame/adapter version", { cwd: projDir }, function(error, stdout, stderr) {
  196. if (!stdout) { // 获取 @vivo-minigame/cli 本地版本号失败
  197. console.log("Failed to get the local adapter version number");
  198. resolve();
  199. return;
  200. }
  201. let info = stdout.split("@qgame/adapter@"); //@qgame/adapter@1.0.3
  202. info = Array.isArray(info) && info[1] && info[1].replace(/[\r\n]/g, "").trim();
  203. localVersion = info;
  204. isGetLocal = true;
  205. if (isGetRemote && isGetLocal) {
  206. isUpdateAdapter = remoteVersion != localVersion;
  207. console.log(`remoteVersion: ${remoteVersion}, localVersion: ${localVersion}`);
  208. resolve();
  209. }
  210. });
  211. setTimeout(() => {
  212. // 如果获取到了本地版本号,但未获取到远程版本号,默认通过
  213. if (!isGetLocal || !isGetRemote) {
  214. console.log("Failed to get the local or remote version number");
  215. resolve();
  216. return;
  217. }
  218. }, 10000);
  219. }).then(() => {
  220. return new Promise((resolve, reject) => {
  221. if (!isUpdateAdapter) {
  222. resolve();
  223. return;
  224. }
  225. console.log("安装@qgame/adapter");
  226. // npm i -S @qgame/adapter@latest
  227. let cmd = `npm${commandSuffix}`;
  228. let args = ["install", "-S", "@qgame/adapter@latest"];
  229. let opts = {
  230. shell: true,
  231. cwd: projDir
  232. };
  233. let cp = childProcess.spawn(cmd, args, opts);
  234. cp.stdout.on('data', (data) => {
  235. console.log(`stdout: ${data}`);
  236. });
  237. cp.stderr.on('data', (data) => {
  238. console.log(`stderr: ${data}`);
  239. // reject();
  240. });
  241. cp.on('close', (code) => {
  242. console.log(`2 end) npm i -S @qgame/adapter@latest:${code}`);
  243. resolve();
  244. });
  245. });
  246. }).catch((e) => {
  247. console.log("catch e", e);
  248. });
  249. });
  250. // 拷贝文件到vivo快游戏
  251. gulp.task("copyFileToProj_VIVO", ["createAdapter_VIVO"], function() {
  252. // 如果有js/main.js,将其删除
  253. let vivoMainPath = path.join(projDir, "src", "js", "main.js");
  254. if (fs.existsSync(vivoMainPath)) {
  255. fs.unlinkSync(vivoMainPath);
  256. }
  257. // 将临时文件夹中的文件,拷贝到项目中去
  258. let originalDir = `${tempReleaseDir}/**/*.*`;
  259. let stream = gulp.src(originalDir);
  260. return stream.pipe(gulp.dest(path.join(projSrc)));
  261. });
  262. // 拷贝icon到vivo快游戏
  263. gulp.task("copyIconToProj_VIVO", ["copyFileToProj_VIVO"], function() {
  264. let originalDir = config.vivoInfo.icon;
  265. let stream = gulp.src(originalDir);
  266. return stream.pipe(gulp.dest(projSrc));
  267. });
  268. // 清除vivo快游戏临时目录
  269. gulp.task("clearTempDir_VIVO", ["copyIconToProj_VIVO"], function() {
  270. // 删掉临时目录
  271. return del([tempReleaseDir], { force: true });
  272. });
  273. // 生成release签名(私钥文件 private.pem 和证书文件 certificate.pem )
  274. gulp.task("generateSign_VIVO", ["clearTempDir_VIVO"], function() {
  275. if (!config.vivoSign.generateSign) {
  276. return;
  277. }
  278. // https://doc.quickapp.cn/tools/compiling-tools.html
  279. return new Promise((resolve, reject) => {
  280. let cmd = `${opensslPath}`;
  281. let args = ["req", "-newkey", "rsa:2048", "-nodes", "-keyout", "private.pem",
  282. "-x509", "-days", "3650", "-out", "certificate.pem"];
  283. let opts = {
  284. cwd: projDir,
  285. shell: true
  286. };
  287. let cp = childProcess.spawn(cmd, args, opts);
  288. cp.stdout.on('data', (data) => {
  289. console.log(`stdout: ${data}`);
  290. });
  291. cp.stderr.on('data', (data) => {
  292. console.log(`stderr: ${data}`);
  293. data += "";
  294. if (data.includes("Country Name")) {
  295. cp.stdin.write(`${config.vivoSign.countryName}\n`);
  296. console.log(`Country Name: ${config.vivoSign.countryName}`);
  297. } else if (data.includes("Province Name")) {
  298. cp.stdin.write(`${config.vivoSign.provinceName}\n`);
  299. console.log(`Province Name: ${config.vivoSign.provinceName}`);
  300. } else if (data.includes("Locality Name")) {
  301. cp.stdin.write(`${config.vivoSign.localityName}\n`);
  302. console.log(`Locality Name: ${config.vivoSign.localityName}`);
  303. } else if (data.includes("Organization Name")) {
  304. cp.stdin.write(`${config.vivoSign.orgName}\n`);
  305. console.log(`Organization Name: ${config.vivoSign.orgName}`);
  306. } else if (data.includes("Organizational Unit Name")) {
  307. cp.stdin.write(`${config.vivoSign.orgUnitName}\n`);
  308. console.log(`Organizational Unit Name: ${config.vivoSign.orgUnitName}`);
  309. } else if (data.includes("Common Name")) {
  310. cp.stdin.write(`${config.vivoSign.commonName}\n`);
  311. console.log(`Common Name: ${config.vivoSign.commonName}`);
  312. } else if (data.includes("Email Address")) {
  313. cp.stdin.write(`${config.vivoSign.emailAddr}\n`);
  314. console.log(`Email Address: ${config.vivoSign.emailAddr}`);
  315. // cp.stdin.end();
  316. }
  317. // reject();
  318. });
  319. cp.on('close', (code) => {
  320. console.log(`子进程退出码:${code}`);
  321. // 签名是否生成成功
  322. let
  323. privatePem = path.join(projDir, "private.pem"),
  324. certificatePem = path.join(projDir, "certificate.pem");
  325. let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem);
  326. if (!isSignExits) {
  327. throw new Error("签名生成失败,请检查!");
  328. }
  329. resolve();
  330. });
  331. });
  332. });
  333. // 拷贝sign文件到指定位置
  334. gulp.task("copySignFile_VIVO", ["generateSign_VIVO"], function() {
  335. if (config.vivoSign.generateSign) { // 新生成的签名
  336. // 移动签名文件到项目中(Laya & vivo快游戏项目中)
  337. let
  338. privatePem = path.join(projDir, "private.pem"),
  339. certificatePem = path.join(projDir, "certificate.pem");
  340. let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem);
  341. if (!isSignExits) {
  342. return;
  343. }
  344. let
  345. xiaomiDest = `${projDir}/sign/release`,
  346. layaDest = `${workSpaceDir}/sign/release`;
  347. let stream = gulp.src([privatePem, certificatePem]);
  348. return stream.pipe(gulp.dest(xiaomiDest))
  349. .pipe(gulp.dest(layaDest));
  350. } else if (config.vivoInfo.useReleaseSign && !config.vivoSign.generateSign) { // 使用release签名,并且没有重新生成
  351. // 从项目中将签名拷贝到vivo快游戏项目中
  352. let
  353. privatePem = path.join(workSpaceDir, "sign", "release", "private.pem"),
  354. certificatePem = path.join(workSpaceDir, "sign", "release", "certificate.pem");
  355. let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem);
  356. if (!isSignExits) {
  357. return;
  358. }
  359. let
  360. xiaomiDest = `${projDir}/sign/release`;
  361. let stream = gulp.src([privatePem, certificatePem]);
  362. return stream.pipe(gulp.dest(xiaomiDest));
  363. }
  364. });
  365. gulp.task("deleteSignFile_VIVO", ["copySignFile_VIVO"], function() {
  366. if (config.vivoSign.generateSign) { // 新生成的签名
  367. let
  368. privatePem = path.join(projDir, "private.pem"),
  369. certificatePem = path.join(projDir, "certificate.pem");
  370. return del([privatePem, certificatePem], { force: true });
  371. }
  372. });
  373. gulp.task("modifyFile_VIVO", ["deleteSignFile_VIVO"], function() {
  374. // 修改manifest.json文件
  375. let manifestPath = path.join(projSrc, "manifest.json");
  376. if (!fs.existsSync(manifestPath)) {
  377. return;
  378. }
  379. let manifestContent = fs.readFileSync(manifestPath, "utf8");
  380. let manifestJson = JSON.parse(manifestContent);
  381. manifestJson.package = config.vivoInfo.package;
  382. manifestJson.name = config.vivoInfo.name;
  383. manifestJson.orientation = config.vivoInfo.orientation;
  384. manifestJson.config.logLevel = config.vivoInfo.logLevel || "off";
  385. manifestJson.deviceOrientation = config.vivoInfo.orientation;
  386. manifestJson.versionName = config.vivoInfo.versionName;
  387. manifestJson.versionCode = config.vivoInfo.versionCode;
  388. manifestJson.minPlatformVersion = config.vivoInfo.minPlatformVersion;
  389. manifestJson.icon = `/${path.basename(config.vivoInfo.icon)}`;
  390. if (config.vivoInfo.subpack) { // 分包
  391. manifestJson.subpackages = config.vivoSubpack;
  392. // 检测分包目录是否有入口文件
  393. console.log('检查分包文件...');
  394. if (manifestJson.subpackages) {
  395. for(let i = 0; i < manifestJson.subpackages.length; i ++) {
  396. let conf = manifestJson.subpackages[i];
  397. if (conf.root) {
  398. let rootPath = path.join(projSrc, conf.root);
  399. if (!fs.existsSync(rootPath)) {
  400. throw new Error(`分包文件/目录 ${rootPath} 不存在!`);
  401. }
  402. let jsIndex = rootPath.lastIndexOf('.js');
  403. let jsPath = rootPath;
  404. if (jsIndex < 0 || jsIndex != rootPath.length - 3) {
  405. jsPath = path.join(rootPath, 'game.js');
  406. }
  407. if (!fs.existsSync(jsPath)) {
  408. throw new Error(`分包文件/目录 ${jsPath} 不存在!`);
  409. }
  410. }
  411. }
  412. }
  413. } else {
  414. delete manifestJson.subpackages;
  415. }
  416. // 增加thirdEngine字段
  417. let EngineVersion = getEngineVersion();
  418. if (EngineVersion) {
  419. manifestJson.thirdEngine = {
  420. "laya": EngineVersion
  421. };
  422. }
  423. fs.writeFileSync(manifestPath, JSON.stringify(manifestJson, null, 4), "utf8");
  424. if (config.version) {
  425. let versionPath = projSrc + "/version.json";
  426. versionCon = fs.readFileSync(versionPath, "utf8");
  427. versionCon = JSON.parse(versionCon);
  428. }
  429. let indexJsStr = (versionCon && versionCon["index.js"]) ? versionCon["index.js"] : "index.js";
  430. // 修改game.js文件
  431. let gameJsPath = path.join(projSrc, "game.js");
  432. let content = fs.existsSync(gameJsPath) && fs.readFileSync(gameJsPath, "utf8");
  433. let reWriteMainJs = !fs.existsSync(gameJsPath) || !content.includes("vvmini");
  434. if (reWriteMainJs) {
  435. content = `require("@qgame/adapter");\nif(!window.navigator)\n\twindow.navigator = {};\nwindow.navigator.userAgent = 'Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E8301 VVGame NetType/WIFI Language/zh_CN';
  436. require("./libs/laya.vvmini.js");\nrequire("./index.js");`;
  437. } else {
  438. // 额外的,如果有引擎插件相关代码,需要删掉
  439. content = content.replace(/if\s\(window\.requirePlugin\)\s{\n[\w\"\.\-\/\(\);\s\n]*\n}\selse\s{\n[\w\"\.\-\/\(\);\s\n]*\n}\n/gm, "");
  440. }
  441. fs.writeFileSync(gameJsPath, content, "utf8");
  442. // vivo项目,修改index.js
  443. let filePath = path.join(projSrc, indexJsStr);
  444. if (!fs.existsSync(filePath)) {
  445. return;
  446. }
  447. let fileContent = fs.readFileSync(filePath, "utf8");
  448. fileContent = fileContent.replace(/loadLib(\(['"])/gm, "require$1./");
  449. fs.writeFileSync(filePath, fileContent, "utf8");
  450. })
  451. gulp.task("modifyMinJs_VIVO", ["modifyFile_VIVO"], function() {
  452. let fileJsPath = path.join(projSrc, "game.js");
  453. let content = fs.readFileSync(fileJsPath, "utf-8");
  454. if (!config.useMinJsLibs) { // 默认保留了平台文件,如果同时取消使用min类库,就会出现文件引用不正确的问题
  455. content = content.replace(/min\/laya(-[\w\d]+)?\.vvmini\.min\.js/gm, "laya.vvmini.js");
  456. } else {
  457. content = content.replace(/(min\/)?laya(-[\w\d]+)?\.vvmini(\.min)?\.js/gm, "min/laya.vvmini.min.js");
  458. }
  459. fs.writeFileSync(fileJsPath, content, 'utf-8');
  460. });
  461. gulp.task("version_VIVO", ["modifyMinJs_VIVO"], function () {
  462. // game.js默认不覆盖,如果同时开启版本管理,就会出现文件引用不正确的问题
  463. let fileJsPath = path.join(projSrc, "game.js");
  464. let content = fs.readFileSync(fileJsPath, "utf-8");
  465. content = content.replace(/laya(-[\w\d]+)?\.xmmini/gm, "laya.xmmini");
  466. content = content.replace(/index(-[\w\d]+)?\.js/gm, "index.js");
  467. fs.writeFileSync(fileJsPath, content, 'utf-8');
  468. if (config.version) {
  469. let versionPath = projSrc + "/version.json";
  470. let mainJSPath = projSrc + "/game.js";
  471. let srcList = [versionPath, mainJSPath];
  472. return gulp.src(srcList)
  473. .pipe(revCollector())
  474. .pipe(gulp.dest(projSrc));
  475. }
  476. });
  477. // 处理engine文件夹,允许开发者自己在bin下定义engine文件夹,以获得针对性的优化
  478. gulp.task("dealEngineFolder1_VIVO", ["version_VIVO"], function() {
  479. // 如果项目中有engine文件夹,我们默认该开发者是熟悉VIVO发布流程的,已经处理好所有的逻辑
  480. // 值得注意的:
  481. // 1) 如果有engine文件夹而未处理2D物理库(box2d.js/physics.js),项目将无法运行
  482. // 2) 如果未处理3D物理库(physics3D.js),打包时间将会很长
  483. let engineFolder = path.join(projDir, "src", "engine");
  484. isExistEngineFolder = fs.existsSync(engineFolder);
  485. if (!isExistEngineFolder) {
  486. return;
  487. }
  488. // 不想写一堆task任务,500ms默认拷贝完成吧
  489. // 未来有了更好的解决方案再修改
  490. return new Promise(function(resolve, reject) {
  491. // 将engine文件夹拷贝到projRoot下
  492. setTimeout(resolve, 500);
  493. var stream = gulp.src([`${engineFolder}/**/*.*`], {base: `${projDir}/src`});
  494. return stream.pipe(gulp.dest(projDir));
  495. }).then(function() {
  496. return new Promise(function(resolve, reject) {
  497. // 删掉src下的engine和adapter
  498. setTimeout(resolve, 500);
  499. return del([engineFolder], { force: true });
  500. });
  501. }).catch(function(err) {
  502. console.log(err);
  503. });
  504. });
  505. gulp.task("dealEngineFolder2_VIVO", ["dealEngineFolder1_VIVO"], function() {
  506. if (!isExistEngineFolder) {
  507. return;
  508. }
  509. let engineFolder = path.join(projDir, "engine");
  510. let engineFileList = fs.readdirSync(engineFolder);
  511. // 修改配置文件
  512. configVivoConfigFile(engineFileList);
  513. });
  514. // 如果项目中用到了 box2d.js|laya.physics.js/laya.physics3D.js ,需要特殊处理
  515. // 之前处理的是有项目中已经存在engine文件夹的情况,现在开始处理没有文件夹的情况
  516. gulp.task("dealNoCompile1_VIVO", ["dealEngineFolder2_VIVO"], function() {
  517. if (!isDealNoCompile) {
  518. return;
  519. }
  520. // 将js/bundle.js | libs/*.* 全放到engine文件夹中
  521. let indexJsStr = (versionCon && versionCon["index.js"]) ? versionCon["index.js"] : "index.js";
  522. let bundleJsStr = (versionCon && versionCon["js/bundle.js"]) ? versionCon["js/bundle.js"] : "js/bundle.js";
  523. let layaJsStr = (versionCon && versionCon["laya.js"]) ? versionCon["laya.js"] : "laya.js";
  524. // 修改index.js,去掉物理库前面的libs
  525. let filePath = path.join(projSrc, indexJsStr);
  526. let fileContent = fs.readFileSync(filePath, "utf8");
  527. let physicsNameList = [];
  528. if (fileContent.includes(bundleJsStr)) {
  529. let adapterJsPath = path.join(projSrc, bundleJsStr);
  530. physicsNameList.push(bundleJsStr);
  531. physicsLibsPathList.push(adapterJsPath);
  532. }
  533. if (fileContent.includes(layaJsStr)) {
  534. let layaJsPath = path.join(projSrc, layaJsStr);
  535. physicsNameList.push(layaJsStr);
  536. physicsLibsPathList.push(layaJsPath);
  537. }
  538. let libsList = fs.readdirSync(path.join(projSrc, "libs"));
  539. let libsFileName, libsFilePath;
  540. for (let i = 0, len = libsList.length; i < len; i++) {
  541. libsFileName = libsList[i];
  542. libsFilePath = path.join(projSrc, "libs", libsFileName);
  543. physicsNameList.push(`libs/${libsFileName}`);
  544. physicsLibsPathList.push(libsFilePath);
  545. }
  546. let minPath = path.join(projSrc, "libs", "min");
  547. if (fs.existsSync(minPath)) {
  548. let minLibsList = fs.readdirSync(minPath);
  549. let minLibsFileName, minLibsFilePath;
  550. for (let i = 0, len = minLibsList.length; i < len; i++) {
  551. minLibsFileName = minLibsList[i];
  552. minLibsFilePath = path.join(minPath, minLibsFileName);
  553. physicsNameList.push(`libs/min/${minLibsFileName}`);
  554. physicsLibsPathList.push(minLibsFilePath);
  555. }
  556. }
  557. // 修改配置文件
  558. configVivoConfigFile(physicsNameList);
  559. // 将物理库拷贝到engine中
  560. var stream = gulp.src(physicsLibsPathList, {base: projSrc});
  561. return stream.pipe(gulp.dest(path.join(projDir, "engine")));
  562. });
  563. function configVivoConfigFile(engineFileList, isAppend) {
  564. let vvConfigPath = path.join(projDir, "minigame.config.js");
  565. let content = fs.readFileSync(vvConfigPath, "utf8");
  566. let externalsStr = "";
  567. let libName;
  568. // let engineStr = '';
  569. let inLayaLibs = false, dirName, newLibPath;
  570. for (let i = 0, len = engineFileList.length; i < len; i++) {
  571. libName = engineFileList[i];
  572. if (i !== 0) {
  573. externalsStr += ',\n';
  574. }
  575. newLibPath = libName.replace("libs/min/", "").replace("libs/", "");
  576. inLayaLibs = config.uesEnginePlugin && fullRemoteEngineList.includes(newLibPath);
  577. dirName = inLayaLibs ? "laya-library" : "engine";
  578. if (inLayaLibs) {
  579. // engineStr += `{\n\t\tmodule_name:'${dirName}/${newLibPath}',\n\t\tmodule_path:'${dirName}/${newLibPath}',\n\t\tmodule_from:'${dirName}/${newLibPath}'\n\t},`;
  580. externalsStr += `\t{\n\t\tmodule_name:'${dirName}/${newLibPath}',\n\t\tmodule_path:'${dirName}/${newLibPath}',\n\t\tmodule_from:'${dirName}/${newLibPath}'\n\t}`;
  581. } else {
  582. externalsStr += `\t{\n\t\tmodule_name:'./${libName}',\n\t\tmodule_path:'./${libName}',\n\t\tmodule_from:'${dirName}/${libName}'\n\t}`;
  583. }
  584. }
  585. if (isAppend) { // 只有源码项目会走这个逻辑
  586. let oldExternalsReg = content.match(/const externals = (\[([^*].|\n|\r)*\])/);
  587. if (!oldExternalsReg) {
  588. throw new Error("源码项目适配vivo引擎插件,设置配置文件出错,请与服务提供商联系(code 3)!");
  589. }
  590. externalsStr = oldExternalsReg[1].replace(/\]$/, `,${externalsStr}\n]`);
  591. externalsStr = `const externals = ${externalsStr}`;
  592. } else {
  593. externalsStr = `const externals = [\n${externalsStr}\n]`;
  594. }
  595. content = content.replace(/const externals = \[([^*].|\n|\r)*\]/gm, externalsStr);
  596. fs.writeFileSync(vvConfigPath, content, "utf8");
  597. }
  598. gulp.task("dealNoCompile2_VIVO", ["dealNoCompile1_VIVO"], function() {
  599. if (!isDealNoCompile || physicsLibsPathList.length === 0) {
  600. return;
  601. }
  602. return del(physicsLibsPathList, { force: true });
  603. });
  604. // 处理引擎插件
  605. // 我们会将所有的libs下的文件放到engine里,但不能认定libs下全是我们的引擎,所以还是要加判断
  606. gulp.task("pluginEngin_VIVO", ["dealNoCompile2_VIVO"], function(cb) {
  607. let manifestJsonPath = path.join(projSrc, "manifest.json");
  608. let manifestJsonContent = fs.readFileSync(manifestJsonPath, "utf8");
  609. let conJson = JSON.parse(manifestJsonContent);
  610. let copyBinPath;
  611. if (!config.uesEnginePlugin) { // 没有使用引擎插件,还是像以前一样发布
  612. delete conJson.plugins;
  613. manifestJsonContent = JSON.stringify(conJson, null, 4);
  614. fs.writeFileSync(manifestJsonPath, manifestJsonContent, "utf8");
  615. return cb();
  616. }
  617. // 引擎源码项目
  618. // 将所有的min拷贝进来
  619. if (config.useMinJsLibs) {
  620. copyBinPath = path.join(workSpaceDir, "bin", "libs", "min");
  621. } else { // 如果不是min
  622. copyBinPath = path.join(workSpaceDir, "bin", "libs");
  623. }
  624. // 针对min引擎文件,很多配置文件也需要该,同时改
  625. if (config.version) {
  626. let versionPath = projSrc + "/version.json";
  627. versionCon = fs.readFileSync(versionPath, "utf8");
  628. versionCon = JSON.parse(versionCon);
  629. }
  630. let indexJsStr = (versionCon && versionCon["index.js"]) ? versionCon["index.js"] : "index.js";
  631. // 获取version等信息
  632. let coreLibPath = path.join(workSpaceDir, "bin", "libs", "laya.core.js");
  633. let isHasCoreLib = fs.existsSync(coreLibPath);
  634. let isOldAsProj = fs.existsSync(`${workSpaceDir}/asconfig.json`) && !isHasCoreLib;
  635. let isNewTsProj = fs.existsSync(`${workSpaceDir}/src/tsconfig.json`) && !isHasCoreLib;
  636. let EngineVersion = getEngineVersion();
  637. if (isOldAsProj || isNewTsProj) {
  638. // 下载对应版本js引擎,按照普通项目走
  639. console.log(`ts源码项目(${isNewTsProj})或as源码项目(${isOldAsProj}),开始处理引擎`);
  640. let engineNum = EngineVersion.split("beta")[0];
  641. let suffix = EngineVersion.includes("beta") ? `_beta${EngineVersion.split("beta")[1]}` : "";
  642. let engineURL;
  643. if (canUsePluginEngine(EngineVersion, "2.7.2")) { // 2.7.2 开始,下载地址更新为 cos 服务器
  644. engineURL = `https://ldc-1251285021.cos.ap-shanghai.myqcloud.com/download/Libs/LayaAirJS_${engineNum}${suffix}.zip`;
  645. } else {
  646. engineURL = `http://ldc.layabox.com/download/LayaAirJS_${engineNum}${suffix}.zip`;
  647. }
  648. let engineDownPath = path.join(releaseDir, `LayaAirJS_${engineNum}${suffix}.zip`);
  649. let engineExtractPath = path.join(releaseDir, `LayaAirJS_${engineNum}${suffix}`);
  650. if (config.useMinJsLibs) {
  651. copyBinPath = path.join(engineExtractPath, "js", "libs", "min");
  652. } else { // 如果不是min
  653. copyBinPath = path.join(engineExtractPath, "js", "libs");
  654. }
  655. // 情况1) 如果已经下载过引擎了,直接开始处理引擎插件
  656. if (fs.existsSync(copyBinPath)) {
  657. console.log("情况1) 如果已经下载过引擎了,直接开始处理引擎插件");
  658. return dealPluginEngine().then(() => {
  659. // return cb();
  660. }).catch((err) => {
  661. console.error("ts源码项目及as源码项目,下载或处理vivo引擎插件项目失败(code 1)!");
  662. throw err;
  663. });
  664. }
  665. // 情况2) 下载并解压引擎,然后开始处理引擎插件
  666. console.log("情况2) 下载并解压引擎,然后开始处理引擎插件");
  667. return downFileToDir(engineURL, engineDownPath).then(() => {
  668. console.log("下载引擎库成功,开始解压");
  669. return extractZipFile(engineDownPath, engineExtractPath);
  670. }).then(() => {
  671. console.log("解压成功,开始处理引擎插件");
  672. return dealPluginEngine();
  673. }).then(() => {
  674. // return cb();
  675. }).catch((err) => {
  676. console.error("ts源码项目及as源码项目,下载或处理vivo引擎插件项目失败(code 2)!");
  677. throw err;
  678. })
  679. }
  680. // 情况3) 非源码项目,开始处理引擎插件
  681. console.log("情况3) 非源码项目,开始处理引擎插件");
  682. return dealPluginEngine().then(() => {
  683. // return cb();
  684. }).catch((err) => {
  685. throw err;
  686. });
  687. function dealPluginEngine() {
  688. // 使用引擎插件
  689. let localUseEngineList = [];
  690. let copyEnginePathList;
  691. return new Promise(function(resolve, reject) {
  692. console.log(`修改game.js和game.json`);
  693. // 1) 修改game.js和game.json
  694. // 修改game.js
  695. let gameJsPath = path.join(projSrc, "game.js");
  696. let gameJscontent = fs.readFileSync(gameJsPath, "utf8");
  697. gameJscontent = gameJscontent.replace(`require("./${indexJsStr}");`, `requirePlugin('layaPlugin');\nrequire("./${indexJsStr}");`);
  698. fs.writeFileSync(gameJsPath, gameJscontent, "utf8");
  699. // 修改manifest.json,使其支持引擎插件
  700. conJson.plugins = {
  701. "laya-library": {
  702. "version": EngineVersion,
  703. "provider": "",
  704. "path": "laya-library"
  705. }
  706. }
  707. manifestJsonContent = JSON.stringify(conJson, null, 4);
  708. fs.writeFileSync(manifestJsonPath, manifestJsonContent, "utf8");
  709. resolve();
  710. }).then(function() {
  711. return new Promise(function(resolve, reject) {
  712. console.log(`确定用到的插件引擎`);
  713. // 2) 确定用到了那些插件引擎,并将插件引擎从index.js的引用中去掉
  714. let indexJsPath = path.join(projSrc, indexJsStr);
  715. let indexJsCon = fs.readFileSync(indexJsPath, "utf8");
  716. let item, fullRequireItem;
  717. for (let i = 0, len = fullRemoteEngineList.length; i < len; i++) {
  718. item = fullRemoteEngineList[i];
  719. fullRequireItem = config.useMinJsLibs ? `require("./libs/min/${item}")` : `require("./libs/${item}")`;
  720. if (indexJsCon.includes(fullRequireItem)) {
  721. localUseEngineList.push(item);
  722. indexJsCon = indexJsCon.replace(fullRequireItem + ";", "").replace(fullRequireItem + ",", "").replace(fullRequireItem, "");
  723. }
  724. }
  725. // 源码项目需要特殊处理
  726. if (isNewTsProj || isOldAsProj) {
  727. indexJsCon = indexJsCon.replace(`require("./laya.js");`, "").replace(`require("./laya.js"),`, "").replace(`require("./laya.js")`, "");
  728. let item, libPath, vivoConfigList = [];
  729. for (let i = 0, len = fullRemoteEngineList.length; i < len; i++) {
  730. item = fullRemoteEngineList[i];
  731. libPath = path.join(copyBinPath, item);
  732. if (fs.existsSync(libPath) && !["bytebuffer", "laya.physics3D", "worker", "workerloader"].includes(item.replace(".min.js", "").replace(".js", ""))) {
  733. localUseEngineList.push(item);
  734. config.useMinJsLibs ? vivoConfigList.push(`libs/min/${item}`) : vivoConfigList.push(`libs/${item}`);
  735. }
  736. }
  737. // let bundleJsStr = (versionCon && versionCon["js/bundle.js"]) ? versionCon["js/bundle.js"] : "js/bundle.js";
  738. // vivoConfigList.push(bundleJsStr);
  739. configVivoConfigFile(vivoConfigList, true);
  740. }
  741. fs.writeFileSync(indexJsPath, indexJsCon, "utf8");
  742. // 再次修改game.js,仅引用使用到的类库
  743. let pluginCon = "", normalCon = "";
  744. localUseEngineList.forEach(function(item) {
  745. pluginCon += `\trequirePlugin("laya-library/${item}");\n`;
  746. normalCon += `\trequire("laya-library/${item}");\n`;
  747. });
  748. let finalyPluginCon = `if (window.requirePlugin) {\n${pluginCon}\n} else {\n${normalCon}\n}`;
  749. let gameJsPath = path.join(projSrc, "game.js");
  750. let gameJsCon = fs.readFileSync(gameJsPath, "utf8");
  751. gameJsCon = gameJsCon.replace(`requirePlugin('layaPlugin');`, finalyPluginCon);
  752. fs.writeFileSync(gameJsPath, gameJsCon, "utf8");
  753. resolve();
  754. });
  755. }).then(function() {
  756. return new Promise(function(resolve, reject) {
  757. console.log(`将本地的引擎插件移动到laya-libs中`);
  758. // 3) 将本地的引擎插件移动到laya-libs中
  759. copyEnginePathList = [`${copyBinPath}/{${fullRemoteEngineList.join(",")}}`];
  760. gulp.src(copyEnginePathList).pipe(gulp.dest(`${projDir}/laya-library`));
  761. setTimeout(resolve, 500);
  762. });
  763. }).then(function() {
  764. return new Promise(function(resolve, reject) {
  765. console.log(`将libs中的本地引擎插件删掉`);
  766. // 4) 将libs中的本地引擎插件删掉
  767. let deleteList = [`${projDir}/engine/libs/{${localUseEngineList.join(",")}}`, `${projDir}/engine/libs/min/{${localUseEngineList.join(",")}}`];
  768. del(deleteList, { force: true }).then(resolve);
  769. });
  770. }).then(function() {
  771. return new Promise(async function(resolve, reject) {
  772. console.log(`完善引擎插件目录`);
  773. // 5) 引擎插件目录laya-libs中还需要新建几个文件,使该目录能够使用
  774. let
  775. layalibsPath = path.join(projDir, "laya-library"),
  776. engineIndex = path.join(layalibsPath, "index.js"),
  777. engineplugin = path.join(layalibsPath, "plugin.json");
  778. // enginesignature = path.join(layalibsPath, "signature.json");
  779. // index.js
  780. if (!fs.existsSync(layalibsPath)) {
  781. throw new Error("引擎插件目录创建失败,请与服务提供商联系!");
  782. }
  783. let layaLibraryList = fs.readdirSync(layalibsPath);
  784. let indexCon = "";
  785. layaLibraryList.forEach(function(item) {
  786. indexCon += `require("./${item}");\n`;
  787. });
  788. fs.writeFileSync(engineIndex, indexCon, "utf8");
  789. // plugin.json
  790. let pluginCon = {"main": "index.js"};
  791. fs.writeFileSync(engineplugin, JSON.stringify(pluginCon, null, 4), "utf8");
  792. // signature.json
  793. // let signatureCon = {
  794. // "provider": provider,
  795. // "signature": []
  796. // };
  797. // localUseEngineList.unshift("index.js");
  798. // let fileName, md5Str;
  799. // for (let i = 0, len = localUseEngineList.length; i < len; i++) {
  800. // fileName = localUseEngineList[i];
  801. // let md5Str = await getFileMd5(path.join(projDir, "laya-library", fileName));
  802. // signatureCon.signature.push({
  803. // "path": fileName,
  804. // "md5": md5Str
  805. // });
  806. // }
  807. // fs.writeFileSync(enginesignature, JSON.stringify(signatureCon, null, 4), "utf8");
  808. resolve();
  809. });
  810. }).catch(function(e) {
  811. throw e;
  812. })
  813. }
  814. });
  815. function downFileToDir(uri, dest){
  816. return new Promise((resolve, reject) => {
  817. if (!uri || !dest) {
  818. reject(new Error(`downFileToDir 参数不全: ${uri}/${dest}`));
  819. return;
  820. }
  821. let
  822. totalLen = 9999,
  823. progress = 0,
  824. layaresponse;
  825. var stream = fs.createWriteStream(dest);
  826. request(uri).on('error', function(err) {
  827. console.log("tool down err:" + err);
  828. reject(err);
  829. }).on("data", function(data) {
  830. progress += data.length;
  831. let downPercent = (progress / totalLen * 100).toFixed(3);
  832. // console.log(`down: ${downPercent}%`);
  833. }).on("response", function(response) {
  834. layaresponse = response;
  835. totalLen = response.caseless.dict['content-length'];
  836. }).pipe(stream).on('close', function() {
  837. if (layaresponse.statusCode == 200) {
  838. console.log("下载成功!");
  839. resolve();
  840. } else {
  841. reject(new Error(`下载失败,连接关闭 -> ${uri}`));
  842. }
  843. });
  844. });
  845. }
  846. function extractZipFile(zipPath, extractDir) {
  847. return new Promise((resolve, reject) => {
  848. if (!zipPath || !extractDir) {
  849. reject(new Error(`extractZipFile 参数不全: ${zipPath}/${extractDir}`));
  850. return false;
  851. }
  852. zipPath = `"${zipPath}"`;
  853. let unzipexepath = path.join(ideModuleDir, "../", "out", "codeextension", "updateversion", "tools", "unzip.exe");
  854. unzipexepath = `"${unzipexepath}"`;
  855. let cmd;
  856. if (process.platform === 'darwin') {
  857. cmd = "unzip -o " + zipPath + " -d " + "\"" + extractDir + "\"";
  858. } else {
  859. cmd = unzipexepath + " -o " + zipPath + " -d " + "\"" + extractDir + "\"";
  860. }
  861. childProcess.exec(cmd, (error, stdout, stderr) => {
  862. if (error || stderr) {
  863. reject(error || stderr);
  864. return;
  865. }
  866. resolve();
  867. });
  868. });
  869. }
  870. // 打包rpk
  871. gulp.task("buildRPK_VIVO", ["pluginEngin_VIVO"], function() {
  872. // 在vivo轻游戏项目目录中执行:
  873. // npm run build || npm run release
  874. let cmdStr = "build";
  875. if (config.vivoInfo.useReleaseSign) {
  876. cmdStr = "release";
  877. }
  878. return new Promise((resolve, reject) => {
  879. let cmd = `npm${commandSuffix}`;
  880. let args = ["run", cmdStr];
  881. let opts = {
  882. cwd: projDir,
  883. shell: true
  884. };
  885. let cp = childProcess.spawn(cmd, args, opts);
  886. // let cp = childProcess.spawn(`npx${commandSuffix}`, ['-v']);
  887. cp.stdout.on('data', (data) => {
  888. console.log(`stdout: ${data}`);
  889. });
  890. cp.stderr.on('data', (data) => {
  891. console.log(`stderr: ${data}`);
  892. console.log(`stderr(iconv): ${iconv.decode(data, 'gbk')}`);
  893. // reject();
  894. });
  895. cp.on('close', (code) => {
  896. console.log(`子进程退出码:${code}`);
  897. // rpk是否生成成功
  898. let distRpkPath = path.join(projDir, "dist", `${config.vivoInfo.package}${config.vivoInfo.useReleaseSign ? ".signed" : ""}.rpk`);
  899. if (!fs.existsSync(distRpkPath)) {
  900. throw new Error("rpk生成失败,请检查!");
  901. }
  902. resolve();
  903. });
  904. });
  905. });
  906. gulp.task("showQRCode_VIVO", ["buildRPK_VIVO"], function() {
  907. // 在vivo轻游戏项目目录中执行:
  908. // npm run server
  909. return new Promise((resolve, reject) => {
  910. let cmd = `npm${commandSuffix}`;
  911. let args = ["run", "server"];
  912. let opts = {
  913. cwd: projDir,
  914. shell: true
  915. };
  916. let cp = childProcess.spawn(cmd, args, opts);
  917. // let cp = childProcess.spawn(`npx${commandSuffix}`, ['-v']);
  918. cp.stdout.on('data', (data) => {
  919. console.log(`${data}`);
  920. // 输出pid,macos要用: macos无法kill进程树,也无法执行命令获取3000端口pid(没有查询权限),导致无法kill这个进程
  921. console.log('vv_qrcode_pid:' + cp.pid);
  922. });
  923. cp.stderr.on('data', (data) => {
  924. console.log(`stderr: ${data}`);
  925. console.log(`stderr(iconv): ${iconv.decode(data, 'gbk')}`);
  926. // reject();
  927. });
  928. cp.on('close', (code) => {
  929. console.log(`子进程退出码:${code}`);
  930. resolve();
  931. });
  932. });
  933. });
  934. gulp.task("buildVivoProj", ["showQRCode_VIVO"], function() {
  935. console.log("all tasks completed");
  936. });