UNPKG

@mate-academy/scripts

Version:

Scripts to init, run, test, deploy Mate academy homework projects

4 lines (2 loc) 33.8 kB
#!/usr/bin/env node "use strict";var t=require("commander"),s=require("child_process"),e=require("path"),i=require("fs-extra"),o=require("tree-kill"),r=require("get-port"),n=require("open"),a=require("fs");function c(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var l=c(e),h=c(i),p=c(o),u=c(r),d=c(n);const m="@mate-academy/scripts";var y="2.1.1";function g(t,e=!0,i=process.cwd()){let o={stdio:"ignore"};e&&(o={stdio:"inherit",cwd:i});const r=s.execSync(t,o);return r?r.toString():r}function f(t,s=!0){try{return g(t,s)}catch(t){return process.exit(1)}}const w={shouldBindStdout:!0,cwd:process.cwd()};function S(t,e=w){const{shouldBindStdout:i=!0,cwd:o}=e;return new Promise(((e,r)=>{const n={cwd:o},a=s.exec(t,n);let c="";i&&(a.stdout&&a.stdout.on("data",(t=>{c+=t.toString(),console.log(t)})),a.stderr&&a.stderr.on("data",(t=>{console.error(t)}))),a.on("close",(s=>{s>0?r(new Error(`${t}, errorCode: ${s}`)):e(c)}))}))}function D(t,e=w){const{shouldBindStdout:i=!0,cwd:o}=e;return new Promise(((e,r)=>{const n={cwd:o},a=s.exec(t,n);let c="",l="";a.stdout&&a.stdout.on("data",(t=>{c+=t.toString(),i&&console.log(t)})),a.stderr&&a.stderr.on("data",(t=>{l+=t.toString(),i&&console.error(t)})),a.on("close",(t=>{e({stdout:c,stderr:l,exitCode:t})})),a.on("error",(s=>{r(new Error(`${t}, error: ${s}`))}))}))}function v(t,e=!0,i=process.cwd()){const o={cwd:i},r=s.exec(t,o);return e&&(r.stdout&&r.stdout.on("data",(t=>{console.log(t)})),r.stderr&&r.stderr.on("data",(t=>{console.error(t)}))),r}const j="dist",R={html:!0,bem:!0,styles:!0,javascript:!0,htmlLint:!1,nodeJs:!0},T={jest:!1,backstop:!1,cypress:!1,cypressComponents:!1};var b,L;!function(t){t.None="none",t.Layout="layout",t.LayoutDOM="layoutDOM",t.Javascript="javascript",t.Typescript="typescript",t.React="react",t.ReactTypescript="reactTypescript",t.NodeJs="nodeJs",t.Vue="vue",t.VueTypescript="vueTypescript"}(b||(b={})),function(t){t.v14="14",t.v20="20"}(L||(L={}));function E(t){switch(t){case b.Layout:return R;case b.LayoutDOM:case b.Javascript:case b.Typescript:case b.React:case b.ReactTypescript:return{...R,bem:!1};case b.None:default:return R}}function k(t){switch(t){case b.Layout:return{...T,jest:!0,backstop:!0};case b.LayoutDOM:return{...T,cypress:!0};case b.Javascript:case b.Typescript:return{...T,jest:!0};case b.React:case b.ReactTypescript:return{...T,cypress:!0};case b.None:default:return T}}const P="package.json";function $(){let t=process.cwd(),s=h.default.readdirSync(t);try{for(;!I(s)||!O(t);){if(C(t))throw new Error("Command should be run inside project folder with @mate-academy/scripts as devDependency");t=l.default.join(t,"../"),s=h.default.readdirSync(t)}}catch(s){console.error(null==s?void 0:s.message),t=process.cwd()}return t}function I(t){return t.includes(P)}function O(t){if("test"===process.env.NODE_ENV)return!0;let s;try{const e=h.default.readFileSync(l.default.join(t,P),{encoding:"utf-8"});s=JSON.parse(e)}catch(t){s=null}return s&&s.devDependencies&&s.devDependencies["@mate-academy/scripts"]}function C(t){return"/"===t}function N(t){const{mateAcademy:s,homepage:e}=JSON.parse(h.default.readFileSync(l.default.join(t,"package.json"),{encoding:"utf-8"})),i=s||{},o=i.linters||{},r=i.tests||{},n=((t=b.None)=>({projectType:t,homepage:"",nodejsMajorVersion:L.v14,linters:{...E(t)},tests:{...k(t)}}))(i.projectType);return{...n,homepage:e,...i,linters:{...n.linters,...o},tests:{...n.tests,...r}}}function V(t,s=""){const e=l.default.join(t,s);return h.default.readdirSync(e).flatMap((t=>{const i=l.default.join(e,t),o=h.default.lstatSync(i),r=l.default.join(s,t);return o.isDirectory()?V(i,r):r}))}function x(t){return Object.entries(t).filter((([,t])=>"boolean"!=typeof t||t)).reduce(((t,[s,e])=>`${t} --${s}${"boolean"==typeof e?"":` ${e}`}`),"")}class J{constructor(t){this.versionString=t,this.validate();const[s,e,i]=this.getParts();this.major=s,this.minor=e,this.patch=i}static fromVersionString(t){return new J(t)}static fromVersionList(t){const s=this.getVersionStringFromVersionList(t);return new J(s)}isHigher(t){return!t||(this.major>t.major||!(this.major<t.major)&&(this.minor>t.minor||!(this.minor<t.minor)&&(this.patch>t.patch||(this.patch,t.patch,!1))))}static getVersionStringFromVersionList(t){return(s=t.split("@"))[s.length-1];var s}validate(){const t=this.versionString.split(".");3!==t.length&&J.throwSemverError();t.every((t=>!Number.isNaN(Number(t))))||J.throwSemverError()}getParts(){return this.versionString.split(".").map(Number)}static throwSemverError(){throw new Error("Semver should contain 3 numbers divided with point(.)")}}function H(t,s){return new Promise(((e,i)=>{p.default(t,s,(t=>{t?i(t):e()}))}))}const A=()=>{};var G,M,B,W,_,F,K,U,q,Y;class z{constructor(t){this.logNoImplementationWarning=()=>{console.warn(`No implementation for command ${this.constructor.name} for ${this.config.projectType} project`)},this[G]=this.logNoImplementationWarning,this[M]=this.logNoImplementationWarning,this[B]=this.logNoImplementationWarning,this[W]=this.logNoImplementationWarning,this[_]=this.logNoImplementationWarning,this[F]=this.logNoImplementationWarning,this[K]=this.logNoImplementationWarning,this[U]=this.logNoImplementationWarning,this[q]=this.logNoImplementationWarning,this[Y]=this.logNoImplementationWarning,this.rootDir=t,this.binDir=l.default.join(t,"node_modules/.bin/"),this.config=N(t)}async run(t){this.checkProjectType();try{await this.common(t),await this[this.config.projectType](t)}catch(t){process.exit(1)}}checkProjectType(){this.config.projectType===b.None&&z.logProjectTypeWarning()}static logProjectTypeWarning(){console.warn('package.json should contain\n{\n ...\n "mateAcademy": {\n "projectType": "layout" | "javascript" | "react" | "reactTypescript" | "vue" | "vueTypescript" | "typescript" | "layoutDOM" | "nodeJs"\n }\n}\n')}child(t){return new t(this.rootDir)}}G=b.None,M=b.Layout,B=b.LayoutDOM,W=b.Javascript,_=b.Typescript,F=b.React,K=b.ReactTypescript,U=b.Vue,q=b.VueTypescript,Y=b.NodeJs;class X{constructor(t){return this.rootDir=t,this.configPath=l.default.join(this.rootDir,"./backstopConfig.js"),this.dataDir=l.default.join(this.rootDir,"backstop_data"),this.referencesDir=l.default.join(this.dataDir,"bitmaps_reference"),this.testResultsDir=l.default.join(this.dataDir,"bitmaps_test"),this.binDir=l.default.join(this.rootDir,"node_modules/.bin/"),this.reportDir=this.dataDir,X.instance||(X.instance=this),X.instance}test(t=8080){h.default.existsSync(this.configPath)&&(this.ensureReferences(),this.cleanTestResults(),this.run("test",{config:this.configPath,port:t}))}ensureReferences(){this.areReferencesExists()||this.loadReferences()}areReferencesExists(){return h.default.existsSync(this.referencesDir)}cleanTestResults(){h.default.removeSync(this.testResultsDir)}loadReferences(){h.default.existsSync(this.configPath)&&(this.cleanReference(),this.run("reference",{config:this.configPath}))}cleanReference(){h.default.removeSync(this.referencesDir)}run(t,{port:s,...e}){const i=x(e);g(`cross-env PORT=${s} ${this.binDir}backstop ${t} ${i}`)}}class Q{constructor(t){this.rootDir=t,this.baseOptions={"dist-dir":l.default.join(this.rootDir,j)},this.source=l.default.join(this.rootDir,"src/index.html")}serve(t,s){const{showLogs:e,open:i,port:o}=t,r={...this.baseOptions,open:i,port:o||8080};return this.run("serve",r,"development",e,s)}build(t=!1){const s={...this.baseOptions,"public-url":"./"};return this.run("build",s,"production",t)}run(t,s,e="development",i=!1,o=void 0){const r=x(s),n=`cross-env NODE_ENV=${e} npx parcel ${t} ${Q.escapePathSpaces(this.source)} ${r}`;i&&console.log(n);return(o?v:g)(n,i)}static escapePathSpaces(t){return t.replace(" ","\\ ")}}class Z{constructor(){this.binDir=l.default.join($(),"node_modules/.bin/")}once(){return g(`${this.binDir}jest ./ --runInBand --passWithNoTests`)}watch(){return g(`${this.binDir}jest ./ --watch`)}onceAsync(){return D(`${this.binDir}jest ./ --passWithNoTests`)}}const tt={envs:{local:!0,global:!1},silent:!1},st={isGlobal:!1,versionString:"",silent:!1};class et{constructor(t){this.packageName=t}async ensure({envs:t=tt.envs,silent:s=tt.silent}=tt){const e=await this.getVersions();t.local&&!e.local&&await this.install({silent:s}),t.global&&!e.global&&await this.install({silent:s,isGlobal:!0})}async update({envs:t=tt.envs,silent:s=tt.silent}=tt){const e=await this.getVersions();t.local&&e.available.isHigher(e.local)&&await this.install({silent:s}),t.global&&e.available.isHigher(e.global)&&await this.install({silent:s,isGlobal:!0})}async install({isGlobal:t=st.isGlobal,versionString:s=st.versionString,silent:e=st.silent}=st){let i=s;if(!i){i=(await this.getVersions()).available.versionString}if(!e){const s=await this.getVersions(),e=s[t?"global":"local"];console.log(`Update ${t?"global":"local"} ${this.packageName} from ${(null==e?void 0:e.versionString)||'"none"'} to ${s.available.versionString}`)}await S(`npm i ${t?"-g":""} ${this.packageName}@${i}`,{shouldBindStdout:!1})}async getVersions(){return this.privateVersions||await this.setVersions(),this.privateVersions}async setVersions(){const[t,s,e]=await Promise.all([this.getAvailableVersion(),this.getGlobalVersion(),this.getLocalVersion()]);this.privateVersions={available:t,global:s,local:e}}async getAvailableVersion(){const t=await S(`npm view ${this.packageName} version`);return J.fromVersionString(t)}getGlobalVersion(){return this.getInstalledVersion(!0)}getLocalVersion(){return this.getInstalledVersion()}async getInstalledVersion(t=!1,s=0){try{const e=await S(`npm ls ${t?"-g":""} --deps=${s} ${this.packageName} version`);return J.fromVersionList(e)}catch(t){return null}}}class it{constructor(t){this.rootDir=t,this.binDir=l.default.join(this.rootDir,"node_modules/.bin/")}start(t={},s=!1){const{port:e,open:i=!0,showLogs:o=!1}=t;return(s?v:g)(`cross-env ${e?`PORT=${e} `:""}${i?"":"BROWSER=none "}${this.binDir}react-scripts start`,o)}build(t,s=!0){const e=`cross-env ${t?`BUILD_PATH=./${t} `:""}${this.binDir}react-scripts build`;return s&&console.log(`Execute command: ${e}`),g(e)}}class ot extends z{constructor(t){super(t),this.gitHooksDestinationDir=l.default.join(this.rootDir,"./.git/hooks"),this.layout=async()=>{this.copyGitIgnore(b.Layout),this.copyProjectTypeSpecificConfigs(b.Layout),this.initGitHooks(b.Layout),await this.ensureCrossEnvInstalled()},this.layoutDOM=async()=>{this.copyGitIgnore(b.LayoutDOM),this.copyProjectTypeSpecificConfigs(b.LayoutDOM),this.initGitHooks(b.LayoutDOM),await this.ensureCrossEnvInstalled()},this.javascript=()=>{this.copyGitIgnore(b.Javascript),this.copyProjectTypeSpecificConfigs(b.Javascript),this.initGitHooks(b.Javascript)},this.nodeJs=()=>{this.copyGitIgnore(b.NodeJs),this.copyProjectTypeSpecificConfigs(b.NodeJs),this.initGitHooks(b.NodeJs)},this.react=()=>{this.copyGitIgnore(b.React),this.copyProjectTypeSpecificTemplates(b.React),this.initGitHooks(b.React)},this.reactTypescript=()=>{this.copyGitIgnore(b.ReactTypescript),this.copyProjectTypeSpecificTemplates(b.ReactTypescript),this.initGitHooks(b.ReactTypescript)},this.vue=()=>{this.copyGitIgnore(b.Vue),this.copyProjectTypeSpecificTemplates(b.Vue),this.initGitHooks(b.Vue)},this.vueTypescript=()=>{this.copyGitIgnore(b.VueTypescript),this.copyProjectTypeSpecificTemplates(b.VueTypescript),this.initGitHooks(b.VueTypescript)},this.crossEnvPackageService=new et("cross-env")}common(){this.copyCommonConfigs()}copyCommonConfigs(){const t=l.default.join(ot.configsDir,"common");h.default.copySync(t,this.rootDir)}copyGitIgnore(t){const s=".gitignore";h.default.copySync(l.default.join(ot.configsDir,"gitignoreTemplates",`${s}.${t}`),l.default.join(this.rootDir,s)),console.log(".gitignore copied")}copyProjectTypeSpecificConfigs(t){const s=l.default.join(ot.configsDir,t);h.default.copySync(s,this.rootDir),console.log(`${t} specific configs copied`)}copyProjectTypeSpecificTemplates(t){const s=l.default.join(ot.templatesDir,t),e=V(s).filter((t=>t.endsWith(".template")));e.forEach((t=>{const e=t.replace(/\.template$/,""),i=l.default.join(s,t),o=l.default.join(this.rootDir,e);h.default.mkdirpSync(l.default.dirname(o)),h.default.copyFileSync(i,o)})),console.log(`${t} specific templates copied`)}initGitHooks(t){const s=l.default.join(ot.gitHooksSourceDir,t);h.default.readdirSync(s).forEach((t=>this.initGitHook(s,t))),console.log("Git hooks installed")}initGitHook(t,s){const e=l.default.join(t,s),i=l.default.join(this.gitHooksDestinationDir,s);h.default.copySync(e,i),console.log(`Git ${s} hook installed`)}async ensureCrossEnvInstalled(){await this.crossEnvPackageService.ensure({silent:!1}),console.log("Cross-env installed")}}ot.configsDir=l.default.join(__dirname,"../configs"),ot.gitHooksSourceDir=l.default.join(ot.configsDir,"git-hooks"),ot.templatesDir=l.default.join(ot.configsDir,"templates");class rt{constructor(t){this.rootDir=t,this.binDir=l.default.join(this.rootDir,"node_modules/.bin/")}start(t={},s=!1){const{port:e,open:i=!0,showLogs:o=!1}=t,r=e?` --port=${e}`:"",n=i?" --open":"";return(s?v:g)(`${process.env.CI?"NO_COLOR=true ":""}${this.binDir}vite dev${r}${n}`,o)}build(t,s,e=!0){const i=t?` --out-dir ${t}`:"",o=s?` --base=${s}`:"",r=`cross-env ${this.binDir}vite build${i}${o}`;return e&&console.log(`Execute command: ${r}`),g(r)}}class nt extends z{constructor(){super(...arguments),this.parcel=new Q(this.rootDir),this.jest=new Z,this.reactScripts=new it(this.rootDir),this.vite=new rt(this.rootDir),this.layout=(t,s)=>this.parcel.serve({showLogs:t.shouldShowInternalLogs,open:t.open,port:t.port},s),this.layoutDOM=t=>{this.layout(t)},this.react=(t,s)=>{const e={port:t.port,open:t.open,showLogs:t.shouldShowInternalLogs};return this.config.nodejsMajorVersion===L.v20?this.vite.start(e,s):this.reactScripts.start(e,s)},this.reactTypescript=(t,s)=>this.react(t,s),this.vue=(t,s)=>this.vite.start({port:t.port,open:t.open,showLogs:t.shouldShowInternalLogs},s),this.vueTypescript=(t,s)=>this.vue(t,s),this.javascript=()=>{this.jest.watch()}}common(){}}class at{constructor(t){this.rootDir=t,this.shouldShowLogs=!1,this.shouldRunE2E=!0,this.shouldRunComponents=!1,this.binDir=l.default.join(this.rootDir,"node_modules/.bin/")}async run(t){this.processOptions(t),this.log("CYPRESS RUN CALLED",t);let s=!1,e=0;try{this.log("RUN CYPRESS"),e=await this.runCypress(),this.log("CYPRESS TESTS RUN SUCCESS")}catch(t){this.log("CYPRESS TESTS RUN FAIL",t),s=!0}finally{s&&process.exit(1)}return e}processOptions(t={}){const{showLogs:s=!1,e2e:e=!0,components:i=!1,startServer:o}=t;this.shouldShowLogs=s,this.shouldRunE2E=e,this.shouldRunComponents=i,this.startServer=o}async runCypress(){const t=[];let s=0;if(this.shouldRunE2E){let e;this.startServer&&(e=await this.startServer());try{const t=e?` --config baseUrl=http://localhost:${e.port}`:"",{stdout:i,stderr:o,exitCode:r}=await D(`${this.binDir}cypress run${t}`,{shouldBindStdout:!0});if(0!==r){if(!i.includes("(Run Finished)"))throw new Error(o);s+=r}}catch(s){t.push(s)}finally{e&&e.stop()}}if(this.shouldRunComponents)try{const{stdout:t,stderr:e,exitCode:i}=await D(`${this.binDir}cypress run-ct`,{shouldBindStdout:!0});if(0!==i){if(!t.includes("(Run Finished)"))throw new Error(e);s+=i}}catch(s){t.push(s)}if(t.length)throw t;return s}log(t,s){this.shouldShowLogs&&(s?console.log(t,s):console.log(t))}}class ct{constructor(t){this.rootDir=t,this.shouldOpen=!0,this.shouldShowLogs=!1,this.isReporterStarted=!1,this.reportsDir=l.default.join(this.rootDir,"reports"),this.rawReportsDir=l.default.join(this.rootDir,"raw_reports"),this.mergedReport=l.default.join(this.reportsDir,"report.json"),this.binDir=l.default.join(this.rootDir,"node_modules/.bin/")}async runBeforeTests(t){this.processOptions(t),await this.cleanPrevReports(),await this.makeRawReportsDir(),this.isReporterStarted=!0}async runAfterTests(){this.isReporterStarted||this.log("REPORTER WAS NOT STARTED. SKIP REPORTS PREPARING");let t=!1;try{t=await this.prepareReports()}catch(t){this.log("REPORTS PREPARING FAIL",t)}t&&this.shouldOpen?(this.log("OPEN REPORTS IN BROWSER"),await this.openReportInBrowser()):this.log("SKIP OPEN REPORTS IN BROWSER ACCORDING"),this.isReporterStarted=!1}processOptions(t={}){const{open:s=!0,showLogs:e=!1}=t;this.shouldOpen=s,this.shouldShowLogs=e}async cleanPrevReports(){await Promise.all([this.cleanReports(),this.cleanRawReports()]),this.log("OLD REPORTS REMOVED")}async cleanReports(){await h.default.remove(this.reportsDir)}async cleanRawReports(){await h.default.remove(this.rawReportsDir)}async prepareReports(){return await this.hasRawReports()?(await this.makeReportsDir(),await this.mergeReports(),await this.generateHtmlReport(),await this.cleanRawReports(),this.log("TEST REPORTS PREPARED"),!0):(this.log("REPORTS WAS NOT CREATED DURING TEST RUN. SKIP REPORTS PREPARING"),await this.cleanRawReports(),!1)}async hasRawReports(){if(!await h.default.pathExists(this.rawReportsDir))return!1;return(await h.default.readdir(this.rawReportsDir)).length>0}async makeRawReportsDir(){await h.default.mkdir(this.rawReportsDir)}async makeReportsDir(){await h.default.mkdir(this.reportsDir)}async mergeReports(){const t=l.default.join(this.rawReportsDir,"*.json");await S(`${this.binDir}mochawesome-merge "${t}" > ${this.mergedReport}`,{shouldBindStdout:this.shouldShowLogs})}async generateHtmlReport(){await S(`${this.binDir}marge -o ${this.reportsDir} ${this.mergedReport}`,{shouldBindStdout:this.shouldShowLogs})}async openReportInBrowser(){await d.default(`file://${l.default.join(this.reportsDir,"report.html")}`)}log(t,s){this.shouldShowLogs&&(s?console.log(t,s):console.log(t))}}class lt{constructor(t="/-\\|",s=0,e=null){this.chars=t,this.current=s,this.interval=e}start(){process.stdout.write("[?25l"),this.interval=setInterval((()=>{process.stdout.write(`\r${this.chars[this.current]}`),this.current=(this.current+1)%this.chars.length}),500)}stop(){this.interval&&(clearInterval(this.interval),process.stdout.write("\r \r"),process.stdout.write("[?25h"))}}class ht extends z{constructor(){super(...arguments),this.backstop=new X(this.rootDir),this.jest=new Z,this.cypress=new at(this.rootDir),this.report=new ct(this.rootDir),this.startCommand=this.child(nt),this.spinner=new lt,this.layout=async({showLogs:t})=>{const s=await ht.getPort(),e=this.startCommand.layout({shouldShowInternalLogs:t,open:!1,port:s},!0);if(!e.stdout)throw await H(e.pid),new Error("Unexpected error: child stdout is null");let i=!1;e.stdout.on("data",(async t=>{if(!i&&t.toString().includes("Server running")){i=!0;try{this.backstop.test(s),this.jest.once(),await H(e.pid),process.exit(0)}catch{await H(e.pid),process.exit(1)}}}))},this.layoutDOM=async t=>{const{showLogs:s}=t;this.showLogs=s;const{jest:e}=this.config.tests,i=async()=>{const t=await ht.getPort(),e=this.startCommand.layout({shouldShowInternalLogs:s,open:!1,port:t},!0);if(!e.stdout)throw await H(e.pid),new Error("Unexpected error: child stdout is null");let i=!1;const o=new Promise(((t,s)=>{if(!e.stdout)return void H(e.pid).then((()=>{this.log("CHILD PROCESS KILLED: No stdout")})).catch((t=>{throw this.log("CHILD PROCESS NOT KILLED: No stdout"),t})).finally((()=>{s(new Error("Unexpected error: child stdout is null"))}));const o=s=>{var r;!i&&s.toString().includes("Server running")&&(i=!0,null===(r=e.stdout)||void 0===r||r.off("data",o),t())};e.stdout.on("data",o)})),r=new Promise(((t,s)=>{setTimeout((async()=>{if(i)t();else{try{await H(e.pid),this.log("CHILD PROCESS KILLED: Timeout")}catch(t){this.log("CHILD PROCESS NOT KILLED: Timeout")}s(new Error("Server not started after 30 seconds"))}}),3e4)}));return await Promise.race([o,r]),{port:t,stop:async()=>{try{await H(e.pid),this.log("CHILD PROCESS KILLED: Stop")}catch(t){this.log("CHILD PROCESS NOT KILLED: Stop")}}}};await this.executeWithReportAndSpinner((async()=>{let s=!1;if(e){const{exitCode:t}=await this.jest.onceAsync();s=0!==t}const o=await this.cypress.run({...t,e2e:!0,startServer:i});return s=s||o>0,s?1:0}))},this.startReact=(t,s=!1)=>this.startCommand.react({shouldShowInternalLogs:s,open:!1,port:t},!0),this.startVue=(t,s=!1)=>this.startCommand.vue({shouldShowInternalLogs:s,open:!1,port:t},!0),this.test=async(t,s)=>{this.showLogs=t.showLogs;const{cypress:e,cypressComponents:i}=this.config.tests,o=async()=>{const e=await ht.getPort(),i=s(e,t.showLogs);let o=!1;const r=new Promise(((t,s)=>{if(!i.stdout)return void H(i.pid).then((()=>{this.log("CHILD PROCESS KILLED: No stdout")})).catch((t=>{throw this.log("CHILD PROCESS NOT KILLED: No stdout"),t})).finally((()=>{s(new Error("Unexpected error: child stdout is null"))}));const e=s=>{var r;o||!s.toString().includes("http://localhost")&&!s.toString().includes("Compiled with warnings")||(o=!0,null===(r=i.stdout)||void 0===r||r.off("data",e),t())};i.stdout.on("data",e)})),n=new Promise(((t,s)=>{setTimeout((async()=>{if(o)t();else{try{await H(i.pid),this.log("CHILD PROCESS KILLED: Timeout")}catch(t){this.log("CHILD PROCESS NOT KILLED: Timeout")}s(new Error("Server not started after 30 seconds"))}}),3e4)}));return await Promise.race([r,n]),{port:e,stop:async()=>{try{await H(i.pid),this.log("CHILD PROCESS KILLED: Stop")}catch(t){this.log("CHILD PROCESS NOT KILLED: Stop")}}}};await this.executeWithReportAndSpinner((async()=>{let s=!1;if(e||i){s=await this.cypress.run({...t,e2e:e,components:i,startServer:o})>0}return s?1:0}))},this.react=async t=>{await this.test(t,this.startReact)},this.reactTypescript=async t=>{await this.react(t)},this.vue=async t=>{await this.test(t,this.startVue)},this.vueTypescript=async t=>{await this.vue(t)},this.javascript=()=>{this.jest.once()},this.nodeJs=()=>{this.jest.once()},this.typescript=()=>{this.jest.once()}}common(){}static getPort(){return u.default({port:u.default.makeRange(3001,3999)})}log(...t){this.showLogs&&console.log(...t)}async executeWithReportAndSpinner(t){let s=0;try{await this.report.runBeforeTests(),this.showLogs||this.spinner.start(),s=await t()}catch(t){this.log("TESTS EXECUTION FAILED",t),process.exit(1)}finally{this.spinner.stop(),await this.report.runAfterTests()}s>0&&process.exit(1)}}class pt extends z{constructor(){super(...arguments),this.parcel=new Q(this.rootDir),this.reactScripts=new it(this.rootDir),this.vite=new rt(this.rootDir),this.layout=t=>{h.default.removeSync(l.default.join(this.rootDir,j)),this.parcel.build(t.shouldShowInternalLogs)},this.layoutDOM=t=>{this.layout(t)},this.react=t=>{this.config.nodejsMajorVersion===L.v20?this.buildVite(t.shouldShowInternalLogs):this.buildReactScripts(t.shouldShowInternalLogs)},this.reactTypescript=t=>{this.react(t)},this.vue=t=>{this.buildVite(t.shouldShowInternalLogs)},this.vueTypescript=t=>{this.vue(t)}}common(){}buildReactScripts(t=!1){t&&console.log("START react-scripts build"),this.reactScripts.build(j,t)}buildVite(t=!1){t&&console.log("START vite build"),this.vite.build(j,this.config.homepage,t)}}class ut{constructor(t){this.rootDir=t,this.binDir=l.default.join(this.rootDir,"node_modules/.bin/")}deploy(t="build"){g(`${this.binDir}gh-pages -d ${t}`)}}class dt extends z{constructor(){super(...arguments),this.layout=async()=>{await S("npm i -D @mate-academy/scripts");const t=await this.getPackage();t.scripts=dt.scripts.layout,delete t["lint-staged"],delete t.husky,await a.promises.writeFile(l.default.join(this.rootDir,"package.json"),JSON.stringify({...t,...dt.mateConfig.layout},null,2)),await Promise.all([dt.safeRun(h.default.copy(l.default.join(this.rootDir,"config/backstop/backstopConfig.js"),l.default.join(this.rootDir,"backstopConfig.js"))),dt.safeRun(h.default.remove(l.default.join(this.rootDir,"config"))),dt.safeRun(h.default.remove(l.default.join(this.rootDir,"server.js"))),dt.safeRun(h.default.remove(l.default.join(this.rootDir,".travis.yml"))),dt.safeRun(h.default.remove(l.default.join(this.rootDir,"gulpfile.js"))),dt.safeRun(h.default.remove(l.default.join(this.rootDir,".linthtmlrc")))]),await S("npm rm @linthtml/gulp-linthtml gulp-autoprefixer gulp-clean gulp-eslint gulp-replace-path gulp-sass gulp-sourcemaps gulp-stylelint gulp @mate-academy/browsersync-config htmllint htmllint-cli husky lint-staged rimraf @mate-academy/htmllint-config"),await S("npm i -D @linthtml/linthtml stylelint-scss @mate-academy/linthtml-config node-sass parcel"),await S("npm i")},this.layoutDOM=async()=>{const t=await this.getPackage();await a.promises.writeFile(l.default.join(this.rootDir,"package.json"),JSON.stringify({...t,...dt.mateConfig.layoutDOM},null,2)),await S("npm rm backstopjs @mate-academy/backstop-config gulp-htmllint"),await S(`rm -rf ${l.default.join(this.rootDir,"backstopConfig.js")}`),await S("npm i -D cypress eslint-plugin-cypress mochawesome mochawesome-merge mochawesome-report-generator"),await S(`mkdir ${l.default.join(this.rootDir,"cypress")}`),await S(`mkdir ${l.default.join(this.rootDir,"cypress","integration")}`),await S(`cp ${l.default.join(__dirname,"../","configs","custom",b.LayoutDOM,"page.spec.js")} ${l.default.join(this.rootDir,"cypress","integration","page.spec.js")}`),await S("npm i")},this.javascript=async()=>{await S("npm i -D @mate-academy/scripts");const t=await this.getPackage();t.scripts=dt.scripts.javascript,delete t["lint-staged"],delete t.husky,await a.promises.writeFile(l.default.join(this.rootDir,"package.json"),JSON.stringify({...t,...dt.mateConfig.javascript},null,2)),await dt.safeRun(h.default.remove(l.default.join(this.rootDir,".travis.yml"))),await S("npm rm husky lint-staged"),await S("npm i"),await S(`${this.binDir}eslint ./ --fix`)},this.nodeJs=async()=>{await S("npm i -D @mate-academy/scripts");const t=await this.getPackage();t.scripts=dt.scripts.nodeJs,delete t["lint-staged"],delete t.husky,await a.promises.writeFile(l.default.join(this.rootDir,"package.json"),JSON.stringify({...t,...dt.mateConfig.nodeJs},null,2)),await dt.safeRun(h.default.remove(l.default.join(this.rootDir,".travis.yml"))),await S("npm rm husky lint-staged"),await S("npm i"),await S(`${this.binDir}eslint ./ --fix`)},this.react=A,this.reactTypescript=A,this.vue=A,this.vueTypescript=A}async common(t){await this[t.projectType]()}async getPackage(){const t=await h.default.readFile(l.default.join(this.rootDir,"package.json"),{encoding:"utf-8"});return JSON.parse(t)}static async safeRun(t){try{await t}catch(t){console.error("Migration error",t)}return!0}}dt.scripts={layout:{init:"mate-scripts init",start:"mate-scripts start",lint:"mate-scripts lint","test:only":"mate-scripts test",build:"mate-scripts build",deploy:"mate-scripts deploy",update:"mate-scripts update",postinstall:"npm run update",test:"npm run lint && npm run test:only"},javascript:{init:"mate-scripts init",start:"mate-scripts start",lint:"mate-scripts lint","test:only":"mate-scripts test",update:"mate-scripts update",postinstall:"npm run update",test:"npm run lint && npm run test:only"},nodeJs:{init:"mate-scripts init",start:"mate-scripts start",lint:"mate-scripts lint","test:only":"mate-scripts test",update:"mate-scripts update",postinstall:"npm run update",test:"npm run lint && npm run test:only"}},dt.mateConfig={[b.None]:null,[b.Layout]:{mateAcademy:{projectType:b.Layout}},[b.LayoutDOM]:{mateAcademy:{projectType:b.LayoutDOM}},[b.Javascript]:{mateAcademy:{projectType:b.Javascript}},[b.React]:{mateAcademy:{projectType:b.React}},[b.ReactTypescript]:{mateAcademy:{projectType:b.ReactTypescript}},[b.Vue]:{mateAcademy:{projectType:b.Vue}},[b.VueTypescript]:{mateAcademy:{projectType:b.VueTypescript}},[b.Typescript]:{mateAcademy:{projectType:b.Typescript}},[b.NodeJs]:{mateAcademy:{projectType:b.NodeJs}}};const mt=new t.Command,yt=new class{constructor(){this.rootDir=$()}make(t,s,e=!1){const i=new t(e?process.cwd():this.rootDir);return s?(...t)=>{const e=t.pop(),o=s(e,...t);return i.run(o)}:()=>i.run()}};mt.name("mate-scripts").version(y,"-v --version","output current version"),mt.command("init").description("init linters and configs").action(yt.make(ot)),mt.command("start").option("-l, --logs","show internal commands logs",!1).option("-o, --open","open web browser after start",!0).option("-p, --port <number>","choose port").description("run development server").action(yt.make(nt,(t=>{const{logs:s,open:e,port:i}=t;return{shouldShowInternalLogs:s,open:e,port:i}}))),mt.command("lint [files...]").option("-s, --styles","lint styles only",!1).option("-h, --html","lint html (markup-style) only",!1).option("-m, --mate-htmllint","Mate HTML linter: lint html (markup-style) only",!1).option("-b, --bem","lint html (BEM) only",!1).option("-j, --javascript","lint javascript only",!1).option("-j, --nodeJs","lint nodeJs only",!1).description("lint html, css and js files").action(yt.make(class extends z{constructor(){super(...arguments),this.layout=t=>{const{html:s,bem:e,files:i,styles:o,javascript:r,htmlLint:n}=t,{linters:a}=this.config;s&&a.html&&this.lintHtml(i),n&&a.htmlLint&&this.mateLintHtml(i),e&&a.bem&&this.lintBem(i),o&&a.styles&&this.lintStyles(i),r&&a.javascript&&this.lintJs(i)},this.layoutDOM=t=>{this.layout(t)},this.javascript=t=>{const{javascript:s,files:e}=t;s&&this.lintJs(e)},this.nodeJs=t=>{const{nodeJs:s,files:e}=t;s&&this.lintJs(e)},this.react=t=>{const{javascript:s,styles:e,files:i}=t;e&&this.lintStyles(i),s&&this.lintJs(i)},this.reactTypescript=t=>{this.react(t)},this.vue=t=>{const{javascript:s,styles:e,files:i}=t;e&&this.lintStyles(i),s&&this.lintJs(i)},this.vueTypescript=t=>{this.vue(t)}}common(){}mateLintHtml(t){const s=t?t.join(" "):"./src/";f(`${this.binDir}html-lint ${s}`)}lintHtml(t){const s=t?t.join(" "):"src/**/*.html";f(`${this.binDir}linthtml ${s}`)}lintBem(t){const s=t?t.join(" "):"./src";f(`${this.binDir}bemlint ${s}`)}lintStyles(t){const s=t?t.join(" "):"./src/**/*.css ./src/**/*.scss";f(`${this.binDir}stylelint ${s}`)}lintJs(t){const s=t?t.join(" "):"./src";f(`${this.binDir}eslint --ext .ts,.tsx,.js,.jsx,.vue ${s} --fix`)}},((t,s)=>{const{styles:e,html:i,bem:o,javascript:r,htmlLint:n,nodeJs:a}=t,c=s&&s.length?s:null;return e||i||r||o||n||a?{styles:e,html:i,bem:o,javascript:r,files:c,htmlLint:n,nodeJs:a}:{styles:!0,html:!0,bem:!0,javascript:!0,htmlLint:!0,nodeJs:!0,files:c}}))),mt.command("check:types").description("validates TS types").action(yt.make(class extends z{constructor(){super(...arguments),this.reactTypescript=()=>{this.checkTypes()},this.vueTypescript=()=>{this.checkTypes()},this.typescript=()=>{this.checkTypes()}}common(){}checkTypes(){f(`${this.binDir}tsc --noEmit`)}})),mt.command("test").option("-n, --not-open","should open test report in browser",!1).option("-l, --logs","should log details to console during run",!1).description("run tests").action(yt.make(ht,(t=>{const{"not-open":s,logs:e}=t;return{open:!s,showLogs:e}}))),mt.command("build").description("create production ready build").option("-l, --logs","show internal commands logs",!1).action(yt.make(pt,(t=>{const{logs:s}=t;return{shouldShowInternalLogs:s}}))),mt.command("deploy").option("-l, --logs","show internal commands logs",!1).description("deploy application to gh-pages").action(yt.make(class extends z{constructor(){super(...arguments),this.ghPages=new ut(this.rootDir),this.buildCommand=this.child(pt),this.destinationDir=l.default.join(this.rootDir,j),this.packageDir=l.default.join(this.rootDir,"node_modules","@mate-academy","scripts"),this.deployScriptFile=l.default.join(this.packageDir,"bash-scripts","deploy-layout.sh"),this.backstop=new X(this.rootDir),this.layout=async t=>{await this.setShellRunner();const{shouldShowInternalLogs:s}=t;await this.buildCommand.run({shouldShowInternalLogs:s}),console.log("Start deploy to gh-pages. Please wait, it may take up to minute.\n");try{this.copyHtmlReport(),this.commitBuild(s),this.runDeployBashScript(s),console.log("Successfully deployed to gh-pages!\n")}catch(t){console.error("Deploy error: ",null==t?void 0:t.message,"")}},this.layoutDOM=async t=>{await this.setShellRunner();const{shouldShowInternalLogs:s}=t;await this.buildCommand.run({shouldShowInternalLogs:s}),console.log("Start deploy to gh-pages. Please wait, it may take up to minute.\n");try{this.runDeployBashScript(s),console.log("Successfully deployed to gh-pages!\n")}catch(t){console.error("Deploy error: ",null==t?void 0:t.message,"")}},this.react=()=>{this.ghPages.deploy(j)},this.reactTypescript=()=>{this.react()},this.vue=()=>{this.ghPages.deploy(j)},this.vueTypescript=()=>{this.vue()}}common(){}async setShellRunner(){try{await S("sh --version",{shouldBindStdout:!1}),this.shellRunner="sh"}catch(t){try{await S("bash --version",{shouldBindStdout:!1}),this.shellRunner="bash"}catch(t){try{await S("zsh --version",{shouldBindStdout:!1}),this.shellRunner="zsh"}catch(t){console.error('Deploy skipped\nPlease run deploy in "Git bash" terminal',""),process.exit(0)}}}}copyHtmlReport(){try{h.default.copySync(l.default.join(this.backstop.reportDir),l.default.join(this.destinationDir,"./report"))}catch(t){console.error("Warning: No html_report")}}commitBuild(t){g(`git add ${this.destinationDir} -f`,t),g('git commit -m "make build" --no-verify',t)}runDeployBashScript(t){g(`${this.shellRunner} ${this.deployScriptFile} ${j}`,t,this.rootDir)}},(t=>{const{logs:s}=t;return{shouldShowInternalLogs:s}}))),mt.command("update").description("update @mate-academy/scripts").action(yt.make(class extends z{constructor(t){super(t),this.layout=A,this.layoutDOM=A,this.javascript=A,this.nodeJs=A,this.react=A,this.reactTypescript=A,this.typescript=A,this.vue=A,this.vueTypescript=A,this.mateScriptsPackageService=new et(m)}async common(){await this.updateMateScriptsVersions(),console.log("versions updated successfully"),g(`${this.binDir}mate-scripts init`),console.log("init command success")}async updateMateScriptsVersions(){await this.mateScriptsPackageService.update({envs:{local:!0}})}})),mt.command("migrate <type>").description("(global) migrate project to @mate-academy/scripts").action(yt.make(dt,((t,s)=>({projectType:s})),!0)),mt.parse(process.argv);