@ttmg/cli 0.1.0-beta.5 → 0.1.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +1 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- "use strict";var e,o,t,n,s,r,i=require("commander"),c=require("inquirer"),a=require("fs"),l=require("jsdom"),u=require("prettier"),d=require("chalk"),p=require("express"),m=require("path"),f=require("cheerio"),g=require("child_process"),h=require("os"),y=require("crypto"),b=require("archiver"),w=require("multer"),S=require("ws"),v=require("axios"),k=require("form-data"),j=require("qrcode-terminal"),M=require("ttmg-pack");function _(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function $(e){if(Object.prototype.hasOwnProperty.call(e,"__esModule"))return e;var o=e.default;if("function"==typeof o){var t=function e(){var t=!1;try{t=this instanceof e}catch{}return t?Reflect.construct(o,arguments,this.constructor):o.apply(this,arguments)};t.prototype=o.prototype}else t={};return Object.defineProperty(t,"__esModule",{value:!0}),Object.keys(e).forEach(function(o){var n=Object.getOwnPropertyDescriptor(e,o);Object.defineProperty(t,o,n.get?n:{enumerable:!0,get:function(){return e[o]}})}),t}function T(){return o?e:(o=1,e={CONFIG_FILE_NAME:"minigame.config.json",SDK_URL:"https://connect.tiktok-minis.com/game/sdk.js",VCONSOLE_URL:"https://connect.tiktok-minis.com/libs/vConsole.js",VCONSOLE_INIT:"\n if(typeof VConsole === 'function') {\n window.vConsole = new VConsole();\n }\n ",MINIS_MANIFEST_FILE_NAME:"minis.manifest.json",MINIS_RUNTIME_URL:"https://www.tiktok.com/minigames/runtime"})}function O(){if(n)return t;n=1;const e=a,o=l,s=u,r=d,{JSDOM:i}=o,{CONFIG_FILE_NAME:c,SDK_URL:p,VCONSOLE_URL:m,VCONSOLE_INIT:f}=T(),g=`${process.cwd()}/${c}`,h=`${process.cwd()}/index.html`;return t=async function({config:o,clientKey:t}){if(e.writeFileSync(g,JSON.stringify(o,null,2)),!e.existsSync(h))return void console.error("index.html does not exist");const n=e.readFileSync(h,"utf8"),c=new i(n),{document:a}=c.window;let l=a.querySelector("head");l||(l=a.createElement("head"),a.documentElement.insertBefore(l,a.body));const u=Array.from(a.querySelectorAll("script")),d=u.some(e=>e.src&&e.src.includes(p)),y=u.some(e=>e.src&&e.src.includes("vConsole.js"));let b=null;if(d){const e=Array.from(l.querySelectorAll("script"));if(b=e.find(e=>function(e,o){return!!e.innerHTML&&e.innerHTML.includes("TTMinis.game.init")&&e.innerHTML.includes(o)}(e,t)),b)console.log("JS SDK 已接入,跳过 SDK 相关脚本注入");else{const o=e.filter(e=>e.src&&e.src.includes(p));o.length>0&&(b=o[o.length-1])}}else{const e=a.createElement("script");e.src=p,l.insertBefore(e,l.firstChild);const o=a.createElement("script");o.innerHTML=`\n window.TTMinis = TTMinis;\n TTMinis.game.init({\n clientKey: "${t}",\n });\n `,l.insertBefore(o,e.nextSibling),b=o}if(function(e){return e.startsWith("sb")}(t)&&(console.log("Sandbox 环境,跳过 vConsole 相关脚本注入"),!y)){let e=b;e=e?e.nextSibling:l.firstChild;const o=a.createElement("script");o.src=m;const t=a.createElement("script");t.innerHTML=f,l.insertBefore(o,e),l.insertBefore(t,e)}const w=await s.format(c.serialize(),{parser:"html"});e.writeFileSync(h,w),console.log(r.green.bold("TikTok H5 Mini Game initialization has been completed..."))},t}var P=function(){if(r)return s;r=1;const e=c,o=O();return s=function(){e.createPromptModule()([{type:"input",name:"clientKey",message:"Please input client key"},{type:"input",name:"devPort",message:"Please input dev port",default:9527}]).then(async e=>{const{clientKey:t,devPort:n}=e,s={_comment:"orientation is the orientation of the game. It can be either 'VERTICAL' or 'HORIZONTAL'.our game default is VERTICAL; minigame.config.json dev is a configuration file for minigame development. You can use it to configure the minigame.",orientation:"VERTICAL",dev:{port:n}};await o({clientKey:t,config:s}),process.exit(0)}).catch(()=>{process.exit(1)})},s}(),E=_(P);function N(e,o=[]){const t=["--auto-open-devtools-for-tabs",...o];let n;switch(h.platform()){case"darwin":n=`open -n -a "${"Google Chrome"}" --args "${e}" ${t.join(" ")}`;break;case"win32":n=`start "" chrome "${e}" ${t.join(" ")}`;break;default:n=`bash -lc 'command -v google-chrome >/dev/null && google-chrome "${e}" ${t.join(" ")} || chromium "${e}" ${t.join(" ")}'`}console.log("执行命令:",n),g.exec(n,(o,t,n)=>{if(o)return console.log(d.red("Open Chrome failed:"),o.message),void console.log(d.yellow(` Please open Chrome and input URL: ${e}`));n&&console.warn(n.trim())})}function I(){const e=h.networkInterfaces();for(const o in e){const t=e[o];if(t)for(const e of t)if("IPv4"===e.family&&!e.internal)return e.address}}var C,z,L=$(Object.freeze({__proto__:null,centerQRCode:function(e){const o=process.stdout.columns||80,t=e.split("\n"),n=t.reduce((e,o)=>Math.max(e,o.length),0),s=Math.floor((o-n)/3),r=" ".repeat(s>0?s:0);return t.map(e=>r+e).join("\n")},getDesktopPath:function(){const e=h.homedir(),o=["Desktop","桌面"];for(const t of o){const o=m.join(e,t);if(a.existsSync(o))return o}return m.join(e,"Desktop")},getLocalIP:I,openUrl:N}));var x,U,q,D,F=function(){if(z)return C;z=1;const e=p,o=m,t=a,n=d,s=f,r=e(),{openUrl:i}=L,c=y,{CONFIG_FILE_NAME:l,MINIS_RUNTIME_URL:u}=T();return C=function(){const a=o.join(process.cwd(),l);if(!t.existsSync(a))return void console.log(n.red.bold(`${l} is not exist, please run minis game init first`));const d=JSON.parse(t.readFileSync(a,"utf8")),p=d.dev?.port||9527;console.log(n.yellow.bold("⚠️ Before dev, please ensure:\n 1. The account used to login www.tiktok.com is in the sandbox target user range of Minis developer platform, otherwise login authorization will throw an error.\n 2. The browser allows www.tiktok.com <popup and redirect>, because the authorization login linkage needs to open a new tab popup for operation, otherwise the authorization login linkage will not be able to debug normally.")),console.log(n.bold.blue("\n \n============== start dev your game, it will take a few seconds ============ \n \n")),r.use((e,o,t)=>{e.url.endsWith(".br")?o.setHeader("Content-Encoding","br"):e.url.endsWith(".gz")&&o.setHeader("Content-Encoding","gzip"),t()}),r.use((e,r,i)=>{try{const e=o.join(process.cwd(),"index.html"),n=t.readFileSync(e,"utf8"),i=s.load(n),a=[];i("script:not([src])").each((e,o)=>{const t=i(o).html();t&&t.trim()&&a.push(t)});a.map(e=>`'sha256-${c.createHash("sha256").update(e,"utf8").digest("base64")}'`);const l="*";r.setHeader("Content-Security-Policy",`default-src 'self';script-src 'self' data: blob: 'unsafe-eval' 'unsafe-inline' connect.tiktok-minis.com sf-connect.tiktokminis.us;img-src 'self' ${l} data: blob: *; connect-src 'self' ${l} data: blob: ; style-src 'self' ${l} 'unsafe-inline' fonts.googleapis.com data: blob: *; font-src 'self' fonts.gstatic.com blob: data: *; media-src 'self' ${l} data: blob: *; frame-src 'none'; base-uri 'self'; worker-src 'self' blob: data: ;`)}catch(e){console.warn(n.red("Failed to set CSP header:"),e.message)}i()}),r.use(e.static(o.join(process.cwd()))),r.listen(p,()=>{const e=`${u}?minis_url=${`http://localhost:${p}`}&enable_log=1`;console.log(`you can access ${n.green.underline.bold(e)} to debug your game in browser...`);try{i(e)}catch(e){console.warn(n.red("Failed to open browser, you can access it manually"),e.message)}})}}(),H=_(F);function G(){if(U)return x;U=1;const e=d,o=a,t=m,{MINIS_MANIFEST_FILE_NAME:n}=T();function s(e,o){let t=o.find(o=>"folder"===o.type&&o.name===e[0]);return t||(t={type:"folder",name:e[0],children:[]},o.push(t)),e.length>1?s(e.slice(1),t.children):t}function r(e,n=e){const s={};return o.readdirSync(e).forEach(i=>{const c=t.join(e,i);if(o.statSync(c).isDirectory()){const e=r(c,n);Object.assign(s,e)}else if(""!==t.extname(i)&&i){const e="/"+t.relative(n,c).replace(/\\/g,"/");s[c]=e}}),s}return x={buildMinisManifest:async function(){try{const e=t.join(process.cwd()),i=[],c=r(e);Object.keys(c).filter(e=>!e.endsWith(".map")).forEach(e=>{const o=c[e].split("/").filter(Boolean),t=o.pop()||"";if(0===o.length)i.push({type:"file",name:t});else{s(o,i).children.push({type:"file",name:t})}}),o.writeFileSync(t.join(e,n),JSON.stringify({name:n,resource_list:i},null,2))}catch(o){console.error(e.red(`Error during debug process: ${o.message}`)),o instanceof Error&&o.stack&&console.error(e.red(`Stack trace: ${o.stack}`)),process.exit(1)}}}}var R=_(function(){if(D)return q;D=1;const e=a,o=h,t=m,n=d,s=b,r=c.createPromptModule(),{buildMinisManifest:i}=G();return q=async function(){try{const{zipName:c}=await r({type:"input",name:"zipName",default:"game",message:"Please input zip name"}),a=Date.now();console.log(n.bold.blue("start build your game, it will take a few minutes..."));e.readdirSync(process.cwd()).forEach(o=>{o.endsWith(".zip")&&e.unlinkSync(t.join(process.cwd(),o))}),await i();const l=s("zip",{zlib:{level:9}});l.on("error",function(e){throw e});const u=function(){const n=o.homedir(),s=["Desktop","桌面"];for(const o of s){const s=t.join(n,o);if(e.existsSync(s))return s}const r=t.join(n,"Desktop");return console.log(`未找到特定桌面文件夹,使用默认路径: ${r}`),r}(),d=t.join(u,`${c}.zip`);await l.pipe(e.createWriteStream(d)),await l.directory(t.resolve(process.cwd()),!1),await l.finalize(),console.log(n.yellow.bold(`build ${c}.zip success, you can find it in desktop, use time ${Date.now()-a} ms`)),process.exit(0)}catch(e){console.log(n.red(`auto build ${zipNameInput}.zip failed: ${e.message}, you should zip it manually`)),process.exit(1)}}}());class A{constructor(e){this.listeners=[],this.state=e}static getInstance(e){return A.instance||(A.instance=new A(e)),A.instance}getState(){return this.state}setState(e){this.state=Object.assign(Object.assign({},this.state),e),this.listeners.forEach(e=>e(this.state))}subscribe(e){return this.listeners.push(e),()=>{this.listeners=this.listeners.filter(o=>o!==e)}}reset(e){this.state=e,this.listeners.forEach(e=>e(this.state))}}const W=A.getInstance({clientServerPort:"",clientServerHost:"",clientKey:"",packages:{}}),B=9529,V=m.join(h.homedir(),"__TTMG__");function K({clientWsPort:e}){const o={ws_port:B,nodeWsPort:B,clientWsPort:e};return btoa(JSON.stringify(o))}let J;function Q(){J.close(()=>{console.log("Dev server closed"),J=null,process.exit(0)})}const Y=m.join(h.homedir(),".ttmg-cli");function Z(){const e=m.join(process.cwd(),"project.config.json");let o;try{const t=JSON.parse(a.readFileSync(e,"utf-8"));o=t.appid||t.appId}catch(e){o=""}if(o)return o;console.log(d.red.bold("No appid found in project.config.json, you should provide it in project.config.json")),process.exit(1)}function X(){const e=Z();return m.join(h.homedir(),"__TTMG__",e)}async function ee(){const e=X();console.log(d.yellow.bold("Start compress game resource"));const o=m.join(h.homedir(),"__TTMG__","upload.zip");var t,n;await(t=e,n=o,new Promise((e,o)=>{const s=a.createWriteStream(n),r=b("zip",{zlib:{level:9}});s.on("close",()=>{e()}),s.on("error",e=>{o(e)}),r.on("error",e=>{o(e)}),r.pipe(s),r.directory(t,!1),r.finalize()})),console.log(d.green.bold("Compress game package resource success \n"));const s=await async function(e){const o=new k;o.append("file",a.createReadStream(e),"upload.zip");try{console.log(d.yellow.bold("Start upload resource to client"));const{clientServerHost:t,clientServerPort:n}=W.getState(),s=await v.post(`http://${t}:${n}/game/upload`,o,{headers:o.getHeaders()});return a.unlinkSync(e),s}catch(e){console.error("Upload resource to client failed:",e.message)}}(o);return{isSuccess:200===(null==s?void 0:s.status)}}m.join(Y,"config.json");const oe=new class{constructor(){this.ws=new S.Server({port:B}),this.ws.on("connection",e=>{e.on("message",e=>{const o=JSON.parse(e.toString());console.log("Client Message",o);if("browser"===o.from){switch(o.method){case"connected":this.sendUploadStatus("start"),ee().then(e=>{e.isSuccess?(this.sendUploadStatus("success",{packages:W.getState().packages,isSuccess:e.isSuccess}),console.log(d.green.bold("Upload resource to client successfully!"))):(this.sendUploadStatus("error",{errMsg:e.errorMsg}),console.log(d.red.bold("Upload resource to client failed!")))}).catch(()=>{this.sendUploadStatus("error",{isSuccess:!1}),console.log(d.red.bold("Start upload resource to client failed!"))});break;case"closeLocalDebug":console.log("closeLocalDebug"),this.ws.close(),Q(),console.log("close server")}}else{switch(o.method){case"shareDevParams":console.log("shareDevParams",o);const e=o.payload;console.log("shareDevParams",e);const{host:t,port:n,wsPort:s}=e;W.setState({clientServerPort:n,clientServerHost:t});const r=`http://${t}:${n}?session=${K({clientWsPort:s})}`;N(r),console.log(d.bold.yellow(`Game debug is ready! Visit ${r} in your browser.`));break;case"checkPermissionFailed":console.log(d.red.bold("Check permission failed! Please authorize in client first."))}}})})}send(e){this.ws.clients.forEach(o=>{if(o.readyState===S.OPEN){const t=Object.assign(Object.assign({},e),{from:"nodeServer"});o.send(JSON.stringify(t))}})}close(){this.ws.close()}sendUploadStatus(e,o){this.send({method:"uploadStatus",status:e,payload:o})}};async function te(){await async function(){console.log(d.yellow.bold("Start compile game for debug"));const e=process.cwd(),o=X();a.existsSync(o)||a.mkdirSync(o,{recursive:!0});const{isSuccess:t,errorMsg:n,packages:s}=await M.debugPkgs({entry:e,output:o,rules:{mainPackageSizeLimit:104857600,projectSizeLimit:104857600}});t?(W.setState({packages:s}),console.log(d.green.bold("Compile game package success \n"))):(console.log(d.redBright("Build game package failed, Please check the error message below:")),console.log(d.redBright(n)),process.exit(1))}(),await async function(){J&&Q();const e=p(),o=9528,t=w({dest:h.tmpdir()});return e.use(p.json()),e.use(p.urlencoded({extended:!0})),e.post("/game/env",async(e,o)=>{const t=e.body,{host:n,port:s,wsPort:r}=t;W.setState({clientServerPort:s,clientServerHost:n});const i=`http://${n}:${s}?session=${K({clientWsPort:r})}`;N(i),console.log(d.bold.yellow(`Game debug is ready! Visit ${i} in your browser.`)),o.json({code:0,msg:"ok",data:{devUrl:i}})}),e.post("/game/upload",t.single("file"),async(e,o)=>{o.json({code:0,msg:"ok",filename:e.file.filename,originalname:e.file.originalname,size:e.file.size,path:e.file.path})}),J=e.listen(o,()=>{console.log(d.cyan.bold("Node devServer is running on port 9528"))}),{port:o,host:I()}}(),await async function(){const e=Z(),o=m.join(V,e),t=I();console.log(d.green.bold("Tips:")),console.log(` 1. ${d.yellow.bold("Scan the QR code to start the client devServer.")}`),console.log(` 2. ${d.yellow.bold("Will auto upload compiled resource to client.")} ${d.bold(o)}`),console.log(` 3. ${d.yellow.bold("Debug your game in the browser.")}\n`);const n=`https://www.tiktok.com/ttmg/dev/${e}?host=${t}&port=9529`;j.generate(n,{small:!0},e=>{console.log(e)})}(),await async function(){let e=null;a.watch(process.cwd(),(o,t)=>{console.log(d.yellow("game resource change, restart to upload")),e&&clearTimeout(e),e=setTimeout(async()=>{oe.sendUploadStatus("start"),ee().then(e=>{e.isSuccess?oe.sendUploadStatus("success"):oe.sendUploadStatus("error",{errMsg:e.errorMsg})}).catch(()=>{oe.sendUploadStatus("error")}),e=null},500)})}()}var ne="0.1.0-beta.5";const se=new i.Command;se.name("ttmg").description("TikTok Mini Games Command Line Tool").version(ne,"-v, --version","显示版本号").option("dev","Debug TikTok Mini Games for Client").option("dev --h5","Debug TikTok Mini Games for Web"),se.option("--h5","H5 Mini Game").command("init").description("Initialize project").action(()=>{se.opts().h5?E():console.log("Native Mini Game initialize")}),se.option("--h5","H5 Mini Game").command("dev").description("Open browser dev environment").action(()=>{se.opts().h5?H():te()}),se.option("--h5","H5 Mini Game").command("build").description("Bundle project").action(()=>{se.opts().h5?R():console.log("Native Mini Game bundle")}),se.command("login").description("User Dev Portal Account to Login").action(async()=>{console.log("will support soon")}),se.parse(process.argv);
2
+ "use strict";var e,o,t,n,s,r,i=require("commander"),c=require("inquirer"),a=require("fs"),l=require("jsdom"),u=require("prettier"),d=require("chalk"),p=require("express"),m=require("path"),g=require("cheerio"),f=require("child_process"),h=require("os"),y=require("crypto"),b=require("archiver"),w=require("multer"),S=require("ws"),v=require("axios"),k=require("form-data"),j=require("qrcode-terminal"),M=require("ttmg-pack");function _(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function $(e){if(Object.prototype.hasOwnProperty.call(e,"__esModule"))return e;var o=e.default;if("function"==typeof o){var t=function e(){var t=!1;try{t=this instanceof e}catch{}return t?Reflect.construct(o,arguments,this.constructor):o.apply(this,arguments)};t.prototype=o.prototype}else t={};return Object.defineProperty(t,"__esModule",{value:!0}),Object.keys(e).forEach(function(o){var n=Object.getOwnPropertyDescriptor(e,o);Object.defineProperty(t,o,n.get?n:{enumerable:!0,get:function(){return e[o]}})}),t}function T(){return o?e:(o=1,e={CONFIG_FILE_NAME:"minigame.config.json",SDK_URL:"https://connect.tiktok-minis.com/game/sdk.js",VCONSOLE_URL:"https://connect.tiktok-minis.com/libs/vConsole.js",VCONSOLE_INIT:"\n if(typeof VConsole === 'function') {\n window.vConsole = new VConsole();\n }\n ",MINIS_MANIFEST_FILE_NAME:"minis.manifest.json",MINIS_RUNTIME_URL:"https://www.tiktok.com/minigames/runtime"})}function E(){if(n)return t;n=1;const e=a,o=l,s=u,r=d,{JSDOM:i}=o,{CONFIG_FILE_NAME:c,SDK_URL:p,VCONSOLE_URL:m,VCONSOLE_INIT:g}=T(),f=`${process.cwd()}/${c}`,h=`${process.cwd()}/index.html`;return t=async function({config:o,clientKey:t}){if(e.writeFileSync(f,JSON.stringify(o,null,2)),!e.existsSync(h))return void console.error("index.html does not exist");const n=e.readFileSync(h,"utf8"),c=new i(n),{document:a}=c.window;let l=a.querySelector("head");l||(l=a.createElement("head"),a.documentElement.insertBefore(l,a.body));const u=Array.from(a.querySelectorAll("script")),d=u.some(e=>e.src&&e.src.includes(p)),y=u.some(e=>e.src&&e.src.includes("vConsole.js"));let b=null;if(d){const e=Array.from(l.querySelectorAll("script"));if(b=e.find(e=>function(e,o){return!!e.innerHTML&&e.innerHTML.includes("TTMinis.game.init")&&e.innerHTML.includes(o)}(e,t)),b)console.log("JS SDK 已接入,跳过 SDK 相关脚本注入");else{const o=e.filter(e=>e.src&&e.src.includes(p));o.length>0&&(b=o[o.length-1])}}else{const e=a.createElement("script");e.src=p,l.insertBefore(e,l.firstChild);const o=a.createElement("script");o.innerHTML=`\n window.TTMinis = TTMinis;\n TTMinis.game.init({\n clientKey: "${t}",\n });\n `,l.insertBefore(o,e.nextSibling),b=o}if(function(e){return e.startsWith("sb")}(t)&&(console.log("Sandbox 环境,跳过 vConsole 相关脚本注入"),!y)){let e=b;e=e?e.nextSibling:l.firstChild;const o=a.createElement("script");o.src=m;const t=a.createElement("script");t.innerHTML=g,l.insertBefore(o,e),l.insertBefore(t,e)}const w=await s.format(c.serialize(),{parser:"html"});e.writeFileSync(h,w),console.log(r.green.bold("TikTok H5 Mini Game initialization has been completed..."))},t}var O=function(){if(r)return s;r=1;const e=c,o=E();return s=function(){e.createPromptModule()([{type:"input",name:"clientKey",message:"Please input client key"},{type:"input",name:"devPort",message:"Please input dev port",default:9527}]).then(async e=>{const{clientKey:t,devPort:n}=e,s={_comment:"orientation is the orientation of the game. It can be either 'VERTICAL' or 'HORIZONTAL'.our game default is VERTICAL; minigame.config.json dev is a configuration file for minigame development. You can use it to configure the minigame.",orientation:"VERTICAL",dev:{port:n}};await o({clientKey:t,config:s}),process.exit(0)}).catch(()=>{process.exit(1)})},s}(),P=_(O);function N(e,o=[]){const t=["--auto-open-devtools-for-tabs",...o];let n;switch(h.platform()){case"darwin":n=`open -n -a "${"Google Chrome"}" --args "${e}" ${t.join(" ")}`;break;case"win32":n=`start "" chrome "${e}" ${t.join(" ")}`;break;default:n=`bash -lc 'command -v google-chrome >/dev/null && google-chrome "${e}" ${t.join(" ")} || chromium "${e}" ${t.join(" ")}'`}console.log("执行命令:",n),f.exec(n,(o,t,n)=>{if(o)return console.log(d.red("启动 Chrome 失败:"),o.message),void console.log(d.yellow("请手动打开 Chrome 并输入 URL"),d(`${e}`));n&&console.warn(n.trim())})}function I(){const e=h.networkInterfaces();for(const o in e){const t=e[o];if(t)for(const e of t)if("IPv4"===e.family&&!e.internal)return e.address}}var C,z,L=$(Object.freeze({__proto__:null,centerQRCode:function(e){const o=process.stdout.columns||80,t=e.split("\n"),n=t.reduce((e,o)=>Math.max(e,o.length),0),s=Math.floor((o-n)/3),r=" ".repeat(s>0?s:0);return t.map(e=>r+e).join("\n")},getDesktopPath:function(){const e=h.homedir(),o=["Desktop","桌面"];for(const t of o){const o=m.join(e,t);if(a.existsSync(o))return o}return m.join(e,"Desktop")},getLocalIP:I,openUrl:N}));var x,U,q,D,F=function(){if(z)return C;z=1;const e=p,o=m,t=a,n=d,s=g,r=e(),{openUrl:i}=L,c=y,{CONFIG_FILE_NAME:l,MINIS_RUNTIME_URL:u}=T();return C=function(){const a=o.join(process.cwd(),l);if(!t.existsSync(a))return void console.log(n.red.bold(`${l} is not exist, please run minis game init first`));const d=JSON.parse(t.readFileSync(a,"utf8")),p=d.dev?.port||9527;console.log(n.yellow.bold("⚠️ Before dev, please ensure:\n 1. The account used to login www.tiktok.com is in the sandbox target user range of Minis developer platform, otherwise login authorization will throw an error.\n 2. The browser allows www.tiktok.com <popup and redirect>, because the authorization login linkage needs to open a new tab popup for operation, otherwise the authorization login linkage will not be able to debug normally.")),console.log(n.bold.blue("\n \n============== start dev your game, it will take a few seconds ============ \n \n")),r.use((e,o,t)=>{e.url.endsWith(".br")?o.setHeader("Content-Encoding","br"):e.url.endsWith(".gz")&&o.setHeader("Content-Encoding","gzip"),t()}),r.use((e,r,i)=>{try{const e=o.join(process.cwd(),"index.html"),n=t.readFileSync(e,"utf8"),i=s.load(n),a=[];i("script:not([src])").each((e,o)=>{const t=i(o).html();t&&t.trim()&&a.push(t)});a.map(e=>`'sha256-${c.createHash("sha256").update(e,"utf8").digest("base64")}'`);const l="*";r.setHeader("Content-Security-Policy",`default-src 'self';script-src 'self' data: blob: 'unsafe-eval' 'unsafe-inline' connect.tiktok-minis.com sf-connect.tiktokminis.us;img-src 'self' ${l} data: blob: *; connect-src 'self' ${l} data: blob: ; style-src 'self' ${l} 'unsafe-inline' fonts.googleapis.com data: blob: *; font-src 'self' fonts.gstatic.com blob: data: *; media-src 'self' ${l} data: blob: *; frame-src 'none'; base-uri 'self'; worker-src 'self' blob: data: ;`)}catch(e){console.warn(n.red("Failed to set CSP header:"),e.message)}i()}),r.use(e.static(o.join(process.cwd()))),r.listen(p,()=>{const e=`${u}?minis_url=${`http://localhost:${p}`}&enable_log=1`;console.log(`you can access ${n.green.underline.bold(e)} to debug your game in browser...`);try{i(e)}catch(e){console.warn(n.red("Failed to open browser, you can access it manually"),e.message)}})}}(),H=_(F);function G(){if(U)return x;U=1;const e=d,o=a,t=m,{MINIS_MANIFEST_FILE_NAME:n}=T();function s(e,o){let t=o.find(o=>"folder"===o.type&&o.name===e[0]);return t||(t={type:"folder",name:e[0],children:[]},o.push(t)),e.length>1?s(e.slice(1),t.children):t}function r(e,n=e){const s={};return o.readdirSync(e).forEach(i=>{const c=t.join(e,i);if(o.statSync(c).isDirectory()){const e=r(c,n);Object.assign(s,e)}else if(""!==t.extname(i)&&i){const e="/"+t.relative(n,c).replace(/\\/g,"/");s[c]=e}}),s}return x={buildMinisManifest:async function(){try{const e=t.join(process.cwd()),i=[],c=r(e);Object.keys(c).filter(e=>!e.endsWith(".map")).forEach(e=>{const o=c[e].split("/").filter(Boolean),t=o.pop()||"";if(0===o.length)i.push({type:"file",name:t});else{s(o,i).children.push({type:"file",name:t})}}),o.writeFileSync(t.join(e,n),JSON.stringify({name:n,resource_list:i},null,2))}catch(o){console.error(e.red(`Error during debug process: ${o.message}`)),o instanceof Error&&o.stack&&console.error(e.red(`Stack trace: ${o.stack}`)),process.exit(1)}}}}var R=_(function(){if(D)return q;D=1;const e=a,o=h,t=m,n=d,s=b,r=c.createPromptModule(),{buildMinisManifest:i}=G();return q=async function(){try{const{zipName:c}=await r({type:"input",name:"zipName",default:"game",message:"Please input zip name"}),a=Date.now();console.log(n.bold.blue("start build your game, it will take a few minutes..."));e.readdirSync(process.cwd()).forEach(o=>{o.endsWith(".zip")&&e.unlinkSync(t.join(process.cwd(),o))}),await i();const l=s("zip",{zlib:{level:9}});l.on("error",function(e){throw e});const u=function(){const n=o.homedir(),s=["Desktop","桌面"];for(const o of s){const s=t.join(n,o);if(e.existsSync(s))return s}const r=t.join(n,"Desktop");return console.log(`未找到特定桌面文件夹,使用默认路径: ${r}`),r}(),d=t.join(u,`${c}.zip`);await l.pipe(e.createWriteStream(d)),await l.directory(t.resolve(process.cwd()),!1),await l.finalize(),console.log(n.yellow.bold(`build ${c}.zip success, you can find it in desktop, use time ${Date.now()-a} ms`)),process.exit(0)}catch(e){console.log(n.red(`auto build ${zipNameInput}.zip failed: ${e.message}, you should zip it manually`)),process.exit(1)}}}());class A{constructor(e){this.listeners=[],this.state=e}static getInstance(e){return A.instance||(A.instance=new A(e)),A.instance}getState(){return this.state}setState(e){this.state=Object.assign(Object.assign({},this.state),e),this.listeners.forEach(e=>e(this.state))}subscribe(e){return this.listeners.push(e),()=>{this.listeners=this.listeners.filter(o=>o!==e)}}reset(e){this.state=e,this.listeners.forEach(e=>e(this.state))}}const W=A.getInstance({clientServerPort:"",clientServerHost:"",clientKey:"",packages:{}}),B=9529,V=m.join(h.homedir(),"__TTMG__");function K({clientWsPort:e}){const o={ws_port:B,nodeWsPort:B,clientWsPort:e};return btoa(JSON.stringify(o))}let J;function Q(){J.close(()=>{console.log("Dev server closed"),J=null,process.exit(0)})}const Y=m.join(h.homedir(),".ttmg-cli");function Z(){const e=m.join(process.cwd(),"project.config.json");let o;try{const t=JSON.parse(a.readFileSync(e,"utf-8"));o=t.appid||t.appId}catch(e){o=""}if(o)return o;console.log(d.red.bold("No appid found in project.config.json, you should provide it in project.config.json")),process.exit(1)}function X(){const e=Z();return m.join(h.homedir(),"__TTMG__",e)}async function ee(){const e=X();console.log(d.yellow.bold("Start compress game resource"));const o=m.join(h.homedir(),"__TTMG__","upload.zip");var t,n;await(t=e,n=o,new Promise((e,o)=>{const s=a.createWriteStream(n),r=b("zip",{zlib:{level:9}});s.on("close",()=>{e()}),s.on("error",e=>{o(e)}),r.on("error",e=>{o(e)}),r.pipe(s),r.directory(t,!1),r.finalize()})),console.log(d.green.bold("Compress game package resource success \n"));const s=await async function(e){const o=new k;o.append("file",a.createReadStream(e),"upload.zip");try{console.log(d.yellow.bold("Start upload resource to client"));const{clientServerHost:t,clientServerPort:n}=W.getState(),s=await v.post(`http://${t}:${n}/game/upload`,o,{headers:o.getHeaders()});return a.unlinkSync(e),s}catch(e){console.error("Upload resource to client failed:",e.message)}}(o);return{isSuccess:200===(null==s?void 0:s.status)}}m.join(Y,"config.json");const oe=new class{constructor(){this.ws=new S.Server({port:B}),this.ws.on("connection",e=>{e.on("message",e=>{const o=JSON.parse(e.toString());console.log("Client Message",o);if("browser"===o.from){switch(o.method){case"connected":this.sendUploadStatus("start"),ee().then(e=>{e.isSuccess?(this.sendUploadStatus("success",{packages:W.getState().packages,isSuccess:e.isSuccess}),console.log(d.green.bold("Upload resource to client successfully!"))):(this.sendUploadStatus("error",{errMsg:e.errorMsg}),console.log(d.red.bold("Upload resource to client failed!")))}).catch(()=>{this.sendUploadStatus("error",{isSuccess:!1}),console.log(d.red.bold("Start upload resource to client failed!"))});break;case"closeLocalDebug":console.log("closeLocalDebug"),this.ws.close(),Q(),console.log("close server")}}else{switch(o.method){case"shareDevParams":console.log("shareDevParams",o);const e=o.payload;console.log("shareDevParams",e);const{host:t,port:n,wsPort:s}=e;W.setState({clientServerPort:n,clientServerHost:t});const r=`http://${t}:${n}?session=${K({clientWsPort:s})}`;N(r),console.log(d.bold.yellow(`Game debug is ready! Visit ${r} in your browser.`));break;case"checkPermissionFailed":console.log(d.red.bold("Check permission failed! Please authorize in client first."))}}})})}send(e){this.ws.clients.forEach(o=>{if(o.readyState===S.OPEN){const t=Object.assign(Object.assign({},e),{from:"nodeServer"});o.send(JSON.stringify(t))}})}close(){this.ws.close()}sendUploadStatus(e,o){this.send({method:"uploadStatus",status:e,payload:o})}};async function te(){await async function(){console.log(d.yellow.bold("Start compile game for debug"));const e=process.cwd(),o=X();a.existsSync(o)||a.mkdirSync(o,{recursive:!0});const{isSuccess:t,errorMsg:n,packages:s}=await M.debugPkgs({entry:e,output:o,rules:{mainPackageSizeLimit:1048576e3,projectSizeLimit:1048576e3}});t?(W.setState({packages:s}),console.log(d.green.bold("Compile game package success \n"))):(console.log(d.redBright("Build game package failed, Please check the error message below:")),console.log(d.redBright(n)),process.exit(1))}(),await async function(){J&&Q();const e=p(),o=9528,t=w({dest:h.tmpdir()});return e.use(p.json()),e.use(p.urlencoded({extended:!0})),e.post("/game/env",async(e,o)=>{const t=e.body,{host:n,port:s,wsPort:r}=t;W.setState({clientServerPort:s,clientServerHost:n});const i=`http://${n}:${s}?session=${K({clientWsPort:r})}`;N(i),console.log(d.bold.yellow(`Game debug is ready! Visit ${i} in your browser.`)),o.json({code:0,msg:"ok",data:{devUrl:i}})}),e.post("/game/upload",t.single("file"),async(e,o)=>{o.json({code:0,msg:"ok",filename:e.file.filename,originalname:e.file.originalname,size:e.file.size,path:e.file.path})}),J=e.listen(o,()=>{console.log(d.cyan.bold("Node devServer is running on port 9528"))}),{port:o,host:I()}}(),await async function(){const e=Z(),o=m.join(V,e),t=I();console.log(d.green.bold("Tips:")),console.log(` 1. ${d.yellow.bold("Scan the QR code to start the client devServer.")}`),console.log(` 2. ${d.yellow.bold("Will auto upload compiled resource to client.")} ${d.bold(o)}`),console.log(` 3. ${d.yellow.bold("Debug your game in the browser.")}\n`);const n=`https://www.tiktok.com/ttmg/dev/${e}?host=${t}&port=9529`;j.generate(n,{small:!0},e=>{console.log(e)})}(),await async function(){let e=null;a.watch(process.cwd(),(o,t)=>{console.log(d.yellow("game resource change, restart to upload")),e&&clearTimeout(e),e=setTimeout(async()=>{oe.sendUploadStatus("start"),ee().then(e=>{e.isSuccess?oe.sendUploadStatus("success"):oe.sendUploadStatus("error",{errMsg:e.errorMsg})}).catch(()=>{oe.sendUploadStatus("error")}),e=null},500)})}()}var ne="0.1.0-beta.6";const se=new i.Command;se.name("ttmg").description("TikTok Mini Games Command Line Tool").version(ne,"-v, --version","显示版本号").option("dev","Debug TikTok Mini Games for Client").option("dev --h5","Debug TikTok Mini Games for Web"),se.option("--h5","H5 Mini Game").command("init").description("Initialize project").action(()=>{se.opts().h5?P():console.log("Native Mini Game initialize")}),se.option("--h5","H5 Mini Game").command("dev").description("Open browser dev environment").action(()=>{se.opts().h5?H():te()}),se.option("--h5","H5 Mini Game").command("build").description("Bundle project").action(()=>{se.opts().h5?R():console.log("Native Mini Game bundle")}),se.command("login").description("User Dev Portal Account to Login").action(async()=>{console.log("will support soon")}),se.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttmg/cli",
3
- "version": "0.1.0-beta.5",
3
+ "version": "0.1.0-beta.6",
4
4
  "description": "TikTok Mini Game Command Line Tool",
5
5
  "license": "ISC",
6
6
  "bin": {