publish.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. // v1.3.0
  2. //是否使用IDE自带的node环境和插件,设置false后,则使用自己环境(使用命令行方式执行)
  3. const useIDENode = process.argv[0].indexOf("LayaAir") > -1 ? true : false;
  4. //获取Node插件和工作路径
  5. let ideModuleDir = useIDENode ? process.argv[1].replace("gulp\\bin\\gulp.js", "").replace("gulp/bin/gulp.js", "") : "";
  6. let workSpaceDir = useIDENode ? process.argv[2].replace("--gulpfile=", "").replace("\\.laya\\publish.js", "").replace("/.laya/publish.js", "") + "/" : "./../";
  7. //引用插件模块
  8. const gulp = require(ideModuleDir + "gulp");
  9. const fs = require("fs");
  10. const path = require("path");
  11. const uglify = require(ideModuleDir + "gulp-uglify");
  12. const jsonminify = require(ideModuleDir + "gulp-jsonminify");
  13. const image = require(ideModuleDir + "gulp-image");
  14. const rev = require(ideModuleDir + "gulp-rev");
  15. const revdel = require(ideModuleDir + "gulp-rev-delete-original");
  16. const revCollector = require(ideModuleDir + 'gulp-rev-collector');
  17. const del = require(ideModuleDir + "del");
  18. const requireDir = require(ideModuleDir + 'require-dir');
  19. global.ideModuleDir = ideModuleDir;
  20. global.workSpaceDir = workSpaceDir;
  21. // 结合compile.js使用
  22. global.publish = true;
  23. const fileList = ["compile.js", "publish_xmgame.js", "publish_oppogame.js", "publish_vivogame.js"];
  24. requireDir('./', {
  25. filter: function (fullPath) {
  26. // 只用到了compile.js和publish.js
  27. if (fileList.includes(path.basename(fullPath))) {
  28. return true;
  29. } else {
  30. return false;
  31. }
  32. }
  33. });
  34. const QUICKGAMELIST = ["xmgame", "oppogame", "vivogame"];
  35. // 清理临时文件夹,加载配置
  36. let config,
  37. releaseDir,
  38. platform,
  39. isOpendataProj = false;
  40. gulp.task("loadConfig", function () {
  41. platform = "web"
  42. if (!useIDENode && process.argv.length > 5 && process.argv[4] == "--config") {
  43. platform = process.argv[5].replace(".json", "");
  44. }
  45. if (useIDENode && process.argv.length >= 4 && process.argv[3].startsWith("--config") && process.argv[3].endsWith(".json")) {
  46. platform = process.argv[3].match(/(\w+).json/)[1];
  47. }
  48. let _path;
  49. if (!useIDENode) {
  50. _path = platform + ".json";
  51. releaseDir = "../release/" + platform;
  52. }
  53. if (useIDENode) {
  54. _path = path.join(workSpaceDir, ".laya", `${platform}.json`);
  55. releaseDir = path.join(workSpaceDir, "release", platform).replace(/\\/g, "/");
  56. }
  57. global.platform = platform;
  58. let file = fs.readFileSync(_path, "utf-8");
  59. if (file) {
  60. if (QUICKGAMELIST.includes(platform)) {
  61. file = file.replace(/\$basePath/g, releaseDir + "/temprelease");
  62. } else {
  63. file = file.replace(/\$basePath/g, releaseDir);
  64. }
  65. config = JSON.parse(file);
  66. global.config = config;
  67. }
  68. // 是否是开放域项目
  69. let projInfoPath = path.join(workSpaceDir, path.basename(workSpaceDir) + ".laya");
  70. let isExist = fs.existsSync(projInfoPath);
  71. if (isExist) {
  72. try {
  73. let projInfo = fs.readFileSync(projInfoPath, "utf8");
  74. projInfo = projInfo && JSON.parse(projInfo);
  75. isOpendataProj = projInfo.layaProType === 12;
  76. } catch (e) {}
  77. }
  78. });
  79. // 重新编译项目
  80. // gulp.task("compile", ["loadConfig"], function () {
  81. // if (config.compile) {
  82. // console.log("compile");
  83. // }
  84. // });
  85. // 清理release文件夹
  86. gulp.task("clearReleaseDir", ["compile"], function (cb) {
  87. if (config.clearReleaseDir) {
  88. let delList = [releaseDir, releaseDir + "_pack", config.packfileTargetValue];
  89. // 小米快游戏,使用即存的项目,删掉Laya工程文件,保留小米环境项目文件
  90. if (platform === "xmgame") {
  91. let xmProjSrc = path.join(releaseDir, config.xmInfo.projName);
  92. // 不要删掉manifest.json/main.js文件
  93. // 这里不是node-glob语法,详见: https://github.com/sindresorhus/del
  94. delList = [`${xmProjSrc}/**`, `!${xmProjSrc}`, `!${xmProjSrc}/node_modules/**`, `!${xmProjSrc}/sign/**`, `!${xmProjSrc}/{babel.config.js,main.js,manifest.json,package.json,package-lock.json}`];
  95. } else if (platform === "oppogame") {
  96. let oppoProjSrc = path.join(releaseDir, config.oppoInfo.projName);
  97. delList = [`${oppoProjSrc}/**`, `!${oppoProjSrc}`, `!${oppoProjSrc}/dist/**`, `!${oppoProjSrc}/{manifest.json}`];
  98. } else if (platform === "vivogame") {
  99. let vvProj = path.join(releaseDir, config.vivoInfo.projName);
  100. let vvProjSrc = path.join(vvProj, "src");
  101. // 不要删掉manifest.json/main.js文件
  102. // 这里不是node-glob语法,详见: https://github.com/sindresorhus/del
  103. delList = [`${vvProjSrc}/**`, `!${vvProjSrc}`, `!${vvProjSrc}/sign/**`, `!${vvProjSrc}/{game.js,manifest.json}`];
  104. delList = delList.concat(`${vvProj}/engine/**`, `${vvProj}/config/**`);
  105. }
  106. del(delList, { force: true }).then(paths => {
  107. cb();
  108. });
  109. } else cb();
  110. });
  111. // copy bin文件到release文件夹
  112. gulp.task("copyFile", ["clearReleaseDir"], function () {
  113. let baseCopyFilter = [`${workSpaceDir}/bin/**/*.*`];
  114. // 只拷贝index.js中引用的类库
  115. if (config.onlyIndexJS) {
  116. baseCopyFilter = baseCopyFilter.concat(`!${workSpaceDir}/bin/libs/*.*`);
  117. }
  118. if (platform === "wxgame" && isOpendataProj) { // 开放域项目微信发布,仅拷贝用到的文件
  119. config.copyFilesFilter = [`${workSpaceDir}/bin/js/bundle.js`, `${workSpaceDir}/bin/index.js`, `${workSpaceDir}/bin/game.js`];
  120. if (config.projectType !== "as") { // 开放域精简类库
  121. config.copyFilesFilter.push(`${workSpaceDir}/bin/libs/laya.opendata.min.js`);
  122. }
  123. } else if (platform === "wxgame") { // 微信项目,不拷贝index.html,不拷贝百度bin目录中的文件
  124. config.copyFilesFilter = baseCopyFilter.concat([`!${workSpaceDir}/bin/index.html`, `!${workSpaceDir}/bin/{project.swan.json,swan-game-adapter.js}`]);
  125. } else if (platform === "bdgame") { // 百度项目,不拷贝index.html,不拷贝微信bin目录中的文件
  126. config.copyFilesFilter = baseCopyFilter.concat([`!${workSpaceDir}/bin/index.html`, `!${workSpaceDir}/bin/{project.config.json,weapp-adapter.js}`]);
  127. } else { // web|QQ项目|快游戏,不拷贝微信、百度在bin目录中的文件
  128. config.copyFilesFilter = baseCopyFilter.concat([`!${workSpaceDir}/bin/{game.js,game.json,project.config.json,weapp-adapter.js,project.swan.json,swan-game-adapter.js}`]);
  129. }
  130. // 快游戏,需要新建一个快游戏项目,拷贝的只是项目的一部分,将文件先拷贝到文件夹的临时目录中去
  131. if (QUICKGAMELIST.includes(platform)) {
  132. config.copyFilesFilter = config.copyFilesFilter.concat(`!${workSpaceDir}/bin/index.html`);
  133. releaseDir = global.tempReleaseDir = path.join(releaseDir, "temprelease");
  134. }
  135. global.releaseDir = releaseDir;
  136. var stream = gulp.src(config.copyFilesFilter, { base: `${workSpaceDir}/bin` });
  137. return stream.pipe(gulp.dest(releaseDir));
  138. });
  139. // copy libs中的js文件到release文件夹
  140. gulp.task("copyLibsJsFile", ["copyFile"], function () {
  141. if (!config.onlyIndexJS) {
  142. return;
  143. }
  144. if (platform === "wxgame" && isOpendataProj) { // 开放域项目微信发布,拷贝文件时已经拷贝类库文件了
  145. return;
  146. }
  147. // 开放域项目,as语言,没有libs目录,mac系统报错
  148. let libs = path.join(workSpaceDir, "bin", "libs");
  149. if (!fs.existsSync(libs)) {
  150. return;
  151. }
  152. // 分析index.js
  153. let indexJSPath = path.join(workSpaceDir, "bin", "index.js");
  154. let indexJsContent = fs.readFileSync(indexJSPath, "utf8");
  155. let libsList = indexJsContent.match(/loadLib\(['"]libs\/[a-zA-z0-9\/\.]+\.(js|wasm)['"]\)/g);
  156. if (!libsList) {
  157. libsList = [];
  158. }
  159. let
  160. item,
  161. libsName = "",
  162. libsStr = "";
  163. for (let i = 0, len = libsList.length; i < len; i++) {
  164. item = libsList[i];
  165. libsName = item.match(/loadLib\(['"]libs\/([a-zA-z0-9\/\.]+\.(js|wasm))['"]\)/);
  166. libsStr += libsStr ? `,${libsName[1]}` : libsName[1];
  167. }
  168. let copyLibsList = [`${workSpaceDir}/bin/libs/{${libsStr}}`];
  169. if (!libsStr.includes(",")) {
  170. copyLibsList = [`${workSpaceDir}/bin/libs/${libsStr}`];
  171. }
  172. // 微信、百度,需要拷贝对应平台的类库
  173. if (platform === "wxgame") {
  174. copyLibsList.push(`${workSpaceDir}/bin/libs/laya.wxmini.js`);
  175. } else if (platform === "bdgame") {
  176. copyLibsList.push(`${workSpaceDir}/bin/libs/laya.bdmini.js`);
  177. }
  178. var stream = gulp.src(copyLibsList, { base: `${workSpaceDir}/bin` });
  179. return stream.pipe(gulp.dest(releaseDir));
  180. });
  181. // 根据不同的项目类型拷贝平台文件
  182. gulp.task("copyPlatformFile", ["copyLibsJsFile"], function () {
  183. let fileLibsPath;
  184. if (useIDENode) {
  185. fileLibsPath = path.join(ideModuleDir, "../", "out", "layarepublic", "LayaAirProjectPack", "lib", "data");
  186. } else if (process.argv.length >= 8 && process.argv[6] === "--libspath") {
  187. fileLibsPath = process.argv[7];
  188. console.log("平台文件包是否存在: " + fs.existsSync(fileLibsPath));
  189. } else {
  190. console.log("没有接收到可用文件包位置,不拷贝对应平台文件");
  191. return;
  192. }
  193. // 开放域项目,微信发布
  194. if (platform === "wxgame" && isOpendataProj) {
  195. let platformDir = path.join(fileLibsPath, "wxfiles", "weapp-adapter.js");
  196. let stream = gulp.src(platformDir);
  197. return stream.pipe(gulp.dest(releaseDir));
  198. }
  199. // 微信项目,非开放域项目
  200. if (platform === "wxgame") {
  201. // 如果新建项目时已经点击了"微信/百度小游戏bin目录快速调试",不再拷贝
  202. let isHadWXFiles =
  203. fs.existsSync(path.join(workSpaceDir, "bin", "game.js")) &&
  204. fs.existsSync(path.join(workSpaceDir, "bin", "game.json")) &&
  205. fs.existsSync(path.join(workSpaceDir, "bin", "project.config.json")) &&
  206. fs.existsSync(path.join(workSpaceDir, "bin", "weapp-adapter.js"));
  207. if (isHadWXFiles) {
  208. return;
  209. }
  210. let platformDir = path.join(fileLibsPath, "wxfiles");
  211. let stream = gulp.src(platformDir + "/*.*");
  212. return stream.pipe(gulp.dest(releaseDir));
  213. }
  214. // qq玩一玩项目,区分语言
  215. if (platform === "qqwanyiwan") {
  216. let projectLangDir = config.projectType;
  217. let platformDir = path.join(fileLibsPath, "qqfiles", projectLangDir);
  218. let stream = gulp.src(platformDir + "/**/*.*");
  219. return stream.pipe(gulp.dest(releaseDir));
  220. }
  221. // 百度项目
  222. if (platform === "bdgame") {
  223. // 如果新建项目时已经点击了"微信/百度小游戏bin目录快速调试",不再拷贝
  224. let isHadBdFiles =
  225. fs.existsSync(path.join(workSpaceDir, "bin", "game.js")) &&
  226. fs.existsSync(path.join(workSpaceDir, "bin", "game.json")) &&
  227. fs.existsSync(path.join(workSpaceDir, "bin", "project.swan.json")) &&
  228. fs.existsSync(path.join(workSpaceDir, "bin", "swan-game-adapter.js"));
  229. if (isHadBdFiles) {
  230. return;
  231. }
  232. let platformDir = path.join(fileLibsPath, "bdfiles");
  233. let stream = gulp.src(platformDir + "/*.*");
  234. return stream.pipe(gulp.dest(releaseDir));
  235. }
  236. });
  237. // 拷贝文件后,针对特定平台修改文件内容
  238. gulp.task("modifyFile", ["copyPlatformFile"], function () {
  239. // QQ玩一玩
  240. if (platform === "qqwanyiwan") {
  241. // 修改bundle.js
  242. let bundleFilePath = path.join(releaseDir, "js", "bundle.js");
  243. if (fs.existsSync(bundleFilePath)) {
  244. let fileContent = fs.readFileSync(bundleFilePath, "utf8");
  245. var appendCode = 'if(window["BK"]&&window["BK"]["Sprite"]){BK.Script.loadlib("GameRes://layaforqq/laya.bkadpter.js");}';
  246. if (fileContent.includes("/**LayaGameStart**/") && !fileContent.includes(appendCode)) {
  247. fileContent = fileContent.split("/**LayaGameStart**/").join(`/**LayaGameStart**/\n${appendCode}`);
  248. fs.writeFileSync(bundleFilePath, fileContent, "utf8");
  249. }
  250. }
  251. // 修改index.js
  252. let indexFilePath = path.join(releaseDir, "index.js");
  253. if (fs.existsSync(indexFilePath)) {
  254. let fileContent = fs.readFileSync(indexFilePath, "utf8");
  255. fileContent = fileContent.replace(/loadLib\("/g, "BK.Script.loadlib(\"GameRes://");
  256. // 非as语言,要添加类库
  257. if ("as" !== config.projectType) {
  258. if (fileContent.includes("//-----libs-end-------")) {
  259. fileContent = fileContent.split("//-----libs-end-------").join(`//-----libs-end-------\nBK.Script.loadlib("GameRes://layaforqq/laya.bkadpter.js");`);
  260. }
  261. }
  262. fs.writeFileSync(indexFilePath, fileContent, "utf8");
  263. }
  264. // 修改main.js
  265. let mainFilePath = path.join(releaseDir, "main.js");
  266. if (fs.existsSync(mainFilePath)) {
  267. let fileContent = fs.readFileSync(mainFilePath, "utf8");
  268. fileContent = `BK.Script.loadlib("GameRes://layaforqq/qqPlayCore.js");
  269. BK.Script.loadlib("GameRes://layaforqq/bkadptpre.js");
  270. BK.Script.loadlib("GameRes://layaforqq/domparserinone.js");
  271. BK.Script.loadlib("GameRes://index.js");`;
  272. fs.writeFileSync(mainFilePath, fileContent, "utf8");
  273. }
  274. return;
  275. }
  276. // 百度项目,修改index.js
  277. if (platform === "bdgame") {
  278. let filePath = path.join(releaseDir, "index.js");
  279. if (!fs.existsSync(filePath)) {
  280. return;
  281. }
  282. let fileContent = fs.readFileSync(filePath, "utf8");
  283. fileContent = fileContent.replace(/loadLib\(/g, "require(");
  284. fs.writeFileSync(filePath, fileContent, "utf8");
  285. return;
  286. }
  287. });
  288. // 压缩json
  289. gulp.task("compressJson", ["modifyFile"], function () {
  290. if (config.compressJson) {
  291. return gulp.src(config.compressJsonFilter, { base: releaseDir })
  292. .pipe(jsonminify())
  293. .pipe(gulp.dest(releaseDir));
  294. }
  295. });
  296. // 压缩js
  297. gulp.task("compressJs", ["compressJson"], function () {
  298. if (config.compressJs) {
  299. return gulp.src(config.compressJsFilter, { base: releaseDir })
  300. .pipe(uglify())
  301. .on('error', function (err) {
  302. console.warn(err.toString());
  303. })
  304. .pipe(gulp.dest(releaseDir));
  305. }
  306. });
  307. // 压缩png,jpg
  308. gulp.task("compressImage", ["compressJs"], function () {
  309. if (config.compressImage) {
  310. return gulp.src(config.compressImageFilter, { base: releaseDir })
  311. .pipe(image({
  312. pngquant: true, //PNG优化工具
  313. optipng: false, //PNG优化工具
  314. zopflipng: true, //PNG优化工具
  315. jpegRecompress: false, //jpg优化工具
  316. mozjpeg: true, //jpg优化工具
  317. guetzli: false, //jpg优化工具
  318. gifsicle: false, //gif优化工具
  319. svgo: false, //SVG优化工具
  320. concurrent: 10, //并发线程数
  321. quiet: true //是否是静默方式
  322. // optipng: ['-i 1', '-strip all', '-fix', '-o7', '-force'],
  323. // pngquant: ['--speed=1', '--force', 256],
  324. // zopflipng: ['-y', '--lossy_8bit', '--lossy_transparent'],
  325. // jpegRecompress: ['--strip', '--quality', 'medium', '--min', 40, '--max', 80],
  326. // mozjpeg: ['-optimize', '-progressive'],
  327. // guetzli: ['--quality', 85]
  328. }))
  329. .pipe(gulp.dest(releaseDir));
  330. }
  331. });
  332. // 开放域的情况下,合并game.js和index.js,并删除game.js
  333. gulp.task("openData", ["compressImage"], function (cb) {
  334. if (config.openDataZone) {
  335. let indexPath = releaseDir + "/index.js";
  336. let indexjs = readFile(indexPath);
  337. let gamejs = readFile(releaseDir + "/game.js");
  338. if (gamejs && indexjs) {
  339. gamejs = gamejs.replace('require("index.js")', indexjs);
  340. fs.writeFileSync(indexPath, gamejs, 'utf-8');
  341. }
  342. if (isOpendataProj) {
  343. // 开放域项目,将game.js删掉,发布最小包
  344. del(`${releaseDir}/game.js`, { force: true }).then(paths => {
  345. cb();
  346. });
  347. } else {
  348. cb();
  349. }
  350. } else {
  351. cb();
  352. }
  353. });
  354. function readFile(path) {
  355. if (fs.existsSync(path)) {
  356. return fs.readFileSync(path, "utf-8");
  357. }
  358. return null;
  359. }
  360. // 生成版本管理信息
  361. gulp.task("version1", ["openData"], function () {
  362. if (config.version) {
  363. return gulp.src(config.versionFilter, { base: releaseDir })
  364. .pipe(rev())
  365. .pipe(gulp.dest(releaseDir))
  366. .pipe(revdel())
  367. .pipe(rev.manifest("version.json"))
  368. .pipe(gulp.dest(releaseDir));
  369. }
  370. });
  371. // 替换index.js里面的变化的文件名
  372. gulp.task("version2", ["version1"], function () {
  373. if (config.version) {
  374. //替换index.html和index.js里面的文件名称
  375. let htmlPath = releaseDir + "/index.html";
  376. let versionPath = releaseDir + "/version.json";
  377. let gameJSPath = releaseDir + "/game.js";
  378. let mainJSPath = releaseDir + "/main.js";
  379. let indexJSPath;
  380. let versionCon = fs.readFileSync(versionPath, "utf8");
  381. versionCon = JSON.parse(versionCon);
  382. indexJSPath = releaseDir + "/" + versionCon["index.js"];
  383. // 替换config.packfileFullValue中的路径
  384. let packfileStr = JSON.stringify(config.packfileFullValue).replace(/\\\\/g, "/");
  385. let tempPackfile = `${workSpaceDir}/.laya/configTemp.json`;
  386. fs.writeFileSync(tempPackfile, packfileStr, "utf8");
  387. let srcList = [versionPath, indexJSPath, tempPackfile];
  388. if (fs.existsSync(htmlPath)) {
  389. srcList.push(htmlPath);
  390. }
  391. if (fs.existsSync(gameJSPath)) {
  392. srcList.push(gameJSPath);
  393. }
  394. if (fs.existsSync(mainJSPath)) {
  395. srcList.push(mainJSPath);
  396. }
  397. return gulp.src(srcList)
  398. .pipe(revCollector())
  399. .pipe(gulp.dest(releaseDir));
  400. }
  401. });
  402. // 筛选4M包
  403. gulp.task("packfile", ["version2"], function() {
  404. if (config.version) {
  405. // 从release目录取得带有版本号的目录
  406. let tempPackfile = `${workSpaceDir}/.laya/configTemp.json`;
  407. let releasePackfile = `${releaseDir}/configTemp.json`;
  408. let packfileStr = fs.readFileSync(releasePackfile, "utf8");
  409. config.packfileFullValue = JSON.parse(packfileStr);
  410. // 删掉临时目录
  411. fs.unlinkSync(tempPackfile);
  412. fs.unlinkSync(releasePackfile);
  413. }
  414. if (config.packfile) { // 提取本地包(文件列表形式)
  415. return gulp.src(config.packfileFullValue, { base: releaseDir })
  416. .pipe(gulp.dest(config.packfileTargetValue || releaseDir + "_pack"));
  417. }
  418. });
  419. // 起始任务
  420. gulp.task("publish", ["buildXiaomiProj", "buildOPPOProj", "buildVivoProj"], function () {
  421. console.log("All tasks completed!");
  422. });