@wiztivi/dana-cli 0.0.17 → 0.0.18

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.
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{Command as e,Option as t,Argument as a}from"commander";import n,{readFile as o,writeFile as i,mkdir as s,cp as r,access as c,readdir as l,symlink as d,rm as p,appendFile as m}from"node:fs/promises";import u,{resolve as g,dirname as f,join as w,sep as h}from"node:path";import*as y from"@clack/prompts";import{log as v}from"@clack/prompts";import{execSync as k,spawn as b,exec as C,spawnSync as A}from"node:child_process";import $ from"@babel/parser";import T from"@babel/traverse";import*as P from"@babel/template";import*as j from"@babel/types";import N from"@babel/generator";import D from"picocolors";import{createRequire as E}from"node:module";import{fileURLToPath as O}from"node:url";import{homedir as S}from"node:os";import{CodeartifactClient as V,GetRepositoryEndpointCommand as x,ListPackageVersionsCommand as _,GetAuthorizationTokenCommand as I}from"@aws-sdk/client-codeartifact";import{maxSatisfying as F}from"semver";import{valid as L,clean as M}from"semver-ts";import R from"handlebars";import z from"node:process";import G from"get-port";import U from"express";import J from"open";import W from"jsonwebtoken";import{findUp as H}from"find-up";import Y from"adm-zip";const q="tvos",B="tizen",X="webos",K="androidtv",Z="css",Q="lightning",ee="vidaa",te="titanos",ae=["@dana/renderer-css","@dana/renderer-lightning-html5","@dana/tools-grunt","@dana/vendor-test","@dana/engine-nodejs","@dana/vendor-components","@wiztivi/dana-templates","@dana/core","@dana/renderer-lightning","@dana/engine-html5","@dana/vendor-fake-native"],ne={[B]:{label:"Samsung",package:"@dana/vendor-tizen",dependencies:["@dana/vendor-tizen","@dana/vendor-shaka","@dana/tools-smarttv-grunt"]},[X]:{label:"LG",package:"@dana/vendor-webos",dependencies:["@dana/vendor-webos","@dana/vendor-shaka","@dana/tools-smarttv-grunt","@dana/vendor-webos-css"]},[ee]:{label:"Vidaa",package:"@dana/vendor-vidaa",dependencies:["@dana/vendor-vidaa","@dana/tools-smarttv-grunt"]},[te]:{label:"TitanOs",package:"@dana/vendor-titanos",dependencies:["@dana/vendor-titanos"]}},oe={[q]:{label:"TV OS",package:"@dana/engine-tvos",dependencies:["@dana/engine-tvos","@dana/renderer-tvos","@dana/tools-apple-grunt"]},[K]:{label:"Android",package:"@dana/engine-android",dependencies:["@dana/engine-android","@dana/renderer-android","@dana/tools-android-grunt"]}},ie={...ne,...oe},se=[Z,Q];const re={"program.usage":"A CLI to help you build dana applications","program.help.footer":"For more information, visit https://doc.dana-framework.com or contact us.","component.creation.success":"component created successfully.","component.creation.error":"Failed to create component","command.rail.description":"Add a rail","command.component.name":"Component's name","command.component.customization":"Full or partial customization. MANDATORY to not use default configuration.","command.rail.option.navigation":"Navigation type. Default: fixed","command.rail.option.direction":"Navigation direction. Default: horizontal","command.rail.option.cyclic":"True if cyclic. Default: false","command.device.description":"Add device(s) to your Dana app","command.device.version.error":"No version found.","command.device.no_selection":"Nothing selected or no device available. Exit","help.custom.devices":"Use the CLI to add the following devices:","help.custom.commands":"Add devices and built-in components to your DANA app:","help.option.title":"Available command options:","help.option.check":"check:","command.menu.description":"Add horizontal menu","command.menu.option.itemView":"Item to be used in the menu","command.menu.option.itemMargin":"Margin between items. Default: 20","helper.git.status.error":"Git status is not clean or an error occurred. Try to commit your change.","command.auth.description":"Authenticate to Dana framework","command.auth.login.description":"Login to Dana","command.auth.logout.description":"Logout from Dana","command.auth.login.option.org":"Organization to login/logout","command.auth.login.option.remote":"Allow connexion to dana authentication stack when working on remote server. Display login url","command.auth.login.option.port":"Provide a port for login server","command.auth.remoteMode":"You are in remote mode. Verify you are forwarding the right port to the host machine.","command.auth.accessBrowserUrl":"You can complete the operation by accessing the following url in the browser.","command.auth.status.description":"Get login status","command.auth.get-packages-token.description":"Get token for packages repository","command.scrollView.description":"Add a scrollView of rails","command.scrollView.option.itemView":"Item to be used in the scrollView. Default: pre-made rail","command.scrollView.option.itemMargin":"Margin between items. Default: 550","command.screen.description":"Add a screen","command.create_app.description":"Create a new Dana app","command.create_app.name":"Project's name","command.create_app.directory":"Project directory","command.create_app.type":"Choose app type","command.create_app.template_type":"Template type","command.create_app.screen_template_type.error":"Error: A template type can only be provided with the 'templated' option","command.create_app.devices":"Add device(s) when creating the project","command.create_app.aws_profile":"Aws profile","command.create_app.aws_repository":'Aws repository provided during registration. Name is XXX in "dana-XXX-repository".',"command.zip.description":"Generate an archive for a Dana app","command.zip.creation.start":"Creation","command.zip.creation.end":"Creation complete.","command.zip.final":"Archive is complete and can be uploaded on dana cloud services platform.","task.installation.start":"Installation","task.installation.end":"Installation complete.","creation.final":"Everything is ready.","script.run.title":"Now you can just run:","git.error":"Git is not installed or not available in the PATH. You must install it to continue.","aws.cli.error":"AWS cli is not installed or not available in the PATH. You must install it to continue.","node.error":"Node version should be >=","setup.ok":"Configuration ok","setup.ko":"Prerequisite not met","device.unavailable":"Enhance your experience by adding device(s) to your subscription:","device.selection":"Press SPACE KEY to select additional device(s).","renderer.selection":"Select AT LEAST one renderer for","input.name":"What is your project's name ?","input.aws.error":"You need to have an AWS account.\nSee:https://dana-framework.com/guides/getting-started#configure-aws-connection","input.aws.profile":"Select your AWS profile","aws.login.error":"Please log to AWS before trying again","error.common.start.message":"An error occurred","command.completion.description":"Install shell completion (bash or zsh)","command.completion.argument":"Shell. Bash or zsh","command.completion.success":"Completion installed to ","login.info.token_expired":"Your token has expired.","login.info.please_login":"Please run 'dana auth login' command","login.error.not_logged":"You're not logged in.","login.info.logged":"You're currently logged in to Dana","command.hook.dana_project":"Can not execute command outside a Dana project: no '.dana' folder found.\nYou can add it to the root of your Dana project if needed","command.settings.description":"Add settings features in the Settings Screen","command.module":"Add a module","command.module.name":"Module's name"},ce=class{static appConfig={};static getConfig(...e){return Promise.resolve({})}static setConfig(...e){return Promise.resolve("Device configuration done")}static updateProfileJsonWithRenderers(e,t){for(const a of t)e={...e,...this.appConfig[a]};return e}},le=class extends ce{static getConfig=async()=>({renderers:[Q,Z]});static setConfig=()=>Promise.resolve("Vidaa configuration done")},de=class extends ce{static getConfig=async()=>({renderers:[Q,Z]});static setConfig=()=>Promise.resolve("TitanOS configuration done")},pe=class extends ce{static appConfig={css:{"template-webos_css":{mixins:["default","webos"],base:{name:"Webos",vendors:["@dana/vendor-components","@dana/vendor-shaka","@dana/vendor-webos-css"]}}},lightning:{"template-webos_lightning":{mixins:["default","webos"],base:{name:"Webos",vendors:["@dana/vendor-components","@dana/vendor-shaka","@dana/vendor-webos-lightning"]}}}};static getConfig=async()=>({renderers:[Z]});static setConfig=async({directory:e,config:t})=>{const a=u.join(e,"profiles","app.config.webos.json"),n=(await o(a)).toString();let s=JSON.parse(n);return s=this.updateProfileJsonWithRenderers(s,t.renderers),await i(a,JSON.stringify(s,null,4)),"Webos configuration done"}},me=class extends ce{static appConfig={css:{"template-tizen_css":{mixins:["default","tizen"],base:{name:"Tizen",vendors:["@dana/renderer-css"]}}},lightning:{"template-tizen_lightning":{mixins:["default","tizen"],base:{name:"Tizen",vendors:["@dana/renderer-lightning-html5"]}}}};static getConfig=async()=>({renderers:[Q]});static setConfig=async({directory:e,config:t})=>{const a=u.join(e,"profiles","app.config.tizen.json"),n=(await o(a)).toString();let s=JSON.parse(n);return s=this.updateProfileJsonWithRenderers(s,t.renderers),await i(a,JSON.stringify(s,null,4)),"Tizen configuration done"}},ue="wtvMenuListView",ge="wtvRailListView",fe="tileView",we="scrollView",he="scrollItemView",ye="ButtonItemView",ve="screen",ke="vod",be=e=>`${e[0].toUpperCase()}${e.substring(1)}`,Ce=e=>e.map(e=>e.command).join(", "),Ae=e=>`${e[0].toUpperCase()}${e.substring(1).toLowerCase()}`,$e=class{static selectionFactory=async(e,t,a)=>{t=t.map(e=>({value:e.value,label:e.label}));const n=await y.select({message:a,options:t});return $e.handleCancellation(e),n};static checkOption=(e,t,a)=>{if(!e)return"";const n=t.find(t=>t.command===e);return n?n.value:(y.log.info(`"${e}" does not match any ${a} option. Please select one below:`),"")};static normalizeFileName=(e,t)=>{const a=be(t);switch(e){case ge:case ue:return`${a}ListView.js`;case fe:return`${a}ListItemView.js`;case we:return`${a}ScrollView.js`;case he:return`${a}ScrollItemView.js`;case ve:case ke:return`${a}Screen.js`;case ye:return`${a}View.js`;default:return`${a}.js`}};static handleCancellation=function(e){y.isCancel(e)&&(y.cancel("Operation cancelled."),process.exit(4))};static getTemplatePath=function(){return E(O(import.meta.url)).resolve("@wiztivi/dana-templates")}},Te="dana",Pe="733912940672";class je{static filePath=u.join(S(),".dana.json");static async getConfig(){try{const e=await o(this.filePath,{encoding:"utf-8"});let t=JSON.parse(e);const a=t.version??1;return 2!=a&&(t=je.migrateConfig(t,a),await i(this.filePath,JSON.stringify(t,null,4))),t}catch{return null}}static migrateConfig(e,t){if(1===t){const t=e;return{lastUsedOrg:t.org,credentials:{[t.org]:{dana:t.dana,aws:t.aws}},isRemote:!1,version:2}}return e}static async store(e,t,a){let n=await this.getConfig();n??={lastUsedOrg:e.org,credentials:{},version:2,isRemote:!1},n.credentials??={},n.lastUsedOrg=e.org,a&&(n.port=a),n.isRemote=t,n.credentials[e.org]={aws:e.aws,dana:e.dana},await i(this.filePath,JSON.stringify(n,null,4))}static async get(e){const t=await this.getConfig(),a=e??t?.lastUsedOrg??"public",n={org:a,isRemote:!1};return t?.credentials?.[a]?.aws&&(n.aws=t?.credentials[a]?.aws),t?.credentials?.[a]?.dana&&(n.dana=t?.credentials[a]?.dana),t?.port&&(n.port=t.port),t?.credentials?.[a]?.codeArtifact&&(n.codeArtifact=t?.credentials[a]?.codeArtifact),n.isRemote=!!t?.isRemote,n}static async delete(e){const t=await this.getConfig();t&&(delete t.credentials[e],await i(this.filePath,JSON.stringify(t,null,4)))}static async updateCodeArtifactToken(e,t){const a=await this.getConfig();a?.credentials&&(a.credentials[e].codeArtifact=t,await i(this.filePath,JSON.stringify(a,null,4)))}}const Ne=async({organization:e,credentials:t,client:a,format:n="npm"})=>{const o=t??(await je.get())?.aws;if(!o)throw new Error("No valid credentials available.");const i=a??De(o),s=Ee(e),r=new x({domain:Te,domainOwner:Pe,repository:s,format:n}),{repositoryEndpoint:c}=await i.send(r);if(!c)throw new Error("Repository endpoint not found");return c},De=e=>new V({region:"eu-central-1",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey,sessionToken:e.sessionToken}}),Ee=e=>`dana-${e}-repository`,Oe=class{static cleanOnError=()=>{try{k("git clean -d -f && git reset --hard head")}catch{throw new Error("Files deletion failed. Delete changes manually.")}return"Changes successfully deleted."};static getInstalledDevices=async e=>{const t=u.join(e,"package.json"),a=(await o(t)).toString(),n=JSON.parse(a),i=[];for(const e of Object.keys(n.scripts))if(e.includes("start:")){const t=e.split(":")[1];i.push(t)}return i};static getAppIcon=()=>Promise.resolve("images/dana.png");static promptTextFactory=async({errorMessage:e,promptMessage:t,validationFunction:a,placeholder:n,initialValue:o})=>{const i=await y.text({message:t,placeholder:n,initialValue:o,validate:t=>a(t,e)});return $e.handleCancellation(i),i};static getLatestPackageVersion=async({packageName:e,repository:t,format:a,namespace:n})=>{let o;try{const i=await je.get(),s=De(i.aws),r=new _({package:e,domain:Te,domainOwner:Pe,repository:t,format:a,namespace:n}),c=await s.send(r),l=c.versions?.map(e=>e.version).filter(Boolean);if(o=F(l,"*"),o)return o;throw new Error(re["command.device.version.error"])}catch(e){throw new Error(e.message??re["error.common.start.message"])}}},Se=class extends ce{static GITIGNORE_PATH=".gitignore";static setConfig=async({directory:e})=>{const t=u.join(e,Se.GITIGNORE_PATH),a=await o(t,{encoding:"utf8"});return await i(t,a+"\n# For TVOS\napp_tvos/build \napp_tvos/libs"),".gitignore updated"};static getConfig=async()=>({jscVersion:await Se.getJscVersion()});static getJscVersion=async()=>await Oe.getLatestPackageVersion({packageName:"jscmodule",repository:"internal-apple-repository",format:"swift",namespace:"dana"})},Ve=(e,t)=>t?.trim().length?"":e,xe=class extends ce{static _askAppId=async()=>{const e=await y.text({message:"What is the appId ?",placeholder:"com.yourname.ui",validate:e=>(e=>e.length?/^[a-zA-Z]\w*(\.[a-zA-Z]\w*)+$/.test(e)?"":"AppId should be in the following format: com.example.myapp !":"AppId is required !")(e)});return $e.handleCancellation(e),e};static _askAppName=async()=>{const e=await y.text({message:"What is the appName ?",placeholder:"Dana",validate:e=>Ve("App name is required !",e)});return $e.handleCancellation(e),e};static _askAppVersion=async()=>{const e=await y.text({message:"What is the app version ?",placeholder:"1.0.0",initialValue:"1.0.0",validate:e=>(e=>e?L(M(e)??"")?"":"App version format should be x.x.x":"App version is required")(e)});return $e.handleCancellation(e),M(e)??""};static _askAppVersionCode=async()=>{const e=await y.text({message:"What is the app version code ?",placeholder:"1000",initialValue:"1000",validate:e=>(e=>e?Number.isInteger(+e)?"":"App version code has to be an Integer":"App version code is required")(e)});return $e.handleCancellation(e),Number(e)};static _getWtvAndroidVersion=async()=>await Oe.getLatestPackageVersion({packageName:"v8-runtime",repository:"internal-android-repository",format:"maven",namespace:"com.dana.androidtv"});static getConfig=async()=>{y.log.info(D.bold("Let's configure Android !"));return{device:await y.group({appId:()=>this._askAppId(),appName:()=>this._askAppName(),appVersion:()=>this._askAppVersion(),appVersionCode:()=>this._askAppVersionCode(),icon:()=>Oe.getAppIcon(),wtvAndroidVersion:()=>this._getWtvAndroidVersion()},{onCancel:()=>{y.cancel("Android configuration cancelled."),process.exit(0)}})}};static setConfig=async({directory:e,config:t})=>(await this.updateProfileJson(e,t),"Android config done");static updateProfileJson=async(e,t)=>{const a=u.join(e,"profiles","app.config.androidtv.json"),n=(await o(a)).toString(),s=JSON.parse(n);return s["base-android"].device={...s["base-android"].device,...t.device},await i(a,JSON.stringify(s,null,4)),"Profile.json updated"}},_e=class{static handleCancellation=function(e){y.isCancel(e)&&(y.cancel("Operation cancelled."),process.exit(0))};static askHtmlRenderers=async e=>{const t=se.map(e=>({value:e,label:e})),a=await y.multiselect({message:`${re["renderer.selection"]} ${e}`,required:!0,options:t});return this.handleCancellation(a),a};static getDeviceConfig=e=>{switch(e){case X:return pe;case B:return me;case q:return Se;case K:return xe;case ee:return le;case te:return de;default:return ce}};static promptUnavailableDevices=e=>{y.log.warn(D.bold(D.yellow(re["device.unavailable"])+"\n"+D.magenta(e.join(", "))))};static promptSelectDevices=async(e,t=!1)=>{const a={message:re["device.selection"],options:e,required:t},n=await y.multiselect(a);return this.handleCancellation(n),n};static installation=async function(e,t){for(const a of e)(null==a.condition||a.condition)&&(t?.message(a.title),await a.task())};static printNpmCommandForUser=function(e){const t=`cd ${e.directory} && npm run`;y.log.info(D.bold("For browser:")),y.log.message(`${t} start`);for(const a of e.selectedDevices){y.log.info(D.bold(`For ${a}: `)),a!==B&&a!==X||y.log.warn(`You MUST update app.config.${a}.json with your own parameters`);const n=e.config[a];if(n?.renderers)for(const e of n.renderers)y.log.message(`${t} start:${a}_${e}`);else y.log.message(`${t} start:${a}`)}};F;static setFirstRoute=async function({directory:e}){const t=u.resolve(e,"scripts","app","ui","screens","SplashScreen.js"),a=await o(t,"utf8"),n=(0,P.default.expression)('this.route("home")'),s=$.parse(a,{sourceType:"module"});T.default(s,{Function(e){j.isObjectProperty(e.parent)&&j.isIdentifier(e.parent.key,{name:"beforeShow"})&&e.traverse({CallExpression(e){const t=e.node.callee;j.isMemberExpression(t)&&j.isIdentifier(t.object,{name:"console"})&&e.replaceWith(n())}})}});const r=N.default(s,{retainLines:!0}).code;return await i(t,r),"first route set"}};class Ie{static getPrompts(e,t){return{name:()=>this.askProjectName(e),directory:({results:e})=>this.getProjectDirectory(e.name),selectedDevices:()=>this.askDevices(t)}}static async askProjectName(e){if(null!=e)return Ie.formatAppName(e);return e=await y.text({message:re["input.name"],placeholder:"Placeholder Name",validate:e=>Ve("Project name is mandatory !",e)}),_e.handleCancellation(e),Ie.formatAppName(e)}static formatAppName(e){return e.trim().split(/[ /]+/).map((e,t)=>0===t?e:be(e)).join("")}static async getProjectDirectory(e){return Promise.resolve(`./${e}`)}static getAvailableDevices(){const e=[],t=[],a=y.spinner();a.start();for(const[a,n]of Object.entries(oe))try{k(`npm view ${n.package} --silent`,{encoding:"utf-8"}),e.push({value:a,label:oe[a].label})}catch{t.push(oe[a].label)}return a.stop("Available devices checked"),Promise.resolve({availableDevices:e,unavailableDevices:t})}static async askDevices(e){let t=this.getBasicDevices();if(!e)return t;const{availableDevices:a,unavailableDevices:n}=await this.getAvailableDevices();if(n.length&&_e.promptUnavailableDevices(n),a.length){const e={message:re["device.selection"],options:a,required:!1};t=t.concat(await y.multiselect(e))}return _e.handleCancellation(t),t}static getBasicDevices(){return Object.keys(ne)}static async getDevicesConfig({selectedDevices:e,awsProfile:t}){const a={};for(const n of e)a[n]=await _e.getDeviceConfig(n).getConfig(t);return a}}async function Fe(e,t){const a=await o(e,"utf-8");return R.compile(a)(t)}var Le;R.registerHelper("ifEqTo",(e,t)=>e===t),R.registerHelper("ifNotEqTo",(e,t)=>e!==t),R.registerHelper("normalizeName",(e,t)=>`${be(e)}${t}`),R.registerHelper("capitalize",e=>be(e)),function(e){e.BARE="bare",e.TUTORIAL="tutorial",e.TEMPLATED="templated"}(Le||(Le={}));const Me={family:"fa-solid-900",path:"styles/fonts/fa-solid-900.ttf"};class Re{static updateAppConfigFile=async function({directory:e}){const t=u.join(e,"profiles"),a=u.join(e,"app.config.json"),o=(await n.readdir(t,{withFileTypes:!0})).filter(e=>!e.isDirectory()).map(e=>u.join("profiles",e.name)),i=(await n.readFile(a)).toString(),s=JSON.parse(i);return s.includes=o,await n.writeFile(a,JSON.stringify(s,null,4)),"Config file(s) updated"};static addModuleToAppConfig=async function({directory:e},{modules:t,shouldAddIconFont:a}){const o=u.join(e,"app.config.json");try{const e=(await n.readFile(o)).toString(),i=JSON.parse(e),s=i.default;s.base??={};const r=s.base;r.modules??=[];const c=r.modules;for(const e of t)c.includes(e)||c.push(e);if(a){s.AppTheme.fonts.MockIcons=Me}return await n.writeFile(o,JSON.stringify(i,null,4)),"App config modules updated"}catch(e){throw new Error(`Failed to parse or update app config: ${e.message}`)}};static updateAppTheme=async function({directory:e},t){const a=u.join(e,"scripts","app","theme","AppTheme.js");let o=await n.readFile(a,"utf-8");return o=o.replace(`${t}: "`,`${t}: "${t}`),await n.writeFile(a,o),"AppTheme statics updated"};static updateNpmrcFile=async function({directory:e}){const t=await je.get(),a=await Ne({organization:t.org,credentials:t?.aws,format:"npm"}),o=u.join(e,".npmrc");return await n.writeFile(o,`@dana:registry=${a}`),"AWS profile updated"};static updatePackageJson=async function({name:e,directory:t,selectedDevices:a,config:o}){const i=u.join(t,"package.json"),s=(await n.readFile(i)).toString(),r=JSON.parse(s);if(e){const t=/[^A-Za-z0-9._-]/g;r.name=e.replaceAll(t,"").toLowerCase()}for(const e of a){const t=o[e];if(t?.renderers)for(const a of t.renderers){const t=`${e}_${a}`;r.scripts[`start:${t}`]=Re.setScriptValue(t)}else r.scripts[`start:${e}`]=Re.setScriptValue(e)}return await n.writeFile(i,JSON.stringify(r,null,4)),"Package updated"};static setDevicesConfig=async function({directory:e,config:t,selectedDevices:a,awsProfile:n}){for(const o of a){const a=t[o];await _e.getDeviceConfig(o).setConfig({directory:e,awsProfile:n,config:a})}return"Configuration done"};static setScriptValue=e=>`grunt serve --profile=template-${e}`;static applyTemplatesToFiles=async function({directory:e,name:t}){const a=u.join(e,"scripts","app"),o=`${i=t,i.trim().split(/[./_\-\\ ]/g).map((e,t)=>0===t?e.toLowerCase():Ae(e)).join("")}`;var i;const s=[{fromPath:u.join(a,"TemplateAppRoutes.js.hbs"),config:{appName:o},toPath:u.join(a,`${o}AppRoutes.js`)},{fromPath:u.join(a,"app.js.hbs"),config:{appName:o},toPath:u.join(a,"app.js")},{fromPath:u.join(e,"tests","unit","app","TemplateAppRoutes.js.hbs"),config:{appName:o},toPath:u.join(e,"tests","unit","app",`${o}AppRoutes.js`)}].concat(Re.getConfigFileOutput(e));for(const e of s){const t=await Fe(e.fromPath,e.config);await n.writeFile(e.toPath,t),await n.unlink(e.fromPath)}return"Files templated"};static getConfigFileOutput=function(e){const t=["gitignoreTemplate.hbs","editorconfigTemplate.hbs","eslintrc.jsonTemplate.hbs","nvmrcTemplate.hbs"],a=[];for(const n of t)a.push({fromPath:u.join(e,n),config:{},toPath:u.join(e,`.${n.replace("Template.hbs","")}`)});return a}}class ze{static installDependencies=({directory:e,selectedDevices:t,config:a})=>{const n=ze.getDependenciesList(t,a);return new Promise((t,a)=>{const o=b("npm",["i","--silent"],{shell:!0,cwd:e});o.on("close",o=>{0===o?ze.installSingleDependency(n,e).then(async()=>{await ze.updateSemver(e),t("Dependencies updated")}).catch(e=>{a(new Error("Dependencies update failed : "+(e.message??re["error.common.start.message"])))}):a(new Error("FAILURE during dependencies installation"))}),o.on("error",e=>{a(new Error(`FAILURE during dependency installation : ${e}`))})})};static getDependenciesList(e,t){let a=[...ae];return e.length&&(a=a.concat(this.getAllDependencies(e,t))),[...new Set(a)]}static installSingleDependency=(e,t)=>new Promise((a,n)=>{const o=b("npm",["i",...e,"--save-dev","--save-exact"],{cwd:t,shell:!0});o.on("close",e=>{0===e?a():n(new Error("FAILURE during dependency installation"))}),o.on("error",e=>{n(new Error(`FAILURE during dependency installation : ${e.message}`))})});static updateSemver=async e=>{const t=u.join(e,"package.json"),a=(await o(t)).toString(),n=JSON.parse(a);n.devDependencies=Object.entries(n.devDependencies).reduce((e,[t,a])=>(e[t]=t.includes("@dana")||t.includes("@wiztivi")?a.replace(/(\d+\.\d+\.)\d$/,"$1x"):a,e),{}),await i(t,JSON.stringify(n,null,4))};static getAllDependencies=function(e,t){let a=[];for(const t of e)a=a.concat(ie[t].dependencies);for(const e in t)if(Object.hasOwn(t,e)){const n=t[e].renderers??[];for(const t of n)a=a.concat(ie[e].renderers?.[t]??"")}return a=a.filter(e=>!!e),a};static installGrunt=function(){return new Promise(e=>{C("npm i -g grunt-cli",{},()=>{e("Grunt installed")})})};static runGitCommand(e,t){return new Promise((a,n)=>{const o=b("git",e,{cwd:t});o.on("close",t=>{0===t?a():n(new Error(`Git ${e[0]} failed`))}),o.on("error",t=>{n(new Error(`Git ${e[0]} failed: ${t.message}`))})})}static initGit=async function({directory:e}){try{return await ze.runGitCommand(["init"],e),await ze.runGitCommand(["add","."],e),await ze.runGitCommand(["commit","-m","Initial commit from Create DANA App"],e),"Git initialized"}catch(e){throw new Error("Git hasn't been initialised: "+e.message)}}}class Ge{static copyTemplate=async function({directory:e}){const t=g(f($e.getTemplatePath()),"app-template");return await s(e,{recursive:!0}),await r(t,e,{recursive:!0}),"Template copied"};static copyForTutorial=async function({directory:e}){const t=g(f($e.getTemplatePath()),"tutorial");return await r(t,e,{recursive:!0}),"Tutorial files copied"};static copyForTemplatedApp=async function({directory:e},{availableModules:t,templateDir:a,shouldAddIconFont:n}){const o=["images"];for(const i of t){await r(w(a,i),w(e,"modules",i),{recursive:!0,filter:e=>{const t=e.replace(w(a,i),"").split(h)[1];return!o.includes(t)}});for(const t of o)try{await c(w(a,i,t)),await r(w(a,i,t),w(e,t),{recursive:!0})}catch{}n&&await Ge.copyFontFile(e)}return"Modules files copied"};static copyFontFile=async function(e){const t=g(f($e.getTemplatePath()),"assets","fonts");await r(w(t,Me.family+".ttf"),w(e,"styles","fonts",Me.family+".ttf"));const a=w(e,"app.config.json"),n=(await o(a)).toString(),s=JSON.parse(n);s.default.AppTheme.fonts.MockIcons=Me,await i(a,JSON.stringify(s,null,4))};static copyAppConfigFiles=async function({selectedDevices:e,directory:t}){const a=g(f($e.getTemplatePath()),"profiles"),n=await l(a);for(const o of n){e.includes(o.split(".")[2])&&await r(w(a,o),w(t,"profiles",o))}return"Config file(s) copied"};static runGruntCommand(e,t){if(0===A("grunt",e,{shell:!0,cwd:t}).status)return Promise.resolve("Grunt task(s) done");throw new Error("Grunt task(s) failed")}static executeGruntTasks=async function({directory:e,selectedDevices:t,config:a}){if(t.includes(K)){const t=await je.get(),n=["copyProjectAndroidTv",`--appUrl=${await Ne({organization:t.org,credentials:t?.aws,format:"maven"})}`,`--organization=${t.org}`],o=["appId","appName","appVersion","appVersionCode","wtvAndroidVersion"];for(const e of o)n.push(`--${e}=${a.androidtv?.device[e]}`);try{await Ge.runGruntCommand(n,e)}catch(e){throw new Error(`Failed to execute grunt tasks: ${e.message}`)}}if(t.includes(q))try{const t=["copyProjectTvos",`--jscVersion=${a.tvos?.jscVersion}`];await Ge.runGruntCommand(t,e)}catch(e){throw new Error(`Failed to execute grunt tasks: ${e.message}`)}return"Grunt task(s) done"}}const Ue=async()=>{y.intro(`Add device(s) to ${u.basename(z.cwd())} !`);const e=y.spinner(),t={};try{t.directory="./",t.awsProfile=(await je.get()).org;const{availableDevices:e,unavailableDevices:a}=await Ie.getAvailableDevices(),n=await Oe.getInstalledDevices(t.directory),o=e.filter(e=>!n.includes(e.value));if(a.length&&_e.promptUnavailableDevices(a),o.length&&(t.selectedDevices=await _e.promptSelectDevices(o)),!t.selectedDevices?.length)return void y.outro(re["command.device.no_selection"]);t.config=await Ie.getDevicesConfig(t)}catch(e){const t=e instanceof Error?e.message:e;y.outro(D.red("An issue occurred :"+t)),z.exit(5)}try{e.start(re["task.installation.start"]),await _e.installation([{title:"Update package.json",task:async()=>Re.updatePackageJson(t)},{title:"Copy app.config files",task:async()=>Ge.copyAppConfigFiles(t)},{title:"Update app.config.json",task:async()=>Re.updateAppConfigFile(t)},{title:"Install dependencies with npm",task:async()=>ze.installDependencies(t)},{title:"Execute grunt tasks",task:async()=>Ge.executeGruntTasks(t)},{title:"Execute devices config",task:async()=>Re.setDevicesConfig(t)}],e)}catch(t){e.stop();const a=t instanceof Error?t.message:t;y.outro(D.red(`${re["error.common.start.message"]} ${a}`)),z.exit(5)}e.stop(re["task.installation.end"]),y.outro("Everything is ready ! Don't forget to commit your change.")};class Je{static getAPIURL(e){return`https://api.${e}.dana-framework.com`}static async login(e,t,a){const n=U();n.disable("x-powered-by");const o=new Promise((o,i)=>{const s=setTimeout(()=>{i(new Error("Login timed out."))},3e5);n.get("/callback",async(n,r)=>{clearTimeout(s);try{if(n.query.error)return r.send("Login failed. Check the CLI for details."),i(new Error(n.query.error));const s=JSON.parse(n.query.token),c={aws:JSON.parse(n.query.aws),dana:s,org:e,isRemote:t,port:a};r.send("Login successful! You can close this window."),await je.store(c,t,a),o(c)}catch(e){r.send("Login failed. Check the CLI for details."),i(e)}})}),i=[10032,10064],s=a?[a].concat(i):i,r=await G({port:s}),c=n.listen(r);return t?(v.info(re["command.auth.remoteMode"]+`Port: ${r}`),v.info(re["command.auth.accessBrowserUrl"]+` ${Je.getAPIURL(e)}/login?port=${r}`)):await J(`${Je.getAPIURL(e)}/login?port=${r}`),o.finally(()=>{c.closeAllConnections(),c.close()})}static async logout(e,t,a){const n=new URL(`${Je.getAPIURL(e)}/logout`);t?.id_token&&n.searchParams.append("id_token",t.id_token),t?.refresh_token&&n.searchParams.append("refresh_token",t.refresh_token),a?(v.info(re["command.auth.remoteMode"]),v.info(re["command.auth.accessBrowserUrl"]+`${n.toString()}`)):await J(n.toString()),await je.delete(e)}static async refresh(e,t,a,n){const o=await fetch(`${Je.getAPIURL(e)}/login?refresh_token=${t}`);if(!o.ok)return Je.login(e,a,n);const i=await o.json(),s={aws:i.aws,dana:{...i.token,refresh_token:t},org:e,isRemote:a,port:n};return await je.store(s,a,n),s}static isTokenExpired(e){const t=W.decode(e);if(!t.exp)return!0;return new Date(1e3*t.exp)<=new Date}static async loginCodeArtifact(e,t){const a=De(t),n=new I({domain:Te,domainOwner:Pe}),{authorizationToken:o,expiration:i}=await a.send(n);if(!o)throw new Error("Authorization token not found");const s={token:o,expiration:i};return await je.updateCodeArtifactToken(e,s),await Je.loginNPM(e,t,s),await Je.loginGradle(e,s),"darwin"===process.platform&&await Je.loginSPM(e,t,s),s}static async loginNPM(e,t,a){const n=De(t),o=await Ne({organization:e,credentials:t,client:n,format:"npm"});k(`npm config set @dana:registry=${o}`),k(`npm config set //${o.replace(/^https?:\/\//,"")}:_authToken=${a.token}`)}static async loginGradle(e,t){const a=Ee(e),n=u.join(process.env.HOME,".gradle");await s(n,{recursive:!0});const r=u.join(n,"gradle.properties"),c=await o(r,"utf-8").catch(()=>""),l=`dana.${a}.token`;let d=!1,p=0;const m=c.split("\n").reduce((e,a,n)=>(a.startsWith(l+"=")?(a=`${l}=${t.token}`,d=!0):""===a&&n>p&&(p=n),e.push(a),e),[]);d||m.splice(p,0,`${l}=${t.token}`),await i(r,m.join("\n"))}static async loginSPM(e,t,a){const n=async function(){const e=u.join(process.env.HOME,"Library","org.swift.swiftpm","configuration"),t=u.join(process.env.HOME,".swiftpm"),a=u.join(process.env.HOME,".swiftpm","configuration","registries.json");return await s(e,{recursive:!0}),await s(t,{recursive:!0}),await c(u.join(t,"configuration")).catch(async()=>await d(e,u.join(t,"configuration"))),await c(a).catch(async()=>await i(a,JSON.stringify({authentication:{},registries:{},version:1},null,2))),a}(),r=De(t),l=await Ne({organization:e,credentials:t,client:r,format:"swift"});await async function(e,t,a){const n=await o(e,{encoding:"utf-8"}),s=JSON.parse(n),r=new URL(a);s.registries[t]={supportsAvailability:!1,url:a},s.authentication[r.host]={loginAPIPath:u.join(r.pathname,"login"),type:"token"},await i(e,JSON.stringify(s,null,2))}(await n,"dana",l),function(e,t){const a=new URL(e),n=A("security",["add-internet-password","-a","token","-l",a.host,"-r","htps","-s",a.host,"-U","-A","-w",t],{encoding:"utf-8"});if(0!==n.status)throw n.stderr.split("\n")[0].includes("User canceled the operation")?new Error("Unable to update password in keychain.\nPlease enter your keychain password when asked to update the token."):n.stderr.split("\n")[0].includes("The specified item already exists in the keychain")?new Error("Unable to update token in keychain.\nDelete current token entry for codeartifact."):new Error("Unable to update password in keychain.\n"+n.stderr.toString())}(l,a.token)}}const We=async()=>{const e=await je.get();try{if(!e.dana?.id_token||!e.dana?.refresh_token)return void await Je.login(e.org,e.isRemote,e?.port);(Je.isTokenExpired(e.dana.id_token)||e.aws&&new Date(e.aws.expiration)<new Date)&&await Je.refresh(e.org,e.dana.refresh_token,e.isRemote,e?.port),(!e.npm?.token||e.npm&&e.npm.expiration<new Date)&&await Je.loginCodeArtifact(e.org,e.aws)}catch(e){y.log.error(e.message),process.exit(6)}},He=async(e,t,a)=>{try{const n=u.join(u.dirname(a),"components",`${e.type}.js.hbs`),o=await Fe(n,e),s=u.resolve(t,$e.normalizeFileName(e.type,e.name));await i(s,o,{encoding:"utf-8"}),y.log.success(`${e.name} ${re["component.creation.success"]}`)}catch(t){const a=`Failed to create component ${e.name}`,n=t instanceof Error?`${a}: ${t.message}`:a;y.log.error(n),process.exit(3)}},Ye=async e=>{const t=process.cwd(),a=$e.getTemplatePath();for(const n of e)await He(n,t,a)},qe=[{value:"MFixedNavigationType",label:D.bold("Fixed. ")+"Focus is fixed on the left side of the list.",command:"fixed"},{value:"MLMRNavigationType",label:D.bold("Left-Middle-Right. ")+"Focus starts on the left side, then moves and stays to the middle, then goes to the right side when the list ends",command:"lmr"},{value:"MLRNavigationType",label:D.bold("Left-Right. ")+" Focus moves in the chosen direction to the edge of the list. Then the list scrolls to show the next items.",command:"lr"},{value:"MPaginateNavigationType",label:D.bold("Paginated. ")+" Focus moves to the end of the visible items. Then, all items are replaced by new ones.",command:"paginated"}],Be=[{value:"HORIZONTAL",label:"HORIZONTAL",command:"h"},{value:"VERTICAL",label:"VERTICAL",command:"v"}],Xe={navigation:"FixedNavigationType",isCyclic:!1,direction:"horizontal",name:"mockRail",itemView:"mockTile",type:ge,addMock:!0},Ke={name:"mockMenu",itemMargin:20,itemView:"ButtonItem",direction:"horizontal",addMock:!0,type:ue},Ze={name:"mock",itemMargin:550,itemView:"Rail",type:we},Qe=class{static askName=async(e,t)=>{if(!t){const a=`${e} name is mandatory !`;t=await y.text({message:`${e} name`,placeholder:"Placeholder Name",validate:e=>Ve(a,e)}),$e.handleCancellation(t)}return t.trim()};static askNumber=async(e,t)=>{const a=`${e} should be a number`,n=`Enter a value for ${e}`,o=t&&Number.isNaN(t)?`${t} is not a number. ${n}`:n;if(!t||Number.isNaN(t)){const e=await y.text({message:o,placeholder:"00",validate:e=>((e,t)=>Number.parseInt(e,10)?"":t)(e,a)});return $e.handleCancellation(e),+e}return t};static askComponentOption=async e=>{const{componentOption:t,optionList:a,optionType:n}=e;let o=$e.checkOption(t,a,n);if(!o){const e=`${be(n)} option`;o=await $e.selectionFactory(n,a,e)}return o};static askCyclic=async e=>{if(!e.cyclic){const e=await y.confirm({message:"Cyclic list ?"});return $e.handleCancellation(e),e}return e.cyclic};static handlePromptCancel=()=>{y.cancel("Operation cancelled."),process.exit(4)};static getDefaultConfig=(e,t)=>({...t,...e,addMock:!e.custom});static railConfig=async e=>{const t=Qe.getDefaultConfig(e,Xe);if(e.custom){const a=await y.group({name:async()=>await this.askName("rail",e.name),navigation:async()=>await this.askComponentOption({componentOption:e.navigation,optionList:qe,optionType:"navigation"}),direction:async()=>await this.askComponentOption({componentOption:e.direction,optionList:Be,optionType:"direction"}),isCyclic:async()=>await this.askCyclic(e),itemView:async()=>await this.askName("itemView",e.itemView)},{onCancel:()=>Qe.handlePromptCancel()});return{...t,...a}}return t};static menuConfig=async e=>{const t=Qe.getDefaultConfig(e,Ke);if(e.custom){const a=await y.group({name:async()=>await this.askName("menu",e.name),itemView:async()=>await this.askName("itemView",e.itemView),itemMargin:async()=>await this.askNumber("item margin",e.itemMargin),direction:async()=>await this.askComponentOption({componentOption:e.direction,optionList:Be,optionType:"direction"})},{onCancel:()=>Qe.handlePromptCancel()});return{...t,...a}}return t};static scrollViewConfig=async e=>{const t=Qe.getDefaultConfig(e,Ze);if(e.custom){const a=await y.group({name:async()=>await this.askName("scrollView",e.name),itemView:async()=>await this.askName("itemView",e.itemView),itemMargin:async()=>await this.askNumber("item margin",e.itemMargin)},{onCancel:()=>Qe.handlePromptCancel()});return{...t,...a}}return t};static screenConfig=async e=>({name:e.name??await this.askName("screen")})},et=async(e,t)=>{const a={...t};e&&(a.name=e);const n=[{...a,...await Qe.menuConfig(a)}];if(a.custom||n.push({type:ye,name:a.itemView??Ke.itemView}),await Ye(n),!a.custom){const e=z.cwd(),t=$e.getTemplatePath(),a=u.join(u.dirname(t),"mocks","menuMock.json"),n=u.resolve(e,"menuMock.json");await r(a,n),await Ge.copyFontFile(e),await Re.updateAppTheme({directory:e},"MockIcons")}},tt=async(e,t)=>{const a={...t};e&&(a.name=e);const n=[{...a,...await Qe.railConfig(a)}];if(a.custom||n.push({type:fe,name:a.itemView??Xe.itemView}),await Ye(n),!a.custom){const e=z.cwd(),t=$e.getTemplatePath(),a=u.join(u.dirname(t),"mocks","railMock.json"),n=u.resolve(e,"railMock.json");await r(a,n)}},at=async(e,t)=>{const a={...t};e&&(a.name=e);const n=[{...a,...await Qe.scrollViewConfig(a)}];if(a.custom||n.push({type:he,name:a.itemView??Ze.itemView},{type:fe,name:"mockTile"}),await Ye(n),!a.custom){const e=z.cwd(),t=$e.getTemplatePath(),a=u.join(u.dirname(t),"mocks","scrollViewMock.json"),n=u.resolve(e,"scrollViewMock.json");await r(a,n)}},nt=async(e,t)=>{try{const a={name:e??"Mock",type:t?.screenType??ve};t?.screenType===ke&&(await et(ke,{}),await at(ke,{})),await Ye([a])}catch(e){y.log.info(`Error creating screen: ${e.message}`)}},ot=e=>{y.outro(D.red(e.message??re["error.common.start.message"])),process.exit(6)},it=async({org:e,port:t,remote:a=!1})=>{const n=await je.get(e);try{let e;n.dana?.id_token&&n.dana?.refresh_token?Je.isTokenExpired(n.dana.id_token)||n.aws&&new Date(n.aws.expiration)<new Date?(y.intro(`Refreshing credentials for organization ${n.org}`),e=await Je.refresh(n.org,n.dana.refresh_token,a,t)):e=n:(y.intro(`Login to organization ${n.org} ...`),e=await Je.login(n.org,a,t)),y.outro(D.green("✓")+" Login successful");const o=await Je.loginCodeArtifact(n.org,e.aws);y.log.step(`Login expires in 12 hours at: ${o.expiration.toLocaleString()}`)}catch(e){ot(e)}};var st;!function(e){e[e.LOGGED=1]="LOGGED",e[e.NOT_LOGGED=2]="NOT_LOGGED",e[e.TOKEN_EXPIRED=3]="TOKEN_EXPIRED"}(st||(st={}));const rt=async()=>{await(async()=>{const e=await je.get();if(!e?.dana?.id_token)return y.log.error(`${re["login.error.not_logged"]} ${re["login.info.please_login"]}`),st.NOT_LOGGED;const t=e.dana.id_token,{given_name:a,family_name:n,email:o,exp:i}=W.decode(t),s=i&&new Date(1e3*i);if(s&&new Date>s)return y.log.error(`${re["login.info.token_expired"]} ${re["login.info.please_login"]}`),st.TOKEN_EXPIRED;const r=s?`\nToken expires on ${s.toUTCString()}`:"";return y.log.success(`${re["login.info.logged"]} ${D.bold(e.org)}: ${D.bold(a+" "+n)} (${o})${r}\n `),st.LOGGED})()},ct=async({org:e})=>{const t=await je.get(e);try{y.intro(`Logout from ${t.org}...`),await Je.logout(t.org,t.dana,t.isRemote),y.outro(D.green("✓")+" Logout successful. You are now being logged out in your browser.")}catch(e){ot(e)}},lt=async e=>{const t=await je.get(e.org);if("android"===e.target)return(async e=>{const t=De(e),a=new I({domain:Te,domainOwner:Pe});try{const e=await t.send(a);console.log(e.authorizationToken)}catch(e){y.log.error(e.message),process.exit(9)}})(t.aws)};class dt{static checkSetup=async()=>{try{await this.executeCheck([{title:"Node version validation",task:async()=>this.validateNodeVersion()},{title:"Git install validation",task:async()=>this.validateGitInstallation()},{title:"AWS install validation",task:async()=>this.validateAwsInstallation()}]),y.log.success(D.green("✔ ")+re["setup.ok"])}catch(e){y.outro(`${D.red(re["setup.ko"])}: ${e.message??""}`),process.exit(8)}};static async executeCheck(e){for(const t of e)await t.task()}static async validateNodeVersion(){return Number.parseInt(process.version.slice(1).split(".")[0],10)>=22?Promise.resolve("Node version ok !"):Promise.reject(new Error(`${re["node.error"]} 22`))}static async validateGitInstallation(){return new Promise((e,t)=>{C("git --version",{},a=>{null!=a&&t(new Error(re["git.error"])),e("Git installation ok !")})})}static async validateAwsInstallation(){return new Promise((e,t)=>{C("aws --version",{},a=>{null!=a&&t(new Error(re["aws.cli.error"])),e("AWS installation ok !")})})}}const pt="modules";var mt;!function(e){e.MENU="menu",e.VOD="vod",e.FIP="fip",e.SETTINGS="settings",e.HOME="home",e.SEARCH="search",e.PLAYER="player"}(mt||(mt={}));const ut=new Set([mt.MENU,mt.VOD,mt.SETTINGS,mt.HOME,mt.SEARCH,mt.PLAYER]),gt=e=>[{title:"Copy template",task:async()=>Ge.copyTemplate(e)},{title:"Update templated files",task:async()=>Re.applyTemplatesToFiles(e)},{title:"Update npmrc",task:async()=>Re.updateNpmrcFile(e)},{title:"Update package.json",task:async()=>Re.updatePackageJson(e)},{title:"Copy app.config files",task:async()=>Ge.copyAppConfigFiles(e)},{title:"Update app.config.json",task:async()=>Re.updateAppConfigFile(e)},{title:"Install dependencies with npm",task:async()=>ze.installDependencies(e)},{title:"Install grunt globally",task:async()=>ze.installGrunt()},{title:"Execute grunt tasks",task:async()=>Ge.executeGruntTasks(e)},{title:"Execute devices config",task:async()=>Re.setDevicesConfig(e)}],ft=e=>({title:"Initialize git",task:async()=>ze.initGit(e)}),wt=e=>[{title:"Copy for tutorial",task:async()=>Ge.copyForTutorial(e)}],ht=async e=>{const t=g(f($e.getTemplatePath()),"modules"),a=await l(t),n=a.some(e=>ut.has(e));return[{title:"Copy modules for templated",task:async()=>Ge.copyForTemplatedApp(e,{availableModules:a,templateDir:t,shouldAddIconFont:n})},{title:"Update app.config with modules list and ",task:async()=>Re.addModuleToAppConfig(e,{modules:a,shouldAddIconFont:n})},{title:"Update appTheme",task:async()=>Re.updateAppTheme(e,"MockIcons"),condition:n},{title:"Set first route",task:()=>_e.setFirstRoute(e)}]},yt=async(e,t)=>{const{devices:a,type:n}=t,o=n===Le.BARE?"":n;y.intro(D.inverse(`Let's create a ${o} Dana application !`)),await dt.checkSetup();const i=await y.group(Ie.getPrompts(e,a),{onCancel:()=>Qe.handlePromptCancel()});i.config=await Ie.getDevicesConfig(i),i.awsProfile=(await je.get()).org;const s=y.spinner();try{s.start(re["task.installation.start"]);const e=await(async(e,t)=>{const a=gt(e);return t===Le.TUTORIAL&&a.push(...wt(e)),t===Le.TEMPLATED&&a.push(...await ht(e)),a.push(ft(e)),a})(i,n);await _e.installation(e,s),s.stop(re["task.installation.end"])}catch(e){const t=e instanceof Error?e.message:"Error";s.stop(t),await p(`./${i.directory}`,{recursive:!0,force:!0}),y.outro(D.red(`${re["error.common.start.message"]}: `)+D.yellow(`\n${i.directory} folder has been removed.`)),process.exit(1)}y.outro(re["creation.final"]),y.intro(D.inverse(re["script.run.title"])),_e.printNpmCommandForUser(i),y.outro("---")},vt="bash",kt="zsh",bt=async e=>{[vt,kt].includes(e)||Ct(`Error: Shell must be either ${vt} or ${kt}`);const t=u.dirname(O(import.meta.url)),a=w(t,"../dana_completion.sh"),n=`.${e}rc`,o=w(S(),n),i=`\n#${"=".repeat(25)}\n# Dana CLI completion`,s=`\nsource "${a}"\n`,r=e===kt?`${i}\nautoload -U compinit\ncompinit ${s}`:`${i}${s}`;try{await m(o,r),y.log.success(D.green("✓")+re["command.completion.success"]+` ${n}`),y.log.info(`Run 'source ~/${n}' or restart your terminal`)}catch(e){Ct(`Failed to install completion: ${e}`)}},Ct=e=>{y.log.error(e),process.exit(7)},At=[".git",".github",".idea","doc","generated","node_modules","dist","build","coverage","templates","tests"];async function $t(){const e=await H(".dana",{type:"directory"}),t=f(e??"").split("/").pop();if(!t)throw new Error("Unable to get your dana project directory name. Please run the command from the root directory of the project");const a=new Y;a.addLocalFolder(`../${t}`,void 0,e=>!function(e){return e.endsWith(".zip")}(e)&&!function(e){return At.some(t=>e.split("/").includes(t))}(e)),a.writeZip(`./archive-${t}-${Date.now()}.zip`)}const Tt=async()=>{y.intro(D.inverse("Creating an uploadable archive !"));const e=y.spinner();try{e.start(re["command.zip.creation.start"]),await $t(),e.stop(re["command.zip.creation.end"])}catch(t){const a=t instanceof Error?t.message:"Error";e.stop(a),y.outro(D.red(`${re["error.common.start.message"]}: `)),z.exit(11)}y.outro(re["command.zip.final"])},Pt=async e=>{const t=await H(".dana",{type:"directory"}),a=f(t);try{await c(g(a,pt,e)),y.log.error(`The ${e} module already exist`),z.exit(12)}catch{try{const t=g(f($e.getTemplatePath()),"modules",e);await r(t,w(a,pt,e),{recursive:!0});const n=e===mt.HOME||e===mt.VOD;if(n){const e=g(f($e.getTemplatePath()),"modules",mt.MENU);await r(e,w(a,pt,mt.MENU),{recursive:!0})}ut.has(e)&&(await Ge.copyFontFile(a),await Re.updateAppTheme({directory:a},"MockIcons"));const o=n?" and menu":"";y.log.success(`The ${e} module has been created\n Don't forget to add ${e}${o} in the desired profile(s)`)}catch(e){y.log.error(`An error occurred : ${e}`),z.exit(12)}}};(await(async()=>{const n=u.dirname(O(import.meta.url)),i=u.join(n,"..","package.json"),s=await o(i,"utf-8"),r=JSON.parse(s),c=new e(r.name);return c.usage(`${re["program.usage"]}`).version(r.version).addCommand((()=>{const t=new e("add-rail");return t.description(re["command.rail.description"]).argument("[name]",re["command.component.name"]).option("--cm, --custom",re["command.component.customization"]).option("-n, --navigation <navigation>",re["command.rail.option.navigation"]).option("-d, --direction <direction>",re["command.rail.option.direction"]).option("-c, --cyclic",re["command.rail.option.cyclic"]).option("-i, --itemView <itemView>",re["command.menu.option.itemView"]).addHelpText("after",`\n${re["help.option.title"]}\n -navigation: ${Ce(qe)} \n -direction: ${Ce(Be)} \n\nMore about rail feature, ${re["help.option.check"]} https://doc.dana-framework.com/docs/vendorcomponents/recycling-listview\n`).action(tt),t})()).addCommand(new e("add-device").description(re["command.device.description"]).hook("preAction",We).action(Ue)).addCommand((()=>{const t=new e("add-menu");return t.description(re["command.menu.description"]).argument("[name]",re["command.component.name"]).option("--cm, --custom",re["command.component.customization"]).option("-i, --itemView <itemView>",re["command.menu.option.itemView"]).option("-m, --itemMargin <itemMargin>",re["command.menu.option.itemMargin"]).option("-d, --direction <direction>",re["command.rail.option.direction"]).action(et),t})()).addCommand((()=>{const a=new e("auth").description(re["command.auth.description"]),n=new e("login").description(re["command.auth.login.description"]).option("-o, --org <organization>",re["command.auth.login.option.org"]).option("-r, --remote",re["command.auth.login.option.remote"]).option("-p, --port <port>",re["command.auth.login.option.port"]).action(it);a.addCommand(n);const o=new e("logout").description(re["command.auth.logout.description"]).option("-o, --org <organization>",re["command.auth.login.option.org"]).action(ct);a.addCommand(o);const i=new e("status").description(re["command.auth.status.description"]).action(rt);a.addCommand(i);const s=new e("get-packages-token").description(re["command.auth.get-packages-token.description"]).addOption(new t("-t, --target <target>","Packages target").makeOptionMandatory(!0).choices(["android"])).option("-o, --org <organization>",re["command.auth.login.option.org"]).hook("preAction",We).action(lt);return a.addCommand(s),a})()).addCommand(new e("add-scrollView").description(re["command.scrollView.description"]).argument("[name]",re["command.component.name"]).option("--cm, --custom",re["command.component.customization"]).option("-i, --itemView <itemView>",re["command.menu.option.itemView"]).option("-m, --itemMargin <itemMargin>",re["command.menu.option.itemMargin"]).action(at)).addCommand((()=>{const t=new e("add-screen");return t.description(re["command.screen.description"]).argument("[name]",re["command.component.name"]).option("-s, --screenType [screenType]",re["command.screen.option.type"]).action(nt),t})()).addCommand((()=>{const a=new e("create-app");return a.description(re["command.create_app.description"]).argument("[projectName]",re["command.create_app.name"]).addOption(new t("-t, --type <type>",re["command.create_app.type"]).choices([Le.BARE,Le.TUTORIAL,Le.TEMPLATED]).default(Le.BARE)).option("-d, --devices",re["command.create_app.devices"]).hook("preAction",We).action(yt),a})()).addCommand((()=>{const t=new e("completion");return t.description(re["command.completion.description"]).argument("<shell>",re["command.completion.argument"]).action(bt),t})()).addCommand((()=>{const t=new e("zip-code");return t.description(re["command.zip.description"]).action(Tt),t})()).addCommand((()=>{const t=new e("add-module");return t.description(re["command.module"]).addArgument(new a("name",re["command.module.name"]).choices(Object.values(mt))).action(Pt),t})()).hook("preSubcommand",async(e,t)=>{await(async(e,t)=>{if(["create-app","auth"].includes(t.name()))return;await H(".dana",{type:"directory"})||(y.log.error(re["command.hook.dana_project"]),process.exit(10))})(0,t)}),c.helpInformation=()=>(e=>{const t=" "+e.commands.map(e=>`${e.name()}: ${e.description()}`).sort((e,t)=>e.localeCompare(t)).join("\n "),a=Object.values(ie).map(e=>e.label).sort((e,t)=>e.localeCompare(t)).join(", ");return`\n DANA\n===================\n\n${re["help.custom.commands"]}\n${t}\n\n\n${re["help.custom.devices"]}\n${a}\n\n`})(c),c.addHelpText("afterAll",`\n${re["program.help.footer"]}\n `),c})()).parse();
2
+ import{Command as e,Option as t,Argument as a}from"commander";import n,{readFile as o,writeFile as i,mkdir as s,cp as r,access as c,readdir as l,symlink as d,rm as p,appendFile as m}from"node:fs/promises";import u,{resolve as g,dirname as f,join as w,sep as h}from"node:path";import*as y from"@clack/prompts";import{log as v}from"@clack/prompts";import{execSync as k,spawn as b,exec as C,spawnSync as A}from"node:child_process";import $ from"@babel/parser";import T from"@babel/traverse";import*as P from"@babel/template";import*as j from"@babel/types";import N from"@babel/generator";import D from"picocolors";import{createRequire as E}from"node:module";import{fileURLToPath as O}from"node:url";import{homedir as S}from"node:os";import{CodeartifactClient as V,GetRepositoryEndpointCommand as x,ListPackageVersionsCommand as _,GetAuthorizationTokenCommand as I}from"@aws-sdk/client-codeartifact";import{maxSatisfying as F}from"semver";import{valid as L,clean as M}from"semver-ts";import R from"handlebars";import z from"node:process";import G from"get-port";import U from"express";import J from"open";import W from"jsonwebtoken";import{findUp as H}from"find-up";import Y from"adm-zip";const q="tvos",B="tizen",X="webos",K="androidtv",Z="css",Q="lightning",ee="vidaa",te="titanos",ae=["@dana/renderer-css","@dana/renderer-lightning-html5","@dana/tools-grunt","@dana/vendor-test","@dana/engine-nodejs","@dana/vendor-components","@wiztivi/dana-templates","@dana/core","@dana/renderer-lightning","@dana/engine-html5","@dana/vendor-fake-native"],ne={[B]:{label:"Samsung",package:"@dana/vendor-tizen",dependencies:["@dana/vendor-tizen","@dana/vendor-shaka","@dana/tools-smarttv-grunt"]},[X]:{label:"LG",package:"@dana/vendor-webos",dependencies:["@dana/vendor-webos","@dana/vendor-shaka","@dana/tools-smarttv-grunt","@dana/vendor-webos-css"]},[ee]:{label:"Vidaa",package:"@dana/vendor-vidaa",dependencies:["@dana/vendor-vidaa","@dana/tools-smarttv-grunt"]},[te]:{label:"TitanOs",package:"@dana/vendor-titanos",dependencies:["@dana/vendor-titanos"]}},oe={[q]:{label:"TV OS",package:"@dana/engine-tvos",dependencies:["@dana/engine-tvos","@dana/renderer-tvos","@dana/tools-apple-grunt"]},[K]:{label:"Android",package:"@dana/engine-android",dependencies:["@dana/engine-android","@dana/renderer-android","@dana/tools-android-grunt"]}},ie={...ne,...oe},se=[Z,Q];const re={"program.usage":"A CLI to help you build dana applications","program.help.footer":"For more information, visit https://doc.dana-framework.com or contact us.","component.creation.success":"component created successfully.","component.creation.error":"Failed to create component","command.rail.description":"Add a rail","command.component.name":"Component's name","command.component.customization":"Full or partial customization. MANDATORY to not use default configuration.","command.rail.option.navigation":"Navigation type. Default: fixed","command.rail.option.direction":"Navigation direction. Default: horizontal","command.rail.option.cyclic":"True if cyclic. Default: false","command.device.description":"Add device(s) to your Dana app","command.device.version.error":"No version found.","command.device.no_selection":"Nothing selected or no device available. Exit","help.custom.devices":"Use the CLI to add the following devices:","help.custom.commands":"Add devices and built-in components to your DANA app:","help.option.title":"Available command options:","help.option.check":"check:","command.menu.description":"Add horizontal menu","command.menu.option.itemView":"Item to be used in the menu","command.menu.option.itemMargin":"Margin between items. Default: 20","helper.git.status.error":"Git status is not clean or an error occurred. Try to commit your change.","command.auth.description":"Authenticate to Dana framework","command.auth.login.description":"Login to Dana","command.auth.logout.description":"Logout from Dana","command.auth.login.option.org":"Organization to login/logout","command.auth.login.option.remote":"Allow connexion to dana authentication stack when working on remote server. Display login url","command.auth.login.option.port":"Provide a port for login server","command.auth.remoteMode":"You are in remote mode. Verify you are forwarding the right port to the host machine.","command.auth.accessBrowserUrl":"You can complete the operation by accessing the following url in the browser.","command.auth.status.description":"Get login status","command.auth.get-packages-token.description":"Get token for packages repository","command.scrollView.description":"Add a scrollView of rails","command.scrollView.option.itemView":"Item to be used in the scrollView. Default: pre-made rail","command.scrollView.option.itemMargin":"Margin between items. Default: 550","command.screen.description":"Add a screen","command.create_app.description":"Create a new Dana app","command.create_app.name":"Project's name","command.create_app.directory":"Project directory","command.create_app.type":"Choose app type","command.create_app.template_type":"Template type","command.create_app.screen_template_type.error":"Error: A template type can only be provided with the 'templated' option","command.create_app.devices":"Add device(s) when creating the project","command.create_app.aws_profile":"Aws profile","command.create_app.aws_repository":'Aws repository provided during registration. Name is XXX in "dana-XXX-repository".',"command.zip.description":"Generate an archive for a Dana app","command.zip.creation.start":"Creation","command.zip.creation.end":"Creation complete.","command.zip.final":"Archive is complete and can be uploaded on dana cloud services platform.","task.installation.start":"Installation","task.installation.end":"Installation complete.","creation.final":"Everything is ready.","script.run.title":"Now you can just run:","git.error":"Git is not installed or not available in the PATH. You must install it to continue.","aws.cli.error":"AWS cli is not installed or not available in the PATH. You must install it to continue.","node.error":"Node version should be >=","setup.ok":"Configuration ok","setup.ko":"Prerequisite not met","device.unavailable":"Enhance your experience by adding device(s) to your subscription:","device.selection":"Press SPACE KEY to select additional device(s).","renderer.selection":"Select AT LEAST one renderer for","input.name":"What is your project's name ?","input.aws.error":"You need to have an AWS account.\nSee:https://dana-framework.com/guides/getting-started#configure-aws-connection","input.aws.profile":"Select your AWS profile","aws.login.error":"Please log to AWS before trying again","error.common.start.message":"An error occurred","command.completion.description":"Install shell completion (bash or zsh)","command.completion.argument":"Shell. Bash or zsh","command.completion.success":"Completion installed to ","login.info.token_expired":"Your token has expired.","login.info.please_login":"Please run 'dana auth login' command","login.error.not_logged":"You're not logged in.","login.info.logged":"You're currently logged in to Dana","command.hook.dana_project":"Can not execute command outside a Dana project: no '.dana' folder found.\nYou can add it to the root of your Dana project if needed","command.settings.description":"Add settings features in the Settings Screen","command.module":"Add a module","command.module.name":"Module's name"},ce=class{static appConfig={};static getConfig(...e){return Promise.resolve({})}static setConfig(...e){return Promise.resolve("Device configuration done")}static updateProfileJsonWithRenderers(e,t){for(const a of t)e={...e,...this.appConfig[a]};return e}},le=class extends ce{static getConfig=async()=>({renderers:[Q,Z]});static setConfig=()=>Promise.resolve("Vidaa configuration done")},de=class extends ce{static getConfig=async()=>({renderers:[Q,Z]});static setConfig=()=>Promise.resolve("TitanOS configuration done")},pe=class extends ce{static appConfig={css:{"template-webos_css":{mixins:["default","webos"],base:{name:"Webos",vendors:["@dana/vendor-components","@dana/vendor-shaka","@dana/vendor-webos-css"]}}},lightning:{"template-webos_lightning":{mixins:["default","webos"],base:{name:"Webos",vendors:["@dana/vendor-components","@dana/vendor-shaka","@dana/vendor-webos-lightning"]}}}};static getConfig=async()=>({renderers:[Z]});static setConfig=async({directory:e,config:t})=>{const a=u.join(e,"profiles","app.config.webos.json"),n=(await o(a)).toString();let s=JSON.parse(n);return s=this.updateProfileJsonWithRenderers(s,t.renderers),await i(a,JSON.stringify(s,null,4)),"Webos configuration done"}},me=class extends ce{static appConfig={css:{"template-tizen_css":{mixins:["default","tizen"],base:{name:"Tizen",vendors:["@dana/renderer-css"]}}},lightning:{"template-tizen_lightning":{mixins:["default","tizen"],base:{name:"Tizen",vendors:["@dana/renderer-lightning-html5"]}}}};static getConfig=async()=>({renderers:[Q]});static setConfig=async({directory:e,config:t})=>{const a=u.join(e,"profiles","app.config.tizen.json"),n=(await o(a)).toString();let s=JSON.parse(n);return s=this.updateProfileJsonWithRenderers(s,t.renderers),await i(a,JSON.stringify(s,null,4)),"Tizen configuration done"}},ue="wtvMenuListView",ge="wtvRailListView",fe="tileView",we="scrollView",he="scrollItemView",ye="ButtonItemView",ve="screen",ke="vod",be=e=>`${e[0].toUpperCase()}${e.substring(1)}`,Ce=e=>e.map(e=>e.command).join(", "),Ae=e=>`${e[0].toUpperCase()}${e.substring(1).toLowerCase()}`,$e=class{static selectionFactory=async(e,t,a)=>{t=t.map(e=>({value:e.value,label:e.label}));const n=await y.select({message:a,options:t});return $e.handleCancellation(e),n};static checkOption=(e,t,a)=>{if(!e)return"";const n=t.find(t=>t.command===e);return n?n.value:(y.log.info(`"${e}" does not match any ${a} option. Please select one below:`),"")};static normalizeFileName=(e,t)=>{const a=be(t);switch(e){case ge:case ue:return`${a}ListView.js`;case fe:return`${a}ListItemView.js`;case we:return`${a}ScrollView.js`;case he:return`${a}ScrollItemView.js`;case ve:case ke:return`${a}Screen.js`;case ye:return`${a}View.js`;default:return`${a}.js`}};static handleCancellation=function(e){y.isCancel(e)&&(y.cancel("Operation cancelled."),process.exit(4))};static getTemplatePath=function(){return E(O(import.meta.url)).resolve("@wiztivi/dana-templates")}},Te="dana",Pe="733912940672";class je{static filePath=u.join(S(),".dana.json");static async getConfig(){try{const e=await o(this.filePath,{encoding:"utf-8"});let t=JSON.parse(e);const a=t.version??1;return 2!=a&&(t=je.migrateConfig(t,a),await i(this.filePath,JSON.stringify(t,null,4))),t}catch{return null}}static migrateConfig(e,t){if(1===t){const t=e;return{lastUsedOrg:t.org,credentials:{[t.org]:{dana:t.dana,aws:t.aws}},isRemote:!1,version:2}}return e}static async store(e,t,a){let n=await this.getConfig();n??={lastUsedOrg:e.org,credentials:{},version:2,isRemote:!1},n.credentials??={},n.lastUsedOrg=e.org,a&&(n.port=a),n.isRemote=t,n.credentials[e.org]={aws:e.aws,dana:e.dana},await i(this.filePath,JSON.stringify(n,null,4))}static async get(e){const t=await this.getConfig(),a=e??t?.lastUsedOrg??"public",n={org:a,isRemote:!1};return t?.credentials?.[a]?.aws&&(n.aws=t?.credentials[a]?.aws),t?.credentials?.[a]?.dana&&(n.dana=t?.credentials[a]?.dana),t?.port&&(n.port=t.port),t?.credentials?.[a]?.codeArtifact&&(n.codeArtifact=t?.credentials[a]?.codeArtifact),n.isRemote=!!t?.isRemote,n}static async delete(e){const t=await this.getConfig();t&&(delete t.credentials[e],await i(this.filePath,JSON.stringify(t,null,4)))}static async updateCodeArtifactToken(e,t){const a=await this.getConfig();a?.credentials&&(a.credentials[e].codeArtifact=t,await i(this.filePath,JSON.stringify(a,null,4)))}}const Ne=async({organization:e,credentials:t,client:a,format:n="npm"})=>{const o=t??(await je.get())?.aws;if(!o)throw new Error("No valid credentials available.");const i=a??De(o),s=Ee(e),r=new x({domain:Te,domainOwner:Pe,repository:s,format:n}),{repositoryEndpoint:c}=await i.send(r);if(!c)throw new Error("Repository endpoint not found");return c},De=e=>new V({region:"eu-central-1",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey,sessionToken:e.sessionToken}}),Ee=e=>`dana-${e}-repository`,Oe=class{static cleanOnError=()=>{try{k("git clean -d -f && git reset --hard head")}catch{throw new Error("Files deletion failed. Delete changes manually.")}return"Changes successfully deleted."};static getInstalledDevices=async e=>{const t=u.join(e,"package.json"),a=(await o(t)).toString(),n=JSON.parse(a),i=[];for(const e of Object.keys(n.scripts))if(e.includes("start:")){const t=e.split(":")[1];i.push(t)}return i};static getAppIcon=()=>Promise.resolve("images/dana.png");static promptTextFactory=async({errorMessage:e,promptMessage:t,validationFunction:a,placeholder:n,initialValue:o})=>{const i=await y.text({message:t,placeholder:n,initialValue:o,validate:t=>a(t,e)});return $e.handleCancellation(i),i};static getLatestPackageVersion=async({packageName:e,repository:t,format:a,namespace:n})=>{let o;try{const i=await je.get(),s=De(i.aws),r=new _({package:e,domain:Te,domainOwner:Pe,repository:t,format:a,namespace:n}),c=await s.send(r),l=c.versions?.map(e=>e.version).filter(Boolean);if(o=F(l,"*"),o)return o;throw new Error(re["command.device.version.error"])}catch(e){throw new Error(e.message??re["error.common.start.message"])}}},Se=class extends ce{static GITIGNORE_PATH=".gitignore";static setConfig=async({directory:e})=>{const t=u.join(e,Se.GITIGNORE_PATH),a=await o(t,{encoding:"utf8"});return await i(t,a+"\n# For TVOS\napp_tvos/build \napp_tvos/libs"),".gitignore updated"};static getConfig=async()=>({jscVersion:await Se.getJscVersion()});static getJscVersion=async()=>await Oe.getLatestPackageVersion({packageName:"jscmodule",repository:"internal-apple-repository",format:"swift",namespace:"dana"})},Ve=(e,t)=>t?.trim().length?"":e,xe=class extends ce{static _askAppId=async()=>{const e=await y.text({message:"What is the appId ?",placeholder:"com.yourname.ui",validate:e=>(e=>e.length?/^[a-zA-Z]\w*(\.[a-zA-Z]\w*)+$/.test(e)?"":"AppId should be in the following format: com.example.myapp !":"AppId is required !")(e)});return $e.handleCancellation(e),e};static _askAppName=async()=>{const e=await y.text({message:"What is the appName ?",placeholder:"Dana",validate:e=>Ve("App name is required !",e)});return $e.handleCancellation(e),e};static _askAppVersion=async()=>{const e=await y.text({message:"What is the app version ?",placeholder:"1.0.0",initialValue:"1.0.0",validate:e=>(e=>e?L(M(e)??"")?"":"App version format should be x.x.x":"App version is required")(e)});return $e.handleCancellation(e),M(e)??""};static _askAppVersionCode=async()=>{const e=await y.text({message:"What is the app version code ?",placeholder:"1000",initialValue:"1000",validate:e=>(e=>e?Number.isInteger(+e)?"":"App version code has to be an Integer":"App version code is required")(e)});return $e.handleCancellation(e),Number(e)};static _getWtvAndroidVersion=async()=>await Oe.getLatestPackageVersion({packageName:"v8-runtime",repository:"internal-android-repository",format:"maven",namespace:"com.dana.androidtv"});static getConfig=async()=>{y.log.info(D.bold("Let's configure Android !"));return{device:await y.group({appId:()=>this._askAppId(),appName:()=>this._askAppName(),appVersion:()=>this._askAppVersion(),appVersionCode:()=>this._askAppVersionCode(),icon:()=>Oe.getAppIcon(),wtvAndroidVersion:()=>this._getWtvAndroidVersion()},{onCancel:()=>{y.cancel("Android configuration cancelled."),process.exit(0)}})}};static setConfig=async({directory:e,config:t})=>(await this.updateProfileJson(e,t),"Android config done");static updateProfileJson=async(e,t)=>{const a=u.join(e,"profiles","app.config.androidtv.json"),n=(await o(a)).toString(),s=JSON.parse(n);return s["base-android"].device={...s["base-android"].device,...t.device},await i(a,JSON.stringify(s,null,4)),"Profile.json updated"}},_e=class{static handleCancellation=function(e){y.isCancel(e)&&(y.cancel("Operation cancelled."),process.exit(0))};static askHtmlRenderers=async e=>{const t=se.map(e=>({value:e,label:e})),a=await y.multiselect({message:`${re["renderer.selection"]} ${e}`,required:!0,options:t});return this.handleCancellation(a),a};static getDeviceConfig=e=>{switch(e){case X:return pe;case B:return me;case q:return Se;case K:return xe;case ee:return le;case te:return de;default:return ce}};static promptUnavailableDevices=e=>{y.log.warn(D.bold(D.yellow(re["device.unavailable"])+"\n"+D.magenta(e.join(", "))))};static promptSelectDevices=async(e,t=!1)=>{const a={message:re["device.selection"],options:e,required:t},n=await y.multiselect(a);return this.handleCancellation(n),n};static installation=async function(e,t){for(const a of e)(null==a.condition||a.condition)&&(t?.message(a.title),await a.task())};static printNpmCommandForUser=function(e){const t=`cd ${e.directory} && npm run`;y.log.info(D.bold("For browser:")),y.log.message(`${t} start`);for(const a of e.selectedDevices){y.log.info(D.bold(`For ${a}: `)),a!==B&&a!==X||y.log.warn(`You MUST update app.config.${a}.json with your own parameters`);const n=e.config[a];if(n?.renderers)for(const e of n.renderers)y.log.message(`${t} start:${a}_${e}`);else y.log.message(`${t} start:${a}`)}};F;static setFirstRoute=async function({directory:e}){const t=u.resolve(e,"scripts","app","ui","screens","SplashScreen.js"),a=await o(t,"utf8"),n=(0,P.default.expression)('this.route("home")'),s=$.parse(a,{sourceType:"module"});T.default(s,{Function(e){j.isObjectProperty(e.parent)&&j.isIdentifier(e.parent.key,{name:"beforeShow"})&&e.traverse({CallExpression(e){const t=e.node.callee;j.isMemberExpression(t)&&j.isIdentifier(t.object,{name:"console"})&&e.replaceWith(n())}})}});const r=N.default(s,{retainLines:!0}).code;return await i(t,r),"first route set"}};class Ie{static getPrompts(e,t){return{name:()=>this.askProjectName(e),directory:({results:e})=>this.getProjectDirectory(e.name),selectedDevices:()=>this.askDevices(t)}}static async askProjectName(e){if(null!=e)return Ie.formatAppName(e);return e=await y.text({message:re["input.name"],placeholder:"Placeholder Name",validate:e=>Ve("Project name is mandatory !",e)}),_e.handleCancellation(e),Ie.formatAppName(e)}static formatAppName(e){return e.trim().split(/[ /]+/).map((e,t)=>0===t?e:be(e)).join("")}static async getProjectDirectory(e){return Promise.resolve(`./${e}`)}static getAvailableDevices(){const e=[],t=[],a=y.spinner();a.start();for(const[a,n]of Object.entries(oe))try{k(`npm view ${n.package} --silent`,{encoding:"utf-8"}),e.push({value:a,label:oe[a].label})}catch{t.push(oe[a].label)}return a.stop("Available devices checked"),Promise.resolve({availableDevices:e,unavailableDevices:t})}static async askDevices(e){let t=this.getBasicDevices();if(!e)return t;const{availableDevices:a,unavailableDevices:n}=await this.getAvailableDevices();if(n.length&&_e.promptUnavailableDevices(n),a.length){const e={message:re["device.selection"],options:a,required:!1};t=t.concat(await y.multiselect(e))}return _e.handleCancellation(t),t}static getBasicDevices(){return Object.keys(ne)}static async getDevicesConfig({selectedDevices:e,awsProfile:t}){const a={};for(const n of e)a[n]=await _e.getDeviceConfig(n).getConfig(t);return a}}async function Fe(e,t){const a=await o(e,"utf-8");return R.compile(a)(t)}var Le;R.registerHelper("ifEqTo",(e,t)=>e===t),R.registerHelper("ifNotEqTo",(e,t)=>e!==t),R.registerHelper("normalizeName",(e,t)=>`${be(e)}${t}`),R.registerHelper("capitalize",e=>be(e)),function(e){e.BARE="bare",e.TUTORIAL="tutorial",e.TEMPLATED="templated"}(Le||(Le={}));const Me={family:"fa-solid-900",path:"styles/fonts/fa-solid-900.ttf"};class Re{static updateAppConfigFile=async function({directory:e}){const t=u.join(e,"profiles"),a=u.join(e,"app.config.json"),o=(await n.readdir(t,{withFileTypes:!0})).filter(e=>!e.isDirectory()).map(e=>u.join("profiles",e.name)),i=(await n.readFile(a)).toString(),s=JSON.parse(i);return s.includes=o,await n.writeFile(a,JSON.stringify(s,null,4)),"Config file(s) updated"};static addModuleToAppConfig=async function({directory:e},{modules:t,shouldAddIconFont:a}){const o=u.join(e,"app.config.json");try{const e=(await n.readFile(o)).toString(),i=JSON.parse(e),s=i.default;s.base??={};const r=s.base;r.modules??=[];const c=r.modules;for(const e of t)c.includes(e)||c.push(e);if(a){s.AppTheme.fonts.MockIcons=Me}return await n.writeFile(o,JSON.stringify(i,null,4)),"App config modules updated"}catch(e){throw new Error(`Failed to parse or update app config: ${e.message}`)}};static updateAppTheme=async function({directory:e},t){const a=u.join(e,"scripts","app","theme","AppTheme.js");let o=await n.readFile(a,"utf-8");return o=o.replace(`${t}: "`,`${t}: "${t}`),await n.writeFile(a,o),"AppTheme statics updated"};static updateNpmrcFile=async function({directory:e}){const t=await je.get(),a=await Ne({organization:t.org,credentials:t?.aws,format:"npm"}),o=u.join(e,".npmrc");return await n.writeFile(o,`@dana:registry=${a}`),"AWS profile updated"};static updatePackageJson=async function({name:e,directory:t,selectedDevices:a,config:o}){const i=u.join(t,"package.json"),s=(await n.readFile(i)).toString(),r=JSON.parse(s);if(e){const t=/[^A-Za-z0-9._-]/g;r.name=e.replaceAll(t,"").toLowerCase()}for(const e of a){const t=o[e];if(t?.renderers)for(const a of t.renderers){const t=`${e}_${a}`;r.scripts[`start:${t}`]=Re.setScriptValue(t)}else r.scripts[`start:${e}`]=Re.setScriptValue(e)}return await n.writeFile(i,JSON.stringify(r,null,4)),"Package updated"};static setDevicesConfig=async function({directory:e,config:t,selectedDevices:a,awsProfile:n}){for(const o of a){const a=t[o];await _e.getDeviceConfig(o).setConfig({directory:e,awsProfile:n,config:a})}return"Configuration done"};static setScriptValue=e=>`grunt serve --profile=template-${e}`;static applyTemplatesToFiles=async function({directory:e,name:t}){const a=u.join(e,"scripts","app"),o=`${i=t,i.trim().split(/[./_\-\\ ]/g).map((e,t)=>0===t?e.toLowerCase():Ae(e)).join("")}`;var i;const s=[{fromPath:u.join(a,"TemplateAppRoutes.js.hbs"),config:{appName:o},toPath:u.join(a,`${o}AppRoutes.js`)},{fromPath:u.join(a,"app.js.hbs"),config:{appName:o},toPath:u.join(a,"app.js")},{fromPath:u.join(e,"tests","unit","app","TemplateAppRoutes.js.hbs"),config:{appName:o},toPath:u.join(e,"tests","unit","app",`${o}AppRoutes.js`)}].concat(Re.getConfigFileOutput(e));for(const e of s){const t=await Fe(e.fromPath,e.config);await n.writeFile(e.toPath,t),await n.unlink(e.fromPath)}return"Files templated"};static getConfigFileOutput=function(e){const t=["gitignoreTemplate.hbs","editorconfigTemplate.hbs","eslintrc.jsonTemplate.hbs","nvmrcTemplate.hbs"],a=[];for(const n of t)a.push({fromPath:u.join(e,n),config:{},toPath:u.join(e,`.${n.replace("Template.hbs","")}`)});return a}}class ze{static installDependencies=({directory:e,selectedDevices:t,config:a})=>{const n=ze.getDependenciesList(t,a);return new Promise((t,a)=>{const o=b("npm",["i","--silent"],{shell:!0,cwd:e});o.on("close",o=>{0===o?ze.installSingleDependency(n,e).then(async()=>{await ze.updateSemver(e),t("Dependencies updated")}).catch(e=>{a(new Error("Dependencies update failed : "+(e.message??re["error.common.start.message"])))}):a(new Error("FAILURE during dependencies installation"))}),o.on("error",e=>{a(new Error(`FAILURE during dependency installation : ${e}`))})})};static getDependenciesList(e,t){let a=[...ae];return e.length&&(a=a.concat(this.getAllDependencies(e,t))),[...new Set(a)]}static installSingleDependency=(e,t)=>new Promise((a,n)=>{const o=b("npm",["i",...e,"--save-dev","--save-exact"],{cwd:t,shell:!0});o.on("close",e=>{0===e?a():n(new Error("FAILURE during dependency installation"))}),o.on("error",e=>{n(new Error(`FAILURE during dependency installation : ${e.message}`))})});static updateSemver=async e=>{const t=u.join(e,"package.json"),a=(await o(t)).toString(),n=JSON.parse(a);n.devDependencies=Object.entries(n.devDependencies).reduce((e,[t,a])=>(e[t]=t.includes("@dana")||t.includes("@wiztivi")?a.replace(/(\d+\.\d+\.)\d$/,"$1x"):a,e),{}),await i(t,JSON.stringify(n,null,4))};static getAllDependencies=function(e,t){let a=[];for(const t of e)a=a.concat(ie[t].dependencies);for(const e in t)if(Object.hasOwn(t,e)){const n=t[e].renderers??[];for(const t of n)a=a.concat(ie[e].renderers?.[t]??"")}return a=a.filter(e=>!!e),a};static installGrunt=function(){return new Promise(e=>{C("npm i -g grunt-cli",{},()=>{e("Grunt installed")})})};static runGitCommand(e,t){return new Promise((a,n)=>{const o=b("git",e,{cwd:t});o.on("close",t=>{0===t?a():n(new Error(`Git ${e[0]} failed`))}),o.on("error",t=>{n(new Error(`Git ${e[0]} failed: ${t.message}`))})})}static initGit=async function({directory:e}){try{return await ze.runGitCommand(["init"],e),await ze.runGitCommand(["add","."],e),await ze.runGitCommand(["commit","-m","Initial commit from Create DANA App"],e),"Git initialized"}catch(e){throw new Error("Git hasn't been initialised: "+e.message)}}}class Ge{static copyTemplate=async function({directory:e}){const t=g(f($e.getTemplatePath()),"app-template");return await s(e,{recursive:!0}),await r(t,e,{recursive:!0}),"Template copied"};static copyForTutorial=async function({directory:e}){const t=g(f($e.getTemplatePath()),"tutorial");return await r(t,e,{recursive:!0}),"Tutorial files copied"};static copyForTemplatedApp=async function({directory:e},{availableModules:t,templateDir:a,shouldAddIconFont:n}){const o=["images"];for(const i of t){await r(w(a,i),w(e,"modules",i),{recursive:!0,filter:e=>{const t=e.replace(w(a,i),"").split(h)[1];return!o.includes(t)}});for(const t of o)try{await c(w(a,i,t)),await r(w(a,i,t),w(e,t),{recursive:!0})}catch{}n&&await Ge.copyFontFile(e)}return"Modules files copied"};static copyFontFile=async function(e){const t=g(f($e.getTemplatePath()),"assets","fonts");await r(w(t,Me.family+".ttf"),w(e,"styles","fonts",Me.family+".ttf"));const a=w(e,"app.config.json"),n=(await o(a)).toString(),s=JSON.parse(n);s.default.AppTheme.fonts.MockIcons=Me,await i(a,JSON.stringify(s,null,4))};static copyAppConfigFiles=async function({selectedDevices:e,directory:t}){const a=g(f($e.getTemplatePath()),"profiles"),n=await l(a);for(const o of n){e.includes(o.split(".")[2])&&await r(w(a,o),w(t,"profiles",o))}return"Config file(s) copied"};static runGruntCommand(e,t){if(0===A("grunt",e,{shell:!0,cwd:t}).status)return Promise.resolve("Grunt task(s) done");throw new Error("Grunt task(s) failed")}static executeGruntTasks=async function({directory:e,selectedDevices:t,config:a}){if(t.includes(K)){const t=await je.get(),n=["copyProjectAndroidTv",`--appUrl=${await Ne({organization:t.org,credentials:t?.aws,format:"maven"})}`,`--organization=${t.org}`],o=["appId","appName","appVersion","appVersionCode","wtvAndroidVersion"];for(const e of o)n.push(`--${e}=${a.androidtv?.device[e]}`);try{await Ge.runGruntCommand(n,e)}catch(e){throw new Error(`Failed to execute grunt tasks: ${e.message}`)}}if(t.includes(q))try{const t=["copyProjectTvos",`--jscVersion=${a.tvos?.jscVersion}`];await Ge.runGruntCommand(t,e)}catch(e){throw new Error(`Failed to execute grunt tasks: ${e.message}`)}return"Grunt task(s) done"}}const Ue=async()=>{y.intro(`Add device(s) to ${u.basename(z.cwd())} !`);const e=y.spinner(),t={};try{t.directory="./",t.awsProfile=(await je.get()).org;const{availableDevices:e,unavailableDevices:a}=await Ie.getAvailableDevices(),n=await Oe.getInstalledDevices(t.directory),o=e.filter(e=>!n.includes(e.value));if(a.length&&_e.promptUnavailableDevices(a),o.length&&(t.selectedDevices=await _e.promptSelectDevices(o)),!t.selectedDevices?.length)return void y.outro(re["command.device.no_selection"]);t.config=await Ie.getDevicesConfig(t)}catch(e){const t=e instanceof Error?e.message:e;y.outro(D.red("An issue occurred :"+t)),z.exit(5)}try{e.start(re["task.installation.start"]),await _e.installation([{title:"Update package.json",task:async()=>Re.updatePackageJson(t)},{title:"Copy app.config files",task:async()=>Ge.copyAppConfigFiles(t)},{title:"Update app.config.json",task:async()=>Re.updateAppConfigFile(t)},{title:"Install dependencies with npm",task:async()=>ze.installDependencies(t)},{title:"Execute grunt tasks",task:async()=>Ge.executeGruntTasks(t)},{title:"Execute devices config",task:async()=>Re.setDevicesConfig(t)}],e)}catch(t){e.stop();const a=t instanceof Error?t.message:t;y.outro(D.red(`${re["error.common.start.message"]} ${a}`)),z.exit(5)}e.stop(re["task.installation.end"]),y.outro("Everything is ready ! Don't forget to commit your change.")};class Je{static getAPIURL(e){return`https://api.${e}.dana-framework.com`}static async login(e,t,a){const n=U();n.disable("x-powered-by");const o=new Promise((o,i)=>{const s=setTimeout(()=>{i(new Error("Login timed out."))},3e5);n.get("/callback",async(n,r)=>{clearTimeout(s);try{if(n.query.error)return r.send("Login failed. Check the CLI for details."),i(new Error(n.query.error));const s=JSON.parse(n.query.token),c={aws:JSON.parse(n.query.aws),dana:s,org:e,isRemote:t,port:a};r.send("Login successful! You can close this window."),await je.store(c,t,a),o(c)}catch(e){r.send("Login failed. Check the CLI for details."),i(e)}})}),i=[10032,10064],s=a?[a].concat(i):i,r=await G({port:s}),c=n.listen(r);return t?(v.info(re["command.auth.remoteMode"]+`Port: ${r}`),v.info(re["command.auth.accessBrowserUrl"]+` ${Je.getAPIURL(e)}/login?port=${r}`)):await J(`${Je.getAPIURL(e)}/login?port=${r}`),o.finally(()=>{c.closeAllConnections(),c.close()})}static async logout(e,t,a){const n=new URL(`${Je.getAPIURL(e)}/logout`);t?.id_token&&n.searchParams.append("id_token",t.id_token),t?.refresh_token&&n.searchParams.append("refresh_token",t.refresh_token),a?(v.info(re["command.auth.remoteMode"]),v.info(re["command.auth.accessBrowserUrl"]+`${n.toString()}`)):await J(n.toString()),await je.delete(e)}static async refresh(e,t,a,n){const o=await fetch(`${Je.getAPIURL(e)}/login?refresh_token=${t}`);if(!o.ok)return Je.login(e,a,n);const i=await o.json(),s={aws:i.aws,dana:{...i.token,refresh_token:t},org:e,isRemote:a,port:n};return await je.store(s,a,n),s}static isTokenExpired(e){const t=W.decode(e);if(!t.exp)return!0;return new Date(1e3*t.exp)<=new Date}static async loginCodeArtifact(e,t){const a=De(t),n=new I({domain:Te,domainOwner:Pe}),{authorizationToken:o,expiration:i}=await a.send(n);if(!o)throw new Error("Authorization token not found");const s={token:o,expiration:i};return await je.updateCodeArtifactToken(e,s),await Je.loginNPM(e,t,s),await Je.loginGradle(e,s),"darwin"===process.platform&&await Je.loginSPM(e,t,s),s}static async loginNPM(e,t,a){const n=De(t),o=await Ne({organization:e,credentials:t,client:n,format:"npm"});k(`npm config set @dana:registry=${o}`),k(`npm config set //${o.replace(/^https?:\/\//,"")}:_authToken=${a.token}`)}static async loginGradle(e,t){const a=Ee(e),n=u.join(process.env.HOME,".gradle");await s(n,{recursive:!0});const r=u.join(n,"gradle.properties"),c=await o(r,"utf-8").catch(()=>""),l=`dana.${a}.token`;let d=!1,p=0;const m=c.split("\n").reduce((e,a,n)=>(a.startsWith(l+"=")?(a=`${l}=${t.token}`,d=!0):""===a&&n>p&&(p=n),e.push(a),e),[]);d||m.splice(p,0,`${l}=${t.token}`),await i(r,m.join("\n"))}static async loginSPM(e,t,a){const n=async function(){const e=u.join(process.env.HOME,"Library","org.swift.swiftpm","configuration"),t=u.join(process.env.HOME,".swiftpm"),a=u.join(process.env.HOME,".swiftpm","configuration","registries.json");return await s(e,{recursive:!0}),await s(t,{recursive:!0}),await c(u.join(t,"configuration")).catch(async()=>await d(e,u.join(t,"configuration"))),await c(a).catch(async()=>await i(a,JSON.stringify({authentication:{},registries:{},version:1},null,2))),a}(),r=De(t),l=await Ne({organization:e,credentials:t,client:r,format:"swift"});await async function(e,t,a){const n=await o(e,{encoding:"utf-8"}),s=JSON.parse(n),r=new URL(a);s.registries[t]={supportsAvailability:!1,url:a},s.authentication[r.host]={loginAPIPath:u.join(r.pathname,"login"),type:"token"},await i(e,JSON.stringify(s,null,2))}(await n,"dana",l),function(e,t){const a=new URL(e),n=A("security",["add-internet-password","-a","token","-l",a.host,"-r","htps","-s",a.host,"-U","-A","-w",t],{encoding:"utf-8"});if(0!==n.status)throw n.stderr.split("\n")[0].includes("User canceled the operation")?new Error("Unable to update password in keychain.\nPlease enter your keychain password when asked to update the token."):n.stderr.split("\n")[0].includes("The specified item already exists in the keychain")?new Error("Unable to update token in keychain.\nDelete current token entry for codeartifact."):new Error("Unable to update password in keychain.\n"+n.stderr.toString())}(l,a.token)}}const We=async()=>{const e=await je.get();try{if(!e.dana?.id_token||!e.dana?.refresh_token)return void await Je.login(e.org,e.isRemote,e?.port);(Je.isTokenExpired(e.dana.id_token)||e.aws&&new Date(e.aws.expiration)<new Date)&&await Je.refresh(e.org,e.dana.refresh_token,e.isRemote,e?.port),(!e.codeArtifact?.token||e.codeArtifact&&e.codeArtifact.expiration<new Date)&&await Je.loginCodeArtifact(e.org,e.aws)}catch(e){y.log.error(e.message),process.exit(6)}},He=async(e,t,a)=>{try{const n=u.join(u.dirname(a),"components",`${e.type}.js.hbs`),o=await Fe(n,e),s=u.resolve(t,$e.normalizeFileName(e.type,e.name));await i(s,o,{encoding:"utf-8"}),y.log.success(`${e.name} ${re["component.creation.success"]}`)}catch(t){const a=`Failed to create component ${e.name}`,n=t instanceof Error?`${a}: ${t.message}`:a;y.log.error(n),process.exit(3)}},Ye=async e=>{const t=process.cwd(),a=$e.getTemplatePath();for(const n of e)await He(n,t,a)},qe=[{value:"MFixedNavigationType",label:D.bold("Fixed. ")+"Focus is fixed on the left side of the list.",command:"fixed"},{value:"MLMRNavigationType",label:D.bold("Left-Middle-Right. ")+"Focus starts on the left side, then moves and stays to the middle, then goes to the right side when the list ends",command:"lmr"},{value:"MLRNavigationType",label:D.bold("Left-Right. ")+" Focus moves in the chosen direction to the edge of the list. Then the list scrolls to show the next items.",command:"lr"},{value:"MPaginateNavigationType",label:D.bold("Paginated. ")+" Focus moves to the end of the visible items. Then, all items are replaced by new ones.",command:"paginated"}],Be=[{value:"HORIZONTAL",label:"HORIZONTAL",command:"h"},{value:"VERTICAL",label:"VERTICAL",command:"v"}],Xe={navigation:"FixedNavigationType",isCyclic:!1,direction:"horizontal",name:"mockRail",itemView:"mockTile",type:ge,addMock:!0},Ke={name:"mockMenu",itemMargin:20,itemView:"ButtonItem",direction:"horizontal",addMock:!0,type:ue},Ze={name:"mock",itemMargin:550,itemView:"Rail",type:we},Qe=class{static askName=async(e,t)=>{if(!t){const a=`${e} name is mandatory !`;t=await y.text({message:`${e} name`,placeholder:"Placeholder Name",validate:e=>Ve(a,e)}),$e.handleCancellation(t)}return t.trim()};static askNumber=async(e,t)=>{const a=`${e} should be a number`,n=`Enter a value for ${e}`,o=t&&Number.isNaN(t)?`${t} is not a number. ${n}`:n;if(!t||Number.isNaN(t)){const e=await y.text({message:o,placeholder:"00",validate:e=>((e,t)=>Number.parseInt(e,10)?"":t)(e,a)});return $e.handleCancellation(e),+e}return t};static askComponentOption=async e=>{const{componentOption:t,optionList:a,optionType:n}=e;let o=$e.checkOption(t,a,n);if(!o){const e=`${be(n)} option`;o=await $e.selectionFactory(n,a,e)}return o};static askCyclic=async e=>{if(!e.cyclic){const e=await y.confirm({message:"Cyclic list ?"});return $e.handleCancellation(e),e}return e.cyclic};static handlePromptCancel=()=>{y.cancel("Operation cancelled."),process.exit(4)};static getDefaultConfig=(e,t)=>({...t,...e,addMock:!e.custom});static railConfig=async e=>{const t=Qe.getDefaultConfig(e,Xe);if(e.custom){const a=await y.group({name:async()=>await this.askName("rail",e.name),navigation:async()=>await this.askComponentOption({componentOption:e.navigation,optionList:qe,optionType:"navigation"}),direction:async()=>await this.askComponentOption({componentOption:e.direction,optionList:Be,optionType:"direction"}),isCyclic:async()=>await this.askCyclic(e),itemView:async()=>await this.askName("itemView",e.itemView)},{onCancel:()=>Qe.handlePromptCancel()});return{...t,...a}}return t};static menuConfig=async e=>{const t=Qe.getDefaultConfig(e,Ke);if(e.custom){const a=await y.group({name:async()=>await this.askName("menu",e.name),itemView:async()=>await this.askName("itemView",e.itemView),itemMargin:async()=>await this.askNumber("item margin",e.itemMargin),direction:async()=>await this.askComponentOption({componentOption:e.direction,optionList:Be,optionType:"direction"})},{onCancel:()=>Qe.handlePromptCancel()});return{...t,...a}}return t};static scrollViewConfig=async e=>{const t=Qe.getDefaultConfig(e,Ze);if(e.custom){const a=await y.group({name:async()=>await this.askName("scrollView",e.name),itemView:async()=>await this.askName("itemView",e.itemView),itemMargin:async()=>await this.askNumber("item margin",e.itemMargin)},{onCancel:()=>Qe.handlePromptCancel()});return{...t,...a}}return t};static screenConfig=async e=>({name:e.name??await this.askName("screen")})},et=async(e,t)=>{const a={...t};e&&(a.name=e);const n=[{...a,...await Qe.menuConfig(a)}];if(a.custom||n.push({type:ye,name:a.itemView??Ke.itemView}),await Ye(n),!a.custom){const e=z.cwd(),t=$e.getTemplatePath(),a=u.join(u.dirname(t),"mocks","menuMock.json"),n=u.resolve(e,"menuMock.json");await r(a,n),await Ge.copyFontFile(e),await Re.updateAppTheme({directory:e},"MockIcons")}},tt=async(e,t)=>{const a={...t};e&&(a.name=e);const n=[{...a,...await Qe.railConfig(a)}];if(a.custom||n.push({type:fe,name:a.itemView??Xe.itemView}),await Ye(n),!a.custom){const e=z.cwd(),t=$e.getTemplatePath(),a=u.join(u.dirname(t),"mocks","railMock.json"),n=u.resolve(e,"railMock.json");await r(a,n)}},at=async(e,t)=>{const a={...t};e&&(a.name=e);const n=[{...a,...await Qe.scrollViewConfig(a)}];if(a.custom||n.push({type:he,name:a.itemView??Ze.itemView},{type:fe,name:"mockTile"}),await Ye(n),!a.custom){const e=z.cwd(),t=$e.getTemplatePath(),a=u.join(u.dirname(t),"mocks","scrollViewMock.json"),n=u.resolve(e,"scrollViewMock.json");await r(a,n)}},nt=async(e,t)=>{try{const a={name:e??"Mock",type:t?.screenType??ve};t?.screenType===ke&&(await et(ke,{}),await at(ke,{})),await Ye([a])}catch(e){y.log.info(`Error creating screen: ${e.message}`)}},ot=e=>{y.outro(D.red(e.message??re["error.common.start.message"])),process.exit(6)},it=async({org:e,port:t,remote:a=!1})=>{const n=await je.get(e);try{let e;n.dana?.id_token&&n.dana?.refresh_token?Je.isTokenExpired(n.dana.id_token)||n.aws&&new Date(n.aws.expiration)<new Date?(y.intro(`Refreshing credentials for organization ${n.org}`),e=await Je.refresh(n.org,n.dana.refresh_token,a,t)):e=n:(y.intro(`Login to organization ${n.org} ...`),e=await Je.login(n.org,a,t)),y.outro(D.green("✓")+" Login successful");const o=await Je.loginCodeArtifact(n.org,e.aws);y.log.step(`Login expires in 12 hours at: ${o.expiration.toLocaleString()}`)}catch(e){ot(e)}};var st;!function(e){e[e.LOGGED=1]="LOGGED",e[e.NOT_LOGGED=2]="NOT_LOGGED",e[e.TOKEN_EXPIRED=3]="TOKEN_EXPIRED"}(st||(st={}));const rt=async()=>{await(async()=>{const e=await je.get();if(!e?.dana?.id_token)return y.log.error(`${re["login.error.not_logged"]} ${re["login.info.please_login"]}`),st.NOT_LOGGED;const t=e.dana.id_token,{given_name:a,family_name:n,email:o,exp:i}=W.decode(t),s=i&&new Date(1e3*i);if(s&&new Date>s)return y.log.error(`${re["login.info.token_expired"]} ${re["login.info.please_login"]}`),st.TOKEN_EXPIRED;const r=s?`\nToken expires on ${s.toUTCString()}`:"";return y.log.success(`${re["login.info.logged"]} ${D.bold(e.org)}: ${D.bold(a+" "+n)} (${o})${r}\n `),st.LOGGED})()},ct=async({org:e})=>{const t=await je.get(e);try{y.intro(`Logout from ${t.org}...`),await Je.logout(t.org,t.dana,t.isRemote),y.outro(D.green("✓")+" Logout successful. You are now being logged out in your browser.")}catch(e){ot(e)}},lt=async e=>{const t=await je.get(e.org);if("android"===e.target)return(async e=>{const t=De(e),a=new I({domain:Te,domainOwner:Pe});try{const e=await t.send(a);console.log(e.authorizationToken)}catch(e){y.log.error(e.message),process.exit(9)}})(t.aws)};class dt{static checkSetup=async()=>{try{await this.executeCheck([{title:"Node version validation",task:async()=>this.validateNodeVersion()},{title:"Git install validation",task:async()=>this.validateGitInstallation()},{title:"AWS install validation",task:async()=>this.validateAwsInstallation()}]),y.log.success(D.green("✔ ")+re["setup.ok"])}catch(e){y.outro(`${D.red(re["setup.ko"])}: ${e.message??""}`),process.exit(8)}};static async executeCheck(e){for(const t of e)await t.task()}static async validateNodeVersion(){return Number.parseInt(process.version.slice(1).split(".")[0],10)>=22?Promise.resolve("Node version ok !"):Promise.reject(new Error(`${re["node.error"]} 22`))}static async validateGitInstallation(){return new Promise((e,t)=>{C("git --version",{},a=>{null!=a&&t(new Error(re["git.error"])),e("Git installation ok !")})})}static async validateAwsInstallation(){return new Promise((e,t)=>{C("aws --version",{},a=>{null!=a&&t(new Error(re["aws.cli.error"])),e("AWS installation ok !")})})}}const pt="modules";var mt;!function(e){e.MENU="menu",e.VOD="vod",e.FIP="fip",e.SETTINGS="settings",e.HOME="home",e.SEARCH="search",e.PLAYER="player"}(mt||(mt={}));const ut=new Set([mt.MENU,mt.VOD,mt.SETTINGS,mt.HOME,mt.SEARCH,mt.PLAYER]),gt=e=>[{title:"Copy template",task:async()=>Ge.copyTemplate(e)},{title:"Update templated files",task:async()=>Re.applyTemplatesToFiles(e)},{title:"Update npmrc",task:async()=>Re.updateNpmrcFile(e)},{title:"Update package.json",task:async()=>Re.updatePackageJson(e)},{title:"Copy app.config files",task:async()=>Ge.copyAppConfigFiles(e)},{title:"Update app.config.json",task:async()=>Re.updateAppConfigFile(e)},{title:"Install dependencies with npm",task:async()=>ze.installDependencies(e)},{title:"Install grunt globally",task:async()=>ze.installGrunt()},{title:"Execute grunt tasks",task:async()=>Ge.executeGruntTasks(e)},{title:"Execute devices config",task:async()=>Re.setDevicesConfig(e)}],ft=e=>({title:"Initialize git",task:async()=>ze.initGit(e)}),wt=e=>[{title:"Copy for tutorial",task:async()=>Ge.copyForTutorial(e)}],ht=async e=>{const t=g(f($e.getTemplatePath()),"modules"),a=await l(t),n=a.some(e=>ut.has(e));return[{title:"Copy modules for templated",task:async()=>Ge.copyForTemplatedApp(e,{availableModules:a,templateDir:t,shouldAddIconFont:n})},{title:"Update app.config with modules list and ",task:async()=>Re.addModuleToAppConfig(e,{modules:a,shouldAddIconFont:n})},{title:"Update appTheme",task:async()=>Re.updateAppTheme(e,"MockIcons"),condition:n},{title:"Set first route",task:()=>_e.setFirstRoute(e)}]},yt=async(e,t)=>{const{devices:a,type:n}=t,o=n===Le.BARE?"":n;y.intro(D.inverse(`Let's create a ${o} Dana application !`)),await dt.checkSetup();const i=await y.group(Ie.getPrompts(e,a),{onCancel:()=>Qe.handlePromptCancel()});i.config=await Ie.getDevicesConfig(i),i.awsProfile=(await je.get()).org;const s=y.spinner();try{s.start(re["task.installation.start"]);const e=await(async(e,t)=>{const a=gt(e);return t===Le.TUTORIAL&&a.push(...wt(e)),t===Le.TEMPLATED&&a.push(...await ht(e)),a.push(ft(e)),a})(i,n);await _e.installation(e,s),s.stop(re["task.installation.end"])}catch(e){const t=e instanceof Error?e.message:"Error";s.stop(t),await p(`./${i.directory}`,{recursive:!0,force:!0}),y.outro(D.red(`${re["error.common.start.message"]}: `)+D.yellow(`\n${i.directory} folder has been removed.`)),process.exit(1)}y.outro(re["creation.final"]),y.intro(D.inverse(re["script.run.title"])),_e.printNpmCommandForUser(i),y.outro("---")},vt="bash",kt="zsh",bt=async e=>{[vt,kt].includes(e)||Ct(`Error: Shell must be either ${vt} or ${kt}`);const t=u.dirname(O(import.meta.url)),a=w(t,"../dana_completion.sh"),n=`.${e}rc`,o=w(S(),n),i=`\n#${"=".repeat(25)}\n# Dana CLI completion`,s=`\nsource "${a}"\n`,r=e===kt?`${i}\nautoload -U compinit\ncompinit ${s}`:`${i}${s}`;try{await m(o,r),y.log.success(D.green("✓")+re["command.completion.success"]+` ${n}`),y.log.info(`Run 'source ~/${n}' or restart your terminal`)}catch(e){Ct(`Failed to install completion: ${e}`)}},Ct=e=>{y.log.error(e),process.exit(7)},At=[".git",".github",".idea","doc","generated","node_modules","dist","build","coverage","templates","tests"];async function $t(){const e=await H(".dana",{type:"directory"}),t=f(e??"").split("/").pop();if(!t)throw new Error("Unable to get your dana project directory name. Please run the command from the root directory of the project");const a=new Y;a.addLocalFolder(`../${t}`,void 0,e=>!function(e){return e.endsWith(".zip")}(e)&&!function(e){return At.some(t=>e.split("/").includes(t))}(e)),a.writeZip(`./archive-${t}-${Date.now()}.zip`)}const Tt=async()=>{y.intro(D.inverse("Creating an uploadable archive !"));const e=y.spinner();try{e.start(re["command.zip.creation.start"]),await $t(),e.stop(re["command.zip.creation.end"])}catch(t){const a=t instanceof Error?t.message:"Error";e.stop(a),y.outro(D.red(`${re["error.common.start.message"]}: `)),z.exit(11)}y.outro(re["command.zip.final"])},Pt=async e=>{const t=await H(".dana",{type:"directory"}),a=f(t);try{await c(g(a,pt,e)),y.log.error(`The ${e} module already exist`),z.exit(12)}catch{try{const t=g(f($e.getTemplatePath()),"modules",e);await r(t,w(a,pt,e),{recursive:!0});const n=e===mt.HOME||e===mt.VOD;if(n){const e=g(f($e.getTemplatePath()),"modules",mt.MENU);await r(e,w(a,pt,mt.MENU),{recursive:!0})}ut.has(e)&&(await Ge.copyFontFile(a),await Re.updateAppTheme({directory:a},"MockIcons"));const o=n?" and menu":"";y.log.success(`The ${e} module has been created\n Don't forget to add ${e}${o} in the desired profile(s)`)}catch(e){y.log.error(`An error occurred : ${e}`),z.exit(12)}}};(await(async()=>{const n=u.dirname(O(import.meta.url)),i=u.join(n,"..","package.json"),s=await o(i,"utf-8"),r=JSON.parse(s),c=new e(r.name);return c.usage(`${re["program.usage"]}`).version(r.version).addCommand((()=>{const t=new e("add-rail");return t.description(re["command.rail.description"]).argument("[name]",re["command.component.name"]).option("--cm, --custom",re["command.component.customization"]).option("-n, --navigation <navigation>",re["command.rail.option.navigation"]).option("-d, --direction <direction>",re["command.rail.option.direction"]).option("-c, --cyclic",re["command.rail.option.cyclic"]).option("-i, --itemView <itemView>",re["command.menu.option.itemView"]).addHelpText("after",`\n${re["help.option.title"]}\n -navigation: ${Ce(qe)} \n -direction: ${Ce(Be)} \n\nMore about rail feature, ${re["help.option.check"]} https://doc.dana-framework.com/docs/vendorcomponents/recycling-listview\n`).action(tt),t})()).addCommand(new e("add-device").description(re["command.device.description"]).hook("preAction",We).action(Ue)).addCommand((()=>{const t=new e("add-menu");return t.description(re["command.menu.description"]).argument("[name]",re["command.component.name"]).option("--cm, --custom",re["command.component.customization"]).option("-i, --itemView <itemView>",re["command.menu.option.itemView"]).option("-m, --itemMargin <itemMargin>",re["command.menu.option.itemMargin"]).option("-d, --direction <direction>",re["command.rail.option.direction"]).action(et),t})()).addCommand((()=>{const a=new e("auth").description(re["command.auth.description"]),n=new e("login").description(re["command.auth.login.description"]).option("-o, --org <organization>",re["command.auth.login.option.org"]).option("-r, --remote",re["command.auth.login.option.remote"]).option("-p, --port <port>",re["command.auth.login.option.port"]).action(it);a.addCommand(n);const o=new e("logout").description(re["command.auth.logout.description"]).option("-o, --org <organization>",re["command.auth.login.option.org"]).action(ct);a.addCommand(o);const i=new e("status").description(re["command.auth.status.description"]).action(rt);a.addCommand(i);const s=new e("get-packages-token").description(re["command.auth.get-packages-token.description"]).addOption(new t("-t, --target <target>","Packages target").makeOptionMandatory(!0).choices(["android"])).option("-o, --org <organization>",re["command.auth.login.option.org"]).hook("preAction",We).action(lt);return a.addCommand(s),a})()).addCommand(new e("add-scrollView").description(re["command.scrollView.description"]).argument("[name]",re["command.component.name"]).option("--cm, --custom",re["command.component.customization"]).option("-i, --itemView <itemView>",re["command.menu.option.itemView"]).option("-m, --itemMargin <itemMargin>",re["command.menu.option.itemMargin"]).action(at)).addCommand((()=>{const t=new e("add-screen");return t.description(re["command.screen.description"]).argument("[name]",re["command.component.name"]).option("-s, --screenType [screenType]",re["command.screen.option.type"]).action(nt),t})()).addCommand((()=>{const a=new e("create-app");return a.description(re["command.create_app.description"]).argument("[projectName]",re["command.create_app.name"]).addOption(new t("-t, --type <type>",re["command.create_app.type"]).choices([Le.BARE,Le.TUTORIAL,Le.TEMPLATED]).default(Le.BARE)).option("-d, --devices",re["command.create_app.devices"]).hook("preAction",We).action(yt),a})()).addCommand((()=>{const t=new e("completion");return t.description(re["command.completion.description"]).argument("<shell>",re["command.completion.argument"]).action(bt),t})()).addCommand((()=>{const t=new e("zip-code");return t.description(re["command.zip.description"]).action(Tt),t})()).addCommand((()=>{const t=new e("add-module");return t.description(re["command.module"]).addArgument(new a("name",re["command.module.name"]).choices(Object.values(mt))).action(Pt),t})()).hook("preSubcommand",async(e,t)=>{await(async(e,t)=>{if(["create-app","auth"].includes(t.name()))return;await H(".dana",{type:"directory"})||(y.log.error(re["command.hook.dana_project"]),process.exit(10))})(0,t)}),c.helpInformation=()=>(e=>{const t=" "+e.commands.map(e=>`${e.name()}: ${e.description()}`).sort((e,t)=>e.localeCompare(t)).join("\n "),a=Object.values(ie).map(e=>e.label).sort((e,t)=>e.localeCompare(t)).join(", ");return`\n DANA\n===================\n\n${re["help.custom.commands"]}\n${t}\n\n\n${re["help.custom.devices"]}\n${a}\n\n`})(c),c.addHelpText("afterAll",`\n${re["program.help.footer"]}\n `),c})()).parse();
@@ -41,7 +41,7 @@ export declare class CredentialsHelper {
41
41
  static get(org?: string): Promise<{
42
42
  aws?: AwsCredentials;
43
43
  dana?: DanaToken;
44
- npm?: CodeArtifactToken;
44
+ codeArtifact?: CodeArtifactToken;
45
45
  org: string;
46
46
  isRemote: boolean;
47
47
  port?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wiztivi/dana-cli",
3
- "version": "0.0.17",
3
+ "version": "0.0.18",
4
4
  "description": "Dana create app CLI",
5
5
  "type": "module",
6
6
  "exports": "./dist/bundle.min.js",
@@ -27,14 +27,14 @@
27
27
  "README.md"
28
28
  ],
29
29
  "dependencies": {
30
- "@aws-sdk/client-codeartifact": "3.1018.0",
30
+ "@aws-sdk/client-codeartifact": "3.1028.0",
31
31
  "@babel/generator": "7.29.1",
32
32
  "@babel/parser": "7.29.2",
33
33
  "@babel/template": "7.28.6",
34
34
  "@babel/traverse": "7.29.0",
35
35
  "@babel/types": "7.29.0",
36
- "@clack/prompts": "1.1.0",
37
- "@wiztivi/dana-templates": "^0.0.17",
36
+ "@clack/prompts": "1.2.0",
37
+ "@wiztivi/dana-templates": "^0.0.18",
38
38
  "child_process": "1.0.x",
39
39
  "command-exists": "1.2.9",
40
40
  "commander": "14.0.x",
@@ -47,7 +47,9 @@
47
47
  "open": "11.0.0",
48
48
  "picocolors": "1.1.x",
49
49
  "semver": "7.7.4",
50
- "semver-ts": "1.0.3"
50
+ "semver-ts": "1.0.3",
51
+ "@types/adm-zip": "0.5.8",
52
+ "adm-zip": "0.5.17"
51
53
  },
52
54
  "devDependencies": {
53
55
  "@rollup/plugin-commonjs": "29.0.2",
@@ -57,7 +59,7 @@
57
59
  "@rollup/plugin-typescript": "12.3.0",
58
60
  "@types/express": "5.0.6",
59
61
  "@types/jsonwebtoken": "9.0.10",
60
- "rollup": "4.60.0",
62
+ "rollup": "4.60.1",
61
63
  "rollup-plugin-node-externals": "8.1.2"
62
64
  }
63
65
  }