tyhuynh-laya-cmd 1.0.5 → 1.0.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.
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- "use strict";const path=require("path"),fs=require("fs-extra"),{program:program}=require("commander");require("colors");const{loadProjectConfig:loadProjectConfig}=require("./src/config/ProjectConfig"),{loadStyles:loadStyles,invalidateCache:invalidateStyles}=require("./src/parsers/StylesParser"),{loadPageStyles:loadPageStyles,invalidateCache:invalidatePages}=require("./src/parsers/PageParser"),{generateUICode:generateUICode}=require("./src/generators/UICodeGen"),{generateAtlas:generateAtlas}=require("./src/generators/AtlasGen"),{collectFiles:collectFiles}=require("./src/utils/FileUtils");function autoDetectLayaDir(){const e=e=>{const o=path.join(e,".laya");return fs.existsSync(o)&&fs.statSync(o).isFile()};let o=process.cwd();if(e(o))return o;const t=path.join(o,"laya");if(e(t))return t;let a=null,n=o;for(;n!==a;){const o=path.join(n,"laya");if(e(o))return o;a=n,n=path.dirname(n)}return null}async function runUI(e){const{project:o,atlas:t,code:a,clear:n,watch:r}=e,l=a||!t&&!a,c=t||!t&&!a,s=path.resolve(o);fs.existsSync(s)||(console.error(`[ERROR] Project directory not found: ${s}`.red),process.exit(1)),r?(console.log("\n╔════════════════════════════════════════╗"),console.log("║ 🎨 UI Build Tool (tyhuynh-laya-cmd) ║"),console.log("╚════════════════════════════════════════╝")):console.log(`\n${"⚡ Custom LayaAir2 Build Tool".cyan.bold}`),console.log(`[UI] Project: ${s.gray}`),console.log(`[UI] Mode: ${[l&&"CODE",c&&"ATLAS"].filter(Boolean).join(" + ").yellow}`),n&&console.log(`[UI] Cache: ${"CLEARED".red}`),console.log(""),await build(s,{doCode:l,doAtlas:c,clear:n}),r&&await startWatchMode(s,{doCode:l,doAtlas:c})}async function build(e,{doCode:o,doAtlas:t,clear:a,hint:n={}}){const r=Date.now();let l;try{l=loadProjectConfig(e)}catch(e){return void console.error(`[ERROR] Cannot load .laya config: ${e.message}`.red)}const c=path.dirname(e),s=path.join(e,"pages"),i=path.join(c,l.codeExportPath,"layaMaxUI.ts"),d=path.join(e,".custom-cmd-cache"),p=path.join(d,"code-cache.json"),h=path.join(d,"atlas-cache.json"),g=o&&!n.atlasOnly,u=t&&!n.codeOnly,y=[],f=!!n&&Object.keys(n).length>0?()=>{}:e=>console.log(` ${e}`);g&&y.push(generateUICode({pagesDir:s,outputPath:i,cachePath:p,projectPath:c,resExportPath:path.join(c,l.resExportPath),clear:a,changedScenes:n.changedScenes||null,log:f}).then(e=>{console.log(`[UI] 📄 UI Code: ${String(e.built).green} built, ${e.skipped} skipped`)}).catch(e=>{console.error(`[UI] ${"❌ CODE ERROR".red}: ${e.message}`)})),u&&y.push((async()=>{try{const o=path.join(e,"styles.xml"),t=loadStyles(o);await generateAtlas({styleMap:t,projectConfig:l,assetDir:path.join(e,"assets"),outputDir:path.join(c,l.resExportPath),cachePath:h,clear:a,changedDirs:n.changedDirs||null,log:f}),console.log(`[UI] 🖼 Atlas: ${"done".green}`)}catch(e){console.error(`[UI] ${"❌ ATLAS ERROR".red}: ${e.message}`)}})()),await Promise.all(y);const m=((Date.now()-r)/1e3).toFixed(2),j=(new Date).toLocaleTimeString();console.log(`[UI] ✅ ${j} — Build complete in ${m}s\n`)}async function startWatchMode(e,{doCode:o,doAtlas:t}){let a;try{a=require("chokidar")}catch{console.error("[WATCH] chokidar not installed. Run: npm install chokidar".red),process.exit(1)}const n=path.join(e,"pages"),r=path.join(e,"assets"),l=path.join(e,"styles.xml"),c=path.join(e,"pageStyles.xml");console.log("[UI] 👀 Watching pages/ & assets/ — Ctrl+C để dừng"),console.log("[UI] .scene → code | image → atlas | .xml → full rebuild\n");let s=null,i=new Set,d=new Set,p=!1;function h(){clearTimeout(s),s=setTimeout(async()=>{const a=[...i],n=[...d],c=p;i.clear(),d.clear(),p=!1;const s=[...a,...n,...c?[l]:[]];if(0===s.length)return;if(console.log("\n[UI] 📝 Đã sửa:"),s.map(o=>path.relative(e,o).replace(/\\/g,"/")).forEach(e=>console.log(`[UI] • ${e}`)),c)return console.log("[UI] ↻ XML config changed — full rebuild..."),void await build(e,{doCode:o,doAtlas:t,clear:!1,hint:{}});const h=o&&a.length>0,g=t&&n.length>0;if(!h&&!g)return;let u=null;g&&(u=new Set(n.map(e=>path.relative(r,path.dirname(e)).replace(/\\/g,"/"))));let y=null;h&&(y=new Set(a)),await build(e,{doCode:o,doAtlas:t,clear:!1,hint:{codeOnly:h&&!g,atlasOnly:g&&!h,changedDirs:u,changedScenes:y}})},300)}function g(e){i.add(e),h()}function u(e){d.add(e),h()}function y(e){e===l&&invalidateStyles(),e===c&&invalidatePages(),p=!0,h()}const f={ignoreInitial:!0,usePolling:!1};o&&a.watch(path.join(n,"**","*.scene"),f).on("add",g).on("change",g).on("unlink",g).on("error",e=>console.error(`[WATCH ERROR] ${e}`.red)),t&&a.watch([`${r}/**/*.png`,`${r}/**/*.jpg`],f).on("add",u).on("change",u).on("unlink",u).on("error",e=>console.error(`[WATCH ERROR] ${e}`.red)),a.watch([l,c],f).on("add",y).on("change",y).on("error",e=>console.error(`[WATCH ERROR] ${e}`.red))}program.name("tyhuynh-laya-cmd").description("Custom LayaAir 2 fast incremental UI build tool").version("1.0.1"),program.command("ui").description("Export UI code and/or atlas").option("-p, --project <dir>","Path to laya/ directory (auto-detected if omitted)",null).option("-a, --atlas","Generate atlas sprite sheets",!1).option("-d, --code","Generate TypeScript UI code",!1).option("-c, --clear","Clear cache (force full rebuild)",!1).option("-w, --watch","Watch mode",!1).action(async e=>{e.project||(e.project=autoDetectLayaDir(),e.project||(console.error("[ERROR] Cannot find laya/ directory. Use -p <dir> to specify manually.".red),process.exit(1)),console.log(` Auto-detected: ${e.project.gray}`)),await runUI(e)}),program.parse(process.argv);
2
+ "use strict";const path=require("path"),fs=require("fs-extra"),{program:program}=require("commander");require("colors");const{loadProjectConfig:loadProjectConfig}=require("./src/config/ProjectConfig"),{loadStyles:loadStyles,invalidateCache:invalidateStyles}=require("./src/parsers/StylesParser"),{loadPageStyles:loadPageStyles,invalidateCache:invalidatePages}=require("./src/parsers/PageParser"),{generateUICode:generateUICode}=require("./src/generators/UICodeGen"),{generateAtlas:generateAtlas}=require("./src/generators/AtlasGen"),{collectFiles:collectFiles}=require("./src/utils/FileUtils");function autoDetectLayaDir(){const e=e=>{const o=path.join(e,".laya");return fs.existsSync(o)&&fs.statSync(o).isFile()};let o=process.cwd();if(e(o))return o;const t=path.join(o,"laya");if(e(t))return t;let a=null,r=o;for(;r!==a;){const o=path.join(r,"laya");if(e(o))return o;a=r,r=path.dirname(r)}return null}async function runUI(e){const{project:o,atlas:t,code:a,clear:r,watch:n}=e,s=a||!t&&!a,l=t||!t&&!a,c=path.resolve(o);fs.existsSync(c)||(console.error(`[ERROR] Project directory not found: ${c}`.red),process.exit(1)),n?(console.log("\n╔════════════════════════════════════════╗"),console.log("║ 🎨 UI Build Tool (tyhuynh-laya-cmd) ║"),console.log("╚════════════════════════════════════════╝")):console.log(`\n${"⚡ Custom LayaAir2 Build Tool".cyan.bold}`),console.log(`[UI] Project: ${c.gray}`),console.log(`[UI] Mode: ${[s&&"CODE",l&&"ATLAS"].filter(Boolean).join(" + ").yellow}`),r&&console.log(`[UI] Cache: ${"CLEARED".red}`),console.log(""),await build(c,{doCode:s,doAtlas:l,clear:r}),n&&await startWatchMode(c,{doCode:s,doAtlas:l})}async function build(e,{doCode:o,doAtlas:t,clear:a,hint:r={}}){const n=Date.now();let s;try{s=loadProjectConfig(e)}catch(e){return void console.error(`[ERROR] Cannot load .laya config: ${e.message}`.red)}const l=path.dirname(e),c=path.join(e,"pages"),i=path.join(l,s.codeExportPath,"layaMaxUI.ts"),d=path.join(e,".custom-cmd-cache"),p=path.join(d,"code-cache.json"),h=path.join(d,"atlas-cache.json"),u=o&&!r.atlasOnly,g=t&&!r.codeOnly,y=[],m=!!r&&Object.keys(r).length>0?()=>{}:e=>console.log(` ${e}`);u&&y.push(generateUICode({pagesDir:c,outputPath:i,cachePath:p,projectPath:l,resExportPath:path.join(l,s.resExportPath),clear:a,changedScenes:r.changedScenes||null,log:m}).then(e=>{console.log(`[UI] 📄 UI Code: ${String(e.built).green} built, ${e.skipped} skipped`)}).catch(e=>{console.error(`[UI] ${"❌ CODE ERROR".red}: ${e.message}`)})),g&&y.push((async()=>{try{const o=path.join(e,"styles.xml"),t=loadStyles(o);await generateAtlas({styleMap:t,projectConfig:s,assetDir:path.join(e,"assets"),outputDir:path.join(l,s.resExportPath),cachePath:h,clear:a,changedDirs:r.changedDirs||null,log:m}),console.log(`[UI] 🖼 Atlas: ${"done".green}`)}catch(e){console.error(`[UI] ${"❌ ATLAS ERROR".red}: ${e.message}`)}})()),await Promise.all(y);const f=((Date.now()-n)/1e3).toFixed(2),j=(new Date).toLocaleTimeString();console.log(`[UI] ✅ ${j} — Build complete in ${f}s\n`)}async function startWatchMode(e,{doCode:o,doAtlas:t}){let a;try{a=require("chokidar")}catch{console.error("[WATCH] chokidar not installed. Run: npm install chokidar".red),process.exit(1)}const r=path.join(e,"pages"),n=path.join(e,"assets"),s=path.join(e,"styles.xml"),l=path.join(e,"pageStyles.xml");console.log("[UI] 👀 Watching pages/ & assets/ — Ctrl+C để dừng"),console.log("[UI] .scene → code | image → atlas | .xml → full rebuild\n");let c=null,i=new Set,d=new Set,p=!1;function h(){clearTimeout(c),c=setTimeout(async()=>{const a=[...i],r=[...d],l=p;i.clear(),d.clear(),p=!1;const c=[...a,...r,...l?[s]:[]];if(0===c.length)return;if(console.log("\n[UI] 📝 Đã sửa:"),c.map(o=>path.relative(e,o).replace(/\\/g,"/")).forEach(e=>console.log(`[UI] • ${e}`)),l)return console.log("[UI] ↻ XML config changed — full rebuild..."),void await build(e,{doCode:o,doAtlas:t,clear:!1,hint:{}});const h=o&&a.length>0,u=t&&r.length>0;if(!h&&!u)return;let g=null;u&&(g=new Set(r.map(e=>path.relative(n,path.dirname(e)).replace(/\\/g,"/"))));let y=null;h&&(y=new Set(a)),await build(e,{doCode:o,doAtlas:t,clear:!1,hint:{codeOnly:h&&!u,atlasOnly:u&&!h,changedDirs:g,changedScenes:y}})},300)}function u(e){i.add(e),h()}function g(e){d.add(e),h()}function y(e){e===s&&invalidateStyles(),e===l&&invalidatePages(),p=!0,h()}const m={ignoreInitial:!0,usePolling:!1};o&&a.watch(path.join(r,"**","*.scene"),m).on("add",u).on("change",u).on("unlink",u).on("error",e=>console.error(`[WATCH ERROR] ${e}`.red)),t&&a.watch([`${n}/**/*.png`,`${n}/**/*.jpg`],m).on("add",g).on("change",g).on("unlink",g).on("error",e=>console.error(`[WATCH ERROR] ${e}`.red)),a.watch([s,l],m).on("add",y).on("change",y).on("error",e=>console.error(`[WATCH ERROR] ${e}`.red))}program.name("tyhuynh-laya-cmd").description("Custom LayaAir 2 fast incremental UI build tool").version("1.0.1"),program.command("ui").description("Export UI code and/or atlas").option("-p, --project <dir>","Path to laya/ directory (auto-detected if omitted)",null).option("-a, --atlas","Generate atlas sprite sheets",!1).option("-d, --code","Generate TypeScript UI code",!1).option("-c, --clear","Clear cache (force full rebuild)",!1).option("-w, --watch","Watch mode",!1).action(async e=>{e.project||(e.project=autoDetectLayaDir(),e.project||(console.error("[ERROR] Cannot find laya/ directory. Use -p <dir> to specify manually.".red),process.exit(1)),console.log(` Auto-detected: ${e.project.gray}`)),await runUI(e)}),program.command("migrate").description("Migrate stray assets from bin/ into laya/assets/ and update styles.xml").option("-p, --project <dir>","Path to laya/ directory (auto-detected if omitted)",null).option("--dry-run","Preview changes without writing anything",!1).action(e=>{const o=[];e.dryRun&&o.push("--dry-run"),e.project&&o.push("-p",e.project);const{spawnSync:t}=require("child_process"),a=require.resolve("./scripts/migrate-stray-assets.js"),r=t(process.execPath,[a,...o],{stdio:"inherit"});process.exit(r.status??0)}),program.parse(process.argv);
@@ -1,2 +1,2 @@
1
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");
2
+ "use strict";const path=require("path"),fs=require("fs-extra");function autoDetectLayaDir(){const e=e=>{const s=path.join(e,".laya");return fs.existsSync(s)&&fs.statSync(s).isFile()};let s=process.cwd();if(e(s))return s;const t=path.join(s,"laya");if(e(t))return t;let n=null,o=s;for(;o!==n;){const s=path.join(o,"laya");if(e(s))return s;n=o,o=path.dirname(o)}return null}const args=process.argv.slice(2),dryRun=args.includes("--dry-run"),pIdx=args.indexOf("-p");let layaDir;-1!==pIdx?layaDir=path.resolve(args[pIdx+1]):(layaDir=autoDetectLayaDir(),layaDir||(console.error("\n[migrate] ❌ Không tìm thấy thư mục laya/."),console.error("[migrate] Hãy chạy lệnh này từ thư mục dự án client/"),console.error("[migrate] hoặc dùng: tyhuynh-laya-cmd migrate -p <path/to/laya>\n"),process.exit(1)),console.log(`[migrate] Auto-detected: ${layaDir}`));const 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");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tyhuynh-laya-cmd",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
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": {