tyhuynh-laya-cmd 1.0.0 → 1.0.1

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.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ "use strict";const path=require("path"),fs=require("fs-extra"),args=process.argv.slice(2),dryRun=args.includes("--dry-run"),pIdx=args.indexOf("-p"),layaDir=path.resolve(-1!==pIdx?args[pIdx+1]:"../../client/laya"),assetsDir=path.join(layaDir,"assets"),binDir=path.join(layaDir,"../bin"),stylesXml=path.join(layaDir,"styles.xml");console.log("\n🔍 Migrate stray assets"+(dryRun?" [DRY RUN]":"")),console.log(` laya : ${layaDir}`),console.log(` assets : ${assetsDir}`),console.log(` bin : ${binDir}\n`);const IMAGE_EXTS=new Set([".png",".jpg",".jpeg",".webp"]);function collectImages(e){const s=[];return function e(t){let n;try{n=fs.readdirSync(t,{withFileTypes:!0})}catch{return}for(const o of n){const n=path.join(t,o.name);o.isDirectory()?e(n):IMAGE_EXTS.has(path.extname(o.name).toLowerCase())&&s.push(n)}}(e),s}const binResDir=path.join(binDir,"res","image");fs.existsSync(binResDir)||(console.error(`❌ bin/res/image not found: ${binResDir}`),process.exit(1));const binImages=collectImages(binResDir),strayFiles=[],atlasBasenamesByDir=new Map;function collectAtlasInfo(e){let s;try{s=fs.readdirSync(e,{withFileTypes:!0})}catch{return}for(const t of s){const s=path.join(e,t.name);if(t.isDirectory())collectAtlasInfo(s);else if(".atlas"===path.extname(t.name).toLowerCase()){const s=path.basename(t.name,".atlas");atlasBasenamesByDir.has(e)||atlasBasenamesByDir.set(e,new Set),atlasBasenamesByDir.get(e).add(s)}}}function isAtlasPage(e){const s=path.dirname(e),t=atlasBasenamesByDir.get(s);if(!t)return!1;const n=path.basename(e,path.extname(e));for(const e of t){if(n===e)return!0;if(n.startsWith(e)&&/^\d+$/.test(n.slice(e.length)))return!0}return!1}collectAtlasInfo(binDir);for(const e of binImages){const s=path.relative(binDir,e).replace(/\\/g,"/"),t=path.join(assetsDir,s);fs.existsSync(t)||(isAtlasPage(e)||strayFiles.push({rel:s,binAbs:e,assetsAbs:t}))}0===strayFiles.length&&(console.log("✅ No stray files found — everything is already in assets/"),process.exit(0)),console.log(`Found ${strayFiles.length} stray file(s):\n`);for(const{rel:e}of strayFiles)console.log(` + ${e}`);console.log("");const dirGroups=new Map,preExistingAssetDirs=new Set;for(const{rel:e}of strayFiles){const s=e.substring(0,e.lastIndexOf("/"));dirGroups.has(s)||dirGroups.set(s,[]),dirGroups.get(s).push(e),fs.existsSync(path.join(assetsDir,s))&&preExistingAssetDirs.add(s)}if(dryRun)console.log("📦 (Dry run) Skipping moving files...");else{console.log("📦 Moving files to laya/assets/ ...");for(const{rel:e,binAbs:s,assetsAbs:t}of strayFiles)fs.ensureDirSync(path.dirname(t)),fs.copyFileSync(s,t),console.log(` ✓ ${e}`)}console.log("\n📝 "+(dryRun?"(Dry run preview) ":"")+'Updating styles.xml with pack="2" entries ...'),fs.existsSync(stylesXml)||(console.error(`❌ styles.xml not found: ${stylesXml}`),process.exit(1));let xmlContent=fs.readFileSync(stylesXml,"utf-8");const existingNames=new Set;for(const e of xmlContent.matchAll(/name="([^"]+)"/g))existingNames.add(e[1]);const newItems=[];function makeItem(e){return` <item name="${e}" type="Sprite" compress="0" pack="2" quality="80" props="" picType="0" scale="0"/>`}for(const[e,s]of dirGroups){if(!preExistingAssetDirs.has(e))existingNames.has(e)?console.log(` ℹ Skipped dir (already in styles.xml): ${e}`):(newItems.push(makeItem(e)),console.log(` + ${e}/ (new directory → dir rule)`));else for(const e of s)existingNames.has(e)?console.log(` ℹ Skipped (already in styles.xml): ${e}`):(newItems.push(makeItem(e)),console.log(` + ${e}`))}if(newItems.length>0){const e=xmlContent.lastIndexOf("</res>");-1===e&&(console.error("❌ Could not find </res> in styles.xml"),process.exit(1));const s=newItems.join("\n")+"\n";xmlContent=xmlContent.slice(0,e)+s+xmlContent.slice(e),dryRun&&(console.log(`\n✅ (Dry run preview) styles.xml WOULD BE updated with ${newItems.length} new entries.`),process.exit(0)),fs.writeFileSync(stylesXml,xmlContent,"utf-8"),console.log(`\n✅ styles.xml updated with ${newItems.length} new entries.`)}else console.log("\n✅ styles.xml already up-to-date (all entries existed)."),dryRun&&process.exit(0);console.log("\nDone! Next steps:\n 1. Run: npm run build:clean\n → AtlasGen will copy the new assets to bin/ going forward\n 2. Verify the game still works (files are already in bin/)\n 3. Commit laya/assets/ and styles.xml to version control\n");
@@ -0,0 +1 @@
1
+ "use strict";const fs=require("fs-extra"),path=require("path"),CACHE_VERSION=1;class BuildCache{constructor(t){this._path=t,this._data={version:1,entries:{}},this._dirty=!1,this._load()}_load(){try{const t=fs.readFileSync(this._path,"utf-8"),s=JSON.parse(t);1===s.version&&(this._data=s)}catch{}}needsRebuild(t,s){const e=this._data.entries[t];if(!e)return!0;let i=0;try{i=fs.statSync(t).mtimeMs}catch{return!0}if(i!==e.srcMtime)return!0;let r=0;try{r=fs.statSync(s).mtimeMs}catch{return!0}return r!==e.outMtime}recordBuild(t,s){let e=0,i=0;try{e=fs.statSync(t).mtimeMs}catch{}try{i=fs.statSync(s).mtimeMs}catch{}this._data.entries[t]={srcMtime:e,outMtime:i},this._dirty=!0}prune(){for(const t of Object.keys(this._data.entries))fs.existsSync(t)||(delete this._data.entries[t],this._dirty=!0)}save(){this._dirty&&(fs.ensureDirSync(path.dirname(this._path)),fs.writeFileSync(this._path,JSON.stringify(this._data,null,2),"utf-8"),this._dirty=!1)}clear(){this._data.entries={},this._dirty=!0,this.save()}}module.exports={BuildCache:BuildCache};
@@ -0,0 +1 @@
1
+ "use strict";const{XMLParser:XMLParser}=require("fast-xml-parser"),fs=require("fs-extra"),path=require("path"),parser=new XMLParser({ignoreAttributes:!1,attributeNamePrefix:"_",allowBooleanAttributes:!0,trimValues:!0,parseTagValue:!0});function loadProjectConfig(e){const t=path.join(e,".laya");if(!fs.existsSync(t))throw new Error(`Không tìm thấy .laya config tại: ${t}`);const r=fs.readFileSync(t,"utf-8"),i=parser.parse(r).project||{},o=(e,t)=>{const r=i[e];return void 0!==r?r:t},a=o("boxTypes","Sprite,Box,List,Tab,RadioGroup,ViewStack,Panel,HBox,VBox,Tree"),s=o("pageTypes","View,Scene");return{projectDir:e,codeExportPath:String(o("codeExportPath","src/ui")).trim(),uiExportPath:String(o("uiExportPath","bin")).trim(),resExportPath:String(o("resExportPath","bin/")).trim(),codeType:Number(o("codeType",1)),uiType:Number(o("uiType",2)),designWidth:Number(o("designWidth",720)),designHeight:Number(o("designHeight",1280)),boxTypes:String(a).split(",").map(e=>e.trim()),pageTypes:String(s).split(",").map(e=>e.trim()),toJsonScene:!0===o("toJsonScene",!0)||"true"===o("toJsonScene","true"),scaleMode:String(o("scaleMode","fixedwidth")).trim(),picWidth:Number(o("picWidth",512)),picHeight:Number(o("picHeight",512)),textureWidth:Number(o("textureWidth",1024)),textureHeight:Number(o("textureHeight",1024)),scaleMode:String(o("scaleMode","fixedwidth")).trim()}}module.exports={loadProjectConfig:loadProjectConfig};
@@ -0,0 +1 @@
1
+ "use strict";const path=require("path"),fs=require("fs-extra"),{getMtime:getMtime,ensureDir:ensureDir,collectFiles:collectFiles,writeIfChanged:writeIfChanged}=require("../utils/FileUtils"),{resolveStyle:resolveStyle}=require("../parsers/StylesParser"),{MaxRectsPacker:MaxRectsPacker}=require("maxrects-packer");function packSpritesMultiPage(e,t,a){const n=new MaxRectsPacker(t,a,1,{smart:!0,pot:!1,square:!1}),s=e.map(e=>({width:e.w,height:e.h,data:e}));n.addArray(s);return n.bins.map(e=>e.rects.map(e=>{const t=e.data;return{name:t.name,path:t.path,w:t.w,h:t.h,x:e.x,y:e.y,sourceW:t.sourceW,sourceH:t.sourceH,offX:t.offX,offY:t.offY,buffer:t.buffer}}))}function buildAtlasJson(e,t,a){const n={},s=[];return e.forEach((e,t)=>{const r=0===t?`${a}.png`:`${a}${t}.png`;s.push(r);for(const a of e){const e=path.basename(a.name);n[e]={frame:{x:a.x,y:a.y,w:a.w,h:a.h,idx:t},sourceSize:{w:a.sourceW,h:a.sourceH},spriteSourceSize:{x:a.offX,y:a.offY}}}}),{frames:n,meta:{image:s.join(","),prefix:t+"/"}}}async function generateAtlas(e){const{styleMap:t,projectConfig:a,assetDir:n,outputDir:s,cachePath:r,clear:i=!1,changedDirs:o=null,log:c=console.log}=e;let l;try{l=require("sharp")}catch{return void c("[AtlasGen] ⚠ sharp not installed — skipping atlas generation")}ensureDir(s);const h=a.textureWidth||1024,f=a.textureHeight||1024,p=a.picWidth||512,m=a.picHeight||512;let u={};if(!i)try{u=fs.readJsonSync(r)}catch{}const g=collectFiles(n,[".png",".jpg"]);if(i){let A=0;function I(e){if(!fs.existsSync(e))return;let t;try{t=fs.readdirSync(e,{withFileTypes:!0})}catch{return}for(const a of t){const t=path.join(e,a.name);if(a.isDirectory()){I(t);try{fs.rmdirSync(t)}catch{}}else if(".json"!==path.extname(a.name).toLowerCase())try{fs.unlinkSync(t),A++}catch{}}}I(path.join(s,"res","image")),A>0&&c(`[AtlasGen] 🧹 Cleaned ${A} non-JSON file(s) from bin/res/image/`)}const d=new Map,y=[],w=await Promise.all(g.map(async e=>{const a=path.relative(n,e).replace(/\\/g,"/"),s=path.dirname(a);let r=resolveStyle(t,a),i=!1,o=!1;if(r&&void 0!==r.pack&&0!=r.pack)2==r.pack?(o=!0,i=!1):i=!0;else try{const t=await l(e).metadata();i=t.width<=p&&t.height<=m}catch{}return{relName:a,parentDir:s,absolutePath:e,shouldPack:i,isUnpack:o}}));for(const{relName:P,parentDir:j,absolutePath:D,shouldPack:F,isUnpack:G}of w){d.has(j)||d.set(j,{packItems:[],rawItems:[]});const W=d.get(j);F?W.packItems.push({name:P,path:D}):W.rawItems.push({name:P,path:D}),G&&y.push(P)}c(`[AtlasGen] Found ${d.size} directories containing images`);async function k(e,t){for(const e of t.rawItems){const t=path.join(s,e.name),a=getMtime(e.path),n=`raw_${e.name}`;(i||!u[n]||u[n]!==a||!fs.existsSync(t))&&(fs.ensureDirSync(path.dirname(t)),fs.copyFileSync(e.path,t),u[n]=a)}if(0===t.packItems.length)return;const a=path.basename(e),n=path.join(s,e),r=`${n}.atlas`;let p=i||!u[e];const m={};if(o&&o.has(e)&&(p=!0),p)for(const e of t.packItems)m[e.name]=getMtime(e.path);else{const a=u[e].mtimes||{};for(const e of t.packItems){const t=getMtime(e.path);m[e.name]=t,t!==a[e.name]&&(p=!0)}fs.existsSync(r)||(p=!0)}if(!p)return;c(`[AtlasGen] Building atlas for ${e} (${t.packItems.length} sprites)`);const g=t.packItems.map(async e=>{try{const t=l(e.path),a=await t.metadata();if(a.width>h||a.height>f)return c(`[AtlasGen] ⚠ ${e.name} is too large for atlas (${a.width}x${a.height}), skipping`),null;let n,s;try{const e=await t.clone().trim().toBuffer({resolveWithObject:!0});n=e.data,s=e.info}catch(e){const a=await t.clone().toBuffer({resolveWithObject:!0});n=a.data,s=a.info}return{name:e.name,path:e.path,w:s.width,h:s.height,sourceW:a.width,sourceH:a.height,offX:-(s.trimOffsetLeft||0),offY:-(s.trimOffsetTop||0),buffer:n}}catch(t){return c(`[AtlasGen] ✗ Cannot process ${e.name} at ${e.path}: ${t.message}`),null}}),d=await Promise.all(g),y=[];for(const e of d)e&&y.push(e);if(0===y.length)return;const w=packSpritesMultiPage(y,h,f);fs.ensureDirSync(path.dirname(n));for(let e=0;e<w.length;e++){const t=0===e?`${a}.png`:`${a}${e}.png`,s=`${n}${0===e?"":e}.png`,r=w[e],i=Math.min(h,Math.max(...r.map(e=>e.x+e.w))),o=Math.min(f,Math.max(...r.map(e=>e.y+e.h))),p=r.map(e=>({input:e.buffer,top:e.y,left:e.x}));try{await l({create:{width:i,height:o,channels:4,background:{r:0,g:0,b:0,alpha:0}}}).composite(p).png().toFile(s)}catch(e){c(`[AtlasGen] ✗ Failed to render ${t}: ${e.message}`)}}const k=buildAtlasJson(w,e,a);fs.writeFileSync(r,JSON.stringify(k,null,2),"utf-8"),u[e]={mtimes:m}}const S=[...d.entries()];let x=0;const $=Array.from({length:Math.min(8,S.length)},async function(){for(;x<S.length;){const[e,t]=S[x++];await k(e,t)}});await Promise.all($),fs.ensureDirSync(path.dirname(r)),writeIfChanged(r,JSON.stringify(u,null,2));const M=path.join(s,"unpack.json"),b=JSON.stringify(y,null,4);writeIfChanged(M,b)&&c(`[AtlasGen] Written unpack.json with ${y.length} items`),c("[AtlasGen] Done processing all atlases and raw images.")}module.exports={generateAtlas:generateAtlas};
@@ -0,0 +1 @@
1
+ "use strict";const path=require("path"),fs=require("fs-extra"),{writeIfChanged:writeIfChanged}=require("../utils/FileUtils"),{resolveStyle:resolveStyle}=require("../parsers/StylesParser"),EDITOR_KEYS=["searchKey","nodeParent","maxID","label","isOpen","isDirectory","isAniNode","hasChild","selectedBox","switchAble","removeAble","$HIDDEN","$LOCKED","selecteID","x","y"],ASSET_REF_RE=/\"([^\"]+\.(?:png|jpg|jpeg|sk|ani))\"/gi;function stripEditorKeys(e,s){if(e&&"object"==typeof e){for(const s of EDITOR_KEYS)delete e[s];if("Script"===e.type&&e.source){e.props||(e.props={});let s=e.source;s.startsWith("src/")&&(s=s.substring(4)),e.props.runtime=s,delete e.source}if(s&&e.props&&e.props.skin){const t=resolveStyle(s,e.props.skin);t&&t.propsMap&&(e.props=Object.assign({},t.propsMap,e.props))}if("Button"===e.type&&e.props&&void 0===e.props.stateNum&&(e.props.stateNum=1),"UIView"===e.type&&e.source){const s=path.basename(e.source,".scene");if(e.type=s,e.props||(e.props={}),e.props.runtime)e.props.runtime.startsWith("src/")&&(e.props.runtime=e.props.runtime.substring(4));else{const t="ui."+e.source.substring(0,e.source.lastIndexOf("/")).replace(/\\/g,"/").replace(/\//g,".");e.props.runtime=`${t}.${s}UI`}delete e.source}if(Array.isArray(e.child))if(0===e.child.length)delete e.child;else for(const t of e.child)stripEditorKeys(t,s)}}function buildLoadList(e){const s=JSON.stringify(e),t={};let n;for(ASSET_REF_RE.lastIndex=0;null!==(n=ASSET_REF_RE.exec(s));)t[n[1]]=!0;return Object.keys(t)}function buildSceneJson(e,s){const t=JSON.parse(JSON.stringify(e));return stripEditorKeys(t,s),t.loadList=buildLoadList(t),t.loadList3D=[],t}async function exportSceneJsonFiles(e){const{resExportPath:s,scenes:t,separatedScenes:n=new Set,styleMap:r,log:i=console.log}=e,o={};let p=0;for(const{relPath:e,parsed:c}of t){const t=buildSceneJson(c.rawJson,r),l=e.replace(/\.scene$/i,".json"),a=path.join(s,l);if(n.has(e))o[l]=t,fs.existsSync(a)&&(fs.unlinkSync(a),i(`[SceneJsonGen] ✗ Removed stale standalone: ${l}`));else{fs.ensureDirSync(path.dirname(a));writeIfChanged(a,JSON.stringify(t))&&i(`[SceneJsonGen] ✓ Written: ${l}`),p++}}const c=path.join(s,"ui.json");return Object.keys(o).length>0?(fs.ensureDirSync(path.dirname(c)),writeIfChanged(c,JSON.stringify(o)),i(`[SceneJsonGen] ✓ Bundled ${Object.keys(o).length} scenes → ui.json`)):fs.existsSync(c)&&(fs.unlinkSync(c),i("[SceneJsonGen] ✓ Deleted empty ui.json")),{bundled:Object.keys(o).length,standalone:p}}module.exports={stripEditorKeys:stripEditorKeys,buildLoadList:buildLoadList,buildSceneJson:buildSceneJson,exportSceneJsonFiles:exportSceneJsonFiles};
@@ -0,0 +1 @@
1
+ "use strict";const path=require("path"),fs=require("fs-extra"),{parseScene:parseScene}=require("../parsers/SceneParser"),{getMtime:getMtime,writeIfChanged:writeIfChanged,collectFiles:collectFiles}=require("../utils/FileUtils"),{BuildCache:BuildCache}=require("../BuildCache"),{loadPageStyles:loadPageStyles}=require("../parsers/PageParser"),{loadStyles:loadStyles}=require("../parsers/StylesParser"),{exportSceneJsonFiles:exportSceneJsonFiles}=require("./SceneJsonGen"),TYPE_MAP={View:"Laya.View",Scene:"Laya.Scene",Dialog:"Laya.Dialog",Sprite:"Laya.Sprite",Box:"Laya.Box",Image:"Laya.Image",Button:"Laya.Button",CheckBox:"Laya.CheckBox",Radio:"Laya.Radio",RadioGroup:"Laya.RadioGroup",Label:"Laya.Label",Text:"Laya.Text",TextInput:"Laya.TextInput",TextArea:"Laya.TextArea",HtmlElement:"Laya.HTMLDivElement",HTMLDivElement:"Laya.HTMLDivElement",List:"Laya.List",Tree:"Laya.Tree",Tab:"Laya.Tab",ViewStack:"Laya.ViewStack",Panel:"Laya.Panel",HBox:"Laya.HBox",VBox:"Laya.VBox",FontClip:"Laya.FontClip",Clip:"Laya.Clip",ProgressBar:"Laya.ProgressBar",Slider:"Laya.Slider",ScrollBar:"Laya.ScrollBar",ComboBox:"Laya.ComboBox",ColorPicker:"Laya.ColorPicker"};function mapType(e){return TYPE_MAP[e]||"any"}function scenePathToModuleName(e){return"ui."+e.replace(/\.scene$/i,"").replace(/\\/g,"/").split("/").slice(0,-1).join(".")}function scenePathToClassName(e){const a=e.replace(/\.scene$/i,"").replace(/\\/g,"/").split("/");return a[a.length-1]+"UI"}function scenePathToLoadPath(e){return e.replace(/\.scene$/i,"").replace(/\\/g,"/")}function generateSceneCode(e,a){const t=scenePathToModuleName(e),n=scenePathToClassName(e),o=scenePathToLoadPath(e),s=a.rootType||"View",r=a.vars.map(e=>{const a=mapType(e.type);return`\t\tpublic ${e.varName}:${a};`}).join("\n");return`export module ${t} {\n export class ${n} extends ${s} {\n${r?r+"\n":""} constructor(){ super()}\n createChildren():void {\n super.createChildren();\n this.loadScene("${o}");\n }\n }\n REG("${t}.${n}",${n});\n}`}const FILE_HEADER="/**This class is automatically generated by LayaAirIDE, please do not make any modifications. */\nimport View=Laya.View;\r\nimport Scene=Laya.Scene;\nvar REG: Function = Laya.ClassUtils.regClass;";async function generateUICode(e){const{pagesDir:a,outputPath:t,cachePath:n,resExportPath:o,clear:s=!1,changedScenes:r=null,log:l=console.log}=e,c=new BuildCache(n);s&&c.clear(),c.prune();let i=e.sceneFiles||collectFiles(a,".scene");try{const e=path.join(a,"../ignore.cfg");if(fs.existsSync(e)){const t=JSON.parse(fs.readFileSync(e,"utf8"));i=i.filter(e=>{const n="laya/pages/"+path.relative(a,e).replace(/\\/g,"/");return!t[n]})}}catch(e){l(`[UICodeGen] ⚠ Could not parse ignore.cfg: ${e.message}`)}l(`[UICodeGen] 找到 ${i.length} scene files`);const p=path.join(a,"../pageStyles.xml"),d=new Set;try{const e=loadPageStyles(p);for(const a of e)"分离模式"===a.ifExport&&d.add(a.name.replace(/\\/g,"/"))}catch(e){l(`[UICodeGen] ⚠ Could not load pageStyles.xml: ${e.message}`)}const h=path.join(a,"../styles.xml");let u;try{u=loadStyles(h)}catch(e){l(`[UICodeGen] ⚠ Could not load styles.xml: ${e.message}`)}let g=!1;const y=new Map;for(const e of i){const a=getMtime(e);y.set(e,a);const t=c._data.entries[e];t&&t.srcMtime===a||(g=!0)}0===getMtime(t)&&(g=!0);const m=path.join(a,"../ignore.cfg"),P=getMtime(p),C=getMtime(m);if(c._data.pageStylesMtime===P&&c._data.ignoreMtime===C||(g=!0),!g)return l("[UICodeGen] ✓ No changes detected — skipping code generation"),{built:0,skipped:i.length,total:i.length};const L=new Map,S=[];let T=0;for(const e of i){const t=path.relative(a,e).replace(/\\/g,"/"),n=scenePathToModuleName(t);let o;try{o=parseScene(e)}catch(e){l(`[UICodeGen] ✗ Parse error: ${t} — ${e.message}`),T++;continue}L.has(n)||L.set(n,[]),L.get(n).push({relPath:t,absPath:e,parsed:o}),S.push({relPath:t,absPath:e,parsed:o})}if(o){const e=r?S.filter(({absPath:e})=>r.has(e)):S;e.length>0&&await exportSceneJsonFiles({resExportPath:o,scenes:e,separatedScenes:d,styleMap:u,log:l})}const f=[...L.entries()].sort((e,a)=>e[0].localeCompare(a[0])),x=[FILE_HEADER];for(const[,e]of f){e.sort((e,a)=>{const t=scenePathToClassName(e.relPath),n=scenePathToClassName(a.relPath);return t.localeCompare(n)});const a=scenePathToModuleName(e[0].relPath),t=[];for(const{relPath:n,parsed:o}of e){const e=scenePathToClassName(n),s=scenePathToLoadPath(n),r=o.rootType||"View",l=o.vars.map(e=>`\t\tpublic ${e.varName}:${mapType(e.type)};`).join("\n"),c=l?l+"\n":"";t.push(` export class ${e} extends ${r} {\n${c} constructor(){ super()}\n createChildren():void {\n super.createChildren();\n this.loadScene("${s}");\n }\n }\n REG("${a}.${e}",${e});`)}x.push(`export module ${a} {\n${t.join("\n")}\n}`)}const $=x.join("\n");l(writeIfChanged(t,$)?`[UICodeGen] ✓ Written: ${t}`:"[UICodeGen] ✓ No content change in output");const M=getMtime(t);for(const[e,a]of y)c._data.entries[e]={srcMtime:a,outMtime:M};c._data.pageStylesMtime=getMtime(p);const I=path.join(a,"../ignore.cfg");c._data.ignoreMtime=getMtime(I),c._dirty=!0,c.save();const B=i.length-T;return l(`[UICodeGen] Done: ${B} processed, ${T} errors`),{built:B,skipped:0,total:i.length}}module.exports={generateUICode:generateUICode,scenePathToModuleName:scenePathToModuleName,scenePathToClassName:scenePathToClassName,mapType:mapType};
@@ -0,0 +1 @@
1
+ "use strict";const{parseXmlFile:parseXmlFile}=require("../utils/XmlUtils"),{getMtime:getMtime}=require("../utils/FileUtils"),EXPORT_MODE_FILE="文件模式",EXPORT_MODE_SEPARATE="分离模式";let _cache=null;function loadPageStyles(e){const a=getMtime(e);if(_cache&&_cache.mtime===a&&_cache.path===e)return _cache.data;const t=parseXmlFile(e),i=t?.page?.item,c=[];if(!i)return _cache={mtime:a,path:e,data:c},c;const l=Array.isArray(i)?i:[i];for(const e of l)e.name&&c.push({name:e.name,ifExport:e.ifExport||"文件模式",aName:e.aName||"",props:e.props||""});return _cache={mtime:a,path:e,data:c},c}function invalidateCache(){_cache=null}module.exports={loadPageStyles:loadPageStyles,invalidateCache:invalidateCache,EXPORT_MODE_FILE:"文件模式",EXPORT_MODE_SEPARATE:"分离模式"};
@@ -0,0 +1 @@
1
+ "use strict";const fs=require("fs-extra");function _collectVars(r,e){if(!r)return;const t=r.props&&r.props.var;if(t&&"string"==typeof t&&t.trim()&&e.push({varName:t.trim(),type:r.type||"Sprite"}),Array.isArray(r.child))for(const t of r.child)_collectVars(t,e)}function parseScene(r){const e=fs.readFileSync(r,"utf-8");let t;try{t=JSON.parse(e)}catch(e){throw new Error(`JSON parse error in ${r}: ${e.message}`)}const s=t.type||"View",o=t.props||{},a=[];_collectVars(t,a);const c=new Set;return{rootType:s,rootProps:o,vars:a.filter(r=>!c.has(r.varName)&&(c.add(r.varName),!0)),rawJson:t}}module.exports={parseScene:parseScene};
@@ -0,0 +1 @@
1
+ "use strict";const{parseXmlFile:parseXmlFile}=require("../utils/XmlUtils"),{getMtime:getMtime}=require("../utils/FileUtils");let _cache=null;function parseProps(e){if(!e)return{};const t={},n=e.split("@@!@@");for(const e of n){const n=e.indexOf("=");if(-1===n)continue;const s=e.substring(0,n).trim();if(!s)continue;const i=e.substring(n+1),o=i.trim(),c=Number(o);t[s]=""!==o&&!isNaN(c)&&isFinite(c)?c:i}return t}function loadStyles(e){const t=getMtime(e);if(_cache&&_cache.mtime===t&&_cache.path===e)return _cache.data;const n=parseXmlFile(e),s=n?.res?.item,i=new Map;if(!s)return i;const o=Array.isArray(s)?s:[s];for(const e of o){const t=e.name;if(!t)continue;const n=e.props||"",s={name:t,type:e.type||"Image",compress:void 0!==e.compress?Number(e.compress):void 0,pack:void 0!==e.pack?Number(e.pack):void 0,quality:void 0!==e.quality?Number(e.quality):80,props:n,propsMap:parseProps(n),picType:void 0!==e.picType?Number(e.picType):void 0,scale:void 0!==e.scale?Number(e.scale):void 0};i.set(t,s)}return expandLocales(i),_cache={mtime:t,path:e,data:i},i}const LOCALE_SEGMENTS=["vi","cn","en","tw"];function expandLocales(e){const t=new RegExp(`/(${LOCALE_SEGMENTS.join("|")})/`),n=[...e.entries()];for(const[s,i]of n){const n=t.exec(s);if(!n)continue;const o=n[1],c=n.index+1,r=c+o.length;for(const t of LOCALE_SEGMENTS){if(t===o)continue;const n=s.substring(0,c)+t+s.substring(r);e.has(n)||e.set(n,{...i,name:n})}}return e}function groupByPack(e){const t=new Map;for(const n of e.values())0!==n.pack&&(t.has(n.pack)||t.set(n.pack,[]),t.get(n.pack).push(n));return t}function isDirectoryEntry(e){return!e.split("/").pop().includes(".")}function resolveStyle(e,t){const n=e.get(t);if(n)return n;const s=t.split("/");for(let t=s.length-2;t>=1;t--){const n=s.slice(0,t+1).join("/"),i=e.get(n);if(i&&isDirectoryEntry(i.name))return i}}function invalidateCache(){_cache=null}module.exports={loadStyles:loadStyles,expandLocales:expandLocales,resolveStyle:resolveStyle,groupByPack:groupByPack,parseProps:parseProps,invalidateCache:invalidateCache};
@@ -0,0 +1 @@
1
+ "use strict";const fs=require("fs-extra"),path=require("path");function getMtime(e){try{return fs.statSync(e).mtimeMs}catch{return 0}}function writeIfChanged(e,t){fs.ensureDirSync(path.dirname(e));let r="";try{r=fs.readFileSync(e,"utf-8")}catch{}return r!==t&&(fs.writeFileSync(e,t,"utf-8"),!0)}function collectFiles(e,t){const r=new Set(Array.isArray(t)?t:[t]),n=[];return function e(t){let i;try{i=fs.readdirSync(t,{withFileTypes:!0})}catch{return}for(const s of i){const i=path.join(t,s.name);s.isDirectory()?e(i):r.has(path.extname(s.name).toLowerCase())&&n.push(i)}}(e),n}function ensureDir(e){return fs.ensureDirSync(e),e}module.exports={getMtime:getMtime,writeIfChanged:writeIfChanged,collectFiles:collectFiles,ensureDir:ensureDir};
@@ -0,0 +1 @@
1
+ "use strict";const{XMLParser:XMLParser}=require("fast-xml-parser"),fs=require("fs-extra"),_parser=new XMLParser({ignoreAttributes:!1,attributeNamePrefix:"",allowBooleanAttributes:!0,trimValues:!0,parseTagValue:!1,isArray:r=>["item"].includes(r)});function parseXmlFile(r){const e=fs.readFileSync(r,"utf-8");return _parser.parse(e)}function parseXmlString(r){return _parser.parse(r)}module.exports={parseXmlFile:parseXmlFile,parseXmlString:parseXmlString};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tyhuynh-laya-cmd",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Custom LayaAir 2 UI build tool - fast incremental atlas and UI code generation",
5
5
  "main": "dist/index.js",
6
6
  "bin": {