@tomjs/create-app
Version:
create tomjs web app
27 lines (22 loc) • 13.9 kB
JavaScript
import qe from"meow";import w from"node:fs";import Ae from"node:os";import g from"node:path";import{fileURLToPath as Pe}from"node:url";import{copySync as Ee,mkdirSync as $e,readFileSync as ie,readJsonSync as ae,rmSync as N,writeFileSync as T,writeJsonSync as Se}from"@tomjs/node";import x from"chalk";import{camelCase as De,cloneDeep as ce,merge as Ce}from"lodash-es";import B from"node:os";import X from"node:path";import{mkdirSync as be,readJsonSync as ve,writeJsonSync as ke}from"@tomjs/node";import O from"chalk";import je from"inquirer";import he from"@tomjs/logger";import{$ as xe}from"execa";import $ from"inquirer";var d=new he({directory:"create-app/logs"});function S(e,t,o){let s={message:e};return Array.isArray(t)||typeof t!="object"?s.default=t:Object.assign(s,t),Object.assign(s,o)}async function h(e,t){let o=`confirm-${Date.now()}`,s=S(e,t);return(await $.prompt([{type:"confirm",name:o,...s}]))[o]}async function V(e,t){let o=`input-${Date.now()}`,{required:s,...r}=S(e,t),i=s??!0,a=r.validate;return(i||a)&&(r.validate=n=>{let m=(n||"").trim();return i&&!m?"It's required!":a?a(n):!0}),(await $.prompt([{type:"input",name:o,...r}]))[o]}async function K(e,t,o){let s=`checkbox-${Date.now()}`,{required:r,min:i,max:a,...p}=S(e,o),n=r??!0,m=p.validate;return(n||i||a)&&(p.validate=c=>n&&c.length===0?"Please select at least one option":i&&c.length<i?`Please select at least ${i} options`:a&&c.length>a?`Please select at most ${a} options`:m?m(c):!0),(await $.prompt([{type:"checkbox",name:s,choices:t,pageSize:Math.min(t.length,10),...p}]))[s]}async function v(e,t,o){let s=`list-${Date.now()}`,r=S(e,o);return(await $.prompt([{type:"list",name:s,choices:t,pageSize:Math.min(t.length,10),...r}]))[s]}var F={cwd:"."};async function D(e,t){let{trim:o,...s}=Object.assign({stdio:"pipe",shell:!0,cwd:F.cwd},t);s.cwd??=F.cwd,Array.isArray(e)&&(e=e.join(" "));let r=i=>{F.verbose?d.debug(i):d.write(i)};r(`$ ${e}`);try{let a=(await xe(s)`${e}`).stdout;return r(a),o?a.trim().replace(/\n|\r/g,""):a.trim()}catch(i){let a=i.stderr||i.message;throw r(a),new Error(a)}}function Q(e){if(e&&e.packageManager)return e.packageManager.split("@")[0];let t=process.env.npm_config_user_agent;return t?t.split(" ")[0].split("/")[0]:void 0}var Y={repo:"https://github.com",owner:B.userInfo().username};function q(e){let{repo:t,owner:o}=e;return`${t}${t.endsWith("/")?"":"/"}${o}`}async function H(e){let t=await je.prompt([{type:"text",name:"repo",message:`${e?.repo?"Edit":"Add"} git repository url:`,default:e?.repo||Y.repo},{type:"text",name:"owner",message:`${e?.owner?"Edit":"Add"} git repository owner:`,default:e?.owner||Y.owner}]);return{id:e?.id||Date.now().toString(),repo:t.repo,owner:t.owner}}var Z=X.join(B.homedir(),".tomjs","create-app"),ee=X.join(Z,"config.json");function C(){return be(Z),Object.assign({gitRepos:[]},ve(ee))}function Oe(e){let t=C();t.createTime?t.updateTime=Date.now():t.createTime=Date.now(),ke(ee,Object.assign(t,e))}function G(e){Oe({gitRepos:e})}async function k(e,t=!1){e||(e=C().gitRepos||[]);let o=e.map(p=>({name:q(p),value:p.id})).concat([{name:O.green("+ Add"),value:"add"},{name:O.yellow("\u2190 Exit"),value:"exit"}]),s=await v(e.length===0?"You choose to add or exit:":"You can choose the repository to be operated, or choose to add or exit:",o);if(s==="exit")return;if(s==="add"){let p=await H();return e.push(p),G(e),k(e,t)}let r=e.findIndex(p=>p.id===s),i=await v("Select an action?",[{name:O.green("^ Edit"),value:"edit"},{name:O.red("- Remove"),value:"remove"},{name:O.yellow("\u2190 Exit"),value:"exit"}]);if(i==="exit")return t?k(e,t):C();if(i==="remove")return await h("Where confirm to remove?",!0)&&(e.splice(r,1),G(e)),k(e,t);let a=await H(e[r]);return e[r]=a,G(e),k(e,t)}async function te(){let e=C(),{gitRepos:t}=e;return t.length===0&&(console.log(`You need to set the ${O.blue("git remote repository")} for the first time.`),await k(t)),e}var b={},Te=g.join(Pe(import.meta.url),"../../"),pe=g.join(Te,"templates"),Ie=ae(g.join(pe,"config.json"))||{},A=Ie.list||[],I=A.map(e=>e.variants?e.variants.map(t=>({parent:e,...t})):[]).flat(),Re=A.map(e=>e.variants&&e.variants.map(t=>t.name)||[e.name]).reduce((e,t)=>e.concat(t),[]),Ne=["name","version","displayName","description","private","type","engines","packageManager","keywords","categories","author","publisher","contributors","homepage","bugs","license","files","bin","main","module","types","exports","publishConfig","repository","activationEvents","contributes","badges","icon","vsce","scripts","dependencies","devDependencies","peerDependencies","optionalDependencies"],Ue=["dev","dev:","debug","start","build","build:","release","clean","preview","test","lint","lint:eslint","lint:stylelint","lint:prettier"];async function ne(e){return D(`git config --get ${e}`,{trim:!0})}var se=e=>/^(?:(@[a-z0-9.+-]+(-[a-z0-9.+-]+)*\/)?([a-z0-9][a-z0-9._-]*[a-z0-9]))$/.test(e),Fe=e=>/^[a-z0-9._-]+$/.test(e),_e=e=>e.includes("/")?e.substring(e.indexOf("/")+1):e;function R(e){return ae(g.join(e,"package.json"))||{}}function M(e,t){Se(g.join(e,"package.json"),t)}function re(e,t,o){let s={},r=Object.keys(e);{let n=r.filter(u=>u.includes(":"));r=r.filter(u=>!u.includes(":")).sort().concat(n)}let i=p(t),a=p(o);return i.concat(r.filter(n=>!i.includes(n)&&!a.includes(n))).concat(a).forEach(n=>{s[n]=e[n]}),s;function p(n){let m=Array.isArray(n)?n:[];return m.length>0&&(m=m.reduce((u,c)=>{if(c.endsWith(":")){let l=r.filter(y=>!u.includes(y)&&y.startsWith(c));return u.concat(l)}return r.includes(c)?u.concat(c):u},[])),m}}async function le(e){if(Object.assign(b,e),b.git){await k(void 0,!0);return}b.type==="project"?await Le():await ue(b.type)}async function Le(){let e="project",t=await me(e,b.cwd),{projectDir:o}=t.userOptions;W(e,t),await J(t);let{workspaces:s}=t,r=g.join(o,"packages");s&&await h("Do you want to create an package?")&&await ue("package",r);let{examples:i}=t,a=Array.isArray(i)&&i.length>0;if(a){let n=I.filter(u=>i.includes(u.name)),m=g.join(o,"examples");for(let u of n){let c=ce(u);c.templates=fe(c.templates).concat(c.name);let l=g.join(m,c.name);c.userOptions={projectDir:l,pkgName:c.name,gitUserUrl:"",textVars:{}},await ge(c),W("example",c),await J(c)}}let p=w.existsSync(r)&&w.readdirSync(g.join(o,"packages")).length>0;if(p||a){let n=R(o),m=Q(n)||"npm",u=[];if(p&&u.push("packages"),a&&u.push("examples"),m==="pnpm"){let l=g.join(o,"pnpm-workspace.yaml");w.existsSync(l)||T(l,`packages:
${u.map(y=>` - '${y}/*'
`)}`)}else n.workspaces??=u.map(l=>`"${l}/*"`),M(o,n);let c=n.devDependencies??{};p&&"vitest"in c&&T(g.join(o,"vitest.config.ts"),'export default ["packages/*"];')}await Ge(t)}async function ue(e,t){let o=!0,s=0;for(;o;){s++;let r=await me(e,t||b.cwd,s===1?b.template:void 0),{pkgName:i}=r.userOptions;W(e,r),await J(r),d.success(`${x.green(i)} created successfully!`),o=await h(`Do you want to create another ${e}?`)}}function oe(e,...t){!Array.isArray(t)||t.length===0||t.forEach(o=>{let s=g.join(e,o);w.existsSync(s)&&N(s)})}function Ve(e,t,o){if(!w.existsSync(e))return;let s=ie(e);s=s.replaceAll(t,o),T(e,s)}function Qe(e,t,o){let s=o.userOptions,{textVars:r}=s;if(["scripts","dependencies","devDependencies","peerDependencies"].forEach(i=>{e[i]&&(i==="scripts"?e[i]=re(e[i],Ue,["prepare"]):e[i]=re(e[i]))}),t==="example"?(delete e.types,delete e.exports,delete e.publishConfig,delete e.repository,delete e.module,delete e.license,e.private=!0):t==="package"&&(e.repository&&typeof e.repository=="object"&&(e.repository.directory=`packages/${r.pkgShortName}`),e.scripts&&Object.keys(e.scripts).forEach(i=>{i.startsWith("lint")&&delete e.scripts[i]})),!o.test){let i=e.scripts;if(i){delete i.test;{let a=i["lint:eslint"]||"";a&&(i["lint:eslint"]=a.replace(",test",""))}}delete e.devDependencies?.vitest}e.type==="commonjs"&&delete e.type,e.private===!1&&delete e.private,o.userOptions.gitUserUrl||delete e.repository}function W(e,t,o){let s=o??t.userOptions.projectDir,r=g.join(s,".temp");$e(r);let i=t.templates||[],a=[];e==="package"?a=["base/package"]:e==="project"&&(a=["base/core"]);let p=a.concat(i);if(p.forEach((n,m)=>{let u=g.join(pe,n);if(!w.existsSync(u))return;Ee(u,s);let c=R(r)||{},l=Ce(c,R(s));if(m===p.length-1){let y={};Ne.forEach(j=>{j in l&&(y[j]=l[j],delete l[j])}),l=Object.assign(y,l),Qe(l,e,t),M(s,l)}else M(r,l)}),N(r),t.test||(oe(s,"test"),Ve(g.join(s,"tsconfig.json"),', "test"',"")),e==="package"){let n=[".vscode"].concat(t.packages?.exclude??[]);oe(s,...n)}}async function J(e,t){let o=e.userOptions,{textVars:s}=o,r=t??o.projectDir;w.readdirSync(r).filter(p=>p.startsWith("_")).forEach(p=>{w.renameSync(g.join(r,p),g.join(r,p.replace("_",".")))});let i=R(r),a=["LICENSE","README.md","README.zh_CN.md"];!i.publishConfig&&!i.publisher&&a.forEach(p=>{N(g.join(r,p))}),["package.json",...a].forEach(p=>{let n=g.join(r,p);if(!w.existsSync(n))return;let m=ie(n);Object.keys(s).forEach(u=>{m=m.replace(new RegExp("{{"+u+"}}","g"),s[u])}),T(n,m)})}async function me(e,t,o){let{gitRepos:s}=await te(),r=(b.name||"").trim(),i=se(r);(!r||!i)&&(r=await V("Package name:",{validate:c=>se(c)?!0:"Please input a valid name!"}));let a=_e(r);a!==r&&(a=await V("Project name:",{default:a,validate:c=>Fe(c)?!0:"Please input a valid name!"})),d.debug("projectName:",a);let p=g.join(t,a);w.existsSync(p)&&await h("Project already exists, overwrite?")&&N(p),d.debug("projectDir:",p);let n;o&&Re.includes(o)&&(n=I.find(c=>c.name===o));let m=async c=>{let l=await v(!n&&c?`${x.red(c)} is invalid, please select a framework again:`:"Select a framework:",A.filter(f=>!f.variants||!f.variants.length?!1:e==="package"?f.variants.filter(E=>!E.packages?.ignore).length>0:!0).map(f=>({name:f.display,value:f.name})));d.debug("framework:",l);let y=A.find(f=>f.name===l)?.variants||[],j=e==="package"?y.filter(f=>!f.packages?.ignore).map(f=>{let E=f.parent;return E?{name:`${E.display} > ${f.display}`,value:f.name}:{name:f.display,value:f.name}}):y.map(f=>({name:f.display,value:f.name})),we=A.find(f=>f.name===l)?.display,U=await v(`Select a ${x.blue(we)} variant:`,j.concat([{name:x.yellow("\u2190 Back"),value:"_"}]));d.debug("variant:",U),U==="_"?await m(c):n=y.find(f=>f.name===U)};if(n||await m(o),!n)throw new Error("variant not found");n=ce(n),n.templates||(n.templates=[]),n.templates=fe(n.templates).concat(n.name),d.debug("variant:",n),e==="project"&&Array.isArray(n.examples)&&(n.examples=await K("Select examples:",n.examples.map(c=>{let l=I.find(y=>y.name===c);if(!l)throw new Error(`Can't find example ${x.yellow(c)} from ${x.blue(n.name)}`);return{name:l.display,value:l.name,checked:!0}}),{required:!1}));let u="";if(e==="example"?(n.devDependencies=0,n.workspaces=!1,n.test=!1):e==="package"?n.workspaces=!1:n.git&&(u=await v("Which git repository do you want to choose?",s.map(c=>{let l=q(c);return{name:l,value:l}}).concat([{name:x.yellow("None"),value:""}]))),n.devDependencies===2){let c=await h("Whether it will be used for devDependencies?");n.devDependencies=c?1:0}return n.test&&(n.test=await h("Whether to use test?")),n.userOptions={pkgName:r,projectDir:p,gitUserUrl:u},await ge(n),n}function fe(e){let t=[],o=async s=>{if(!(!Array.isArray(s)||!s.length))for(let r of s){if(r.startsWith("base/")){t.push(r);continue}let i=I.find(a=>a.name===r);if(!i){d.warning(`Can't find template ${x.yellow(r)}`);continue}o(i.templates)}};return o(e),[...new Set(t)]}async function ge(e){let{pkgName:t,gitUserUrl:o}=e.userOptions,s=await ne("user.name"),r=await ne("user.email"),i={name:s||Ae.userInfo().username,email:r||""},a=["pnpm","yarn","npm"].map(c=>`# ${c}
${c} add ${t}${e.devDependencies?" -D":""}`).join(`
`),p={pkgName:t,pkgShortName:t.startsWith("@")?t.split("/")[1]:t,pkgInstall:a,gitUserName:i.name,gitUserEmail:i.email,gitOrg:m(),gitUrl:n(),gitFullUrl:u(),gitFullSSHUrl:u(),dateYear:new Date().getFullYear()};function n(){return`${o||`https://github.com/${m()}`}/${t.substring(t.indexOf("/")+1)}`}function m(){return o?o.substring(o.lastIndexOf("/")+1):t.startsWith("@")?t.split("/")[0].substring(1):De(i.name)}function u(c=!1){let l=o||`https://github.com/${m()}`;return c&&(l=l.replace(/http(s):\/\//g,"git@").replace(/\//,":")),`${l}/${t.substring(t.indexOf("/")+1)}.git`}return e.userOptions.textVars=p,p}async function Ge(e){let{gitUserUrl:t,textVars:o,projectDir:s}=e.userOptions;t&&(await D("git init",{cwd:s}),await D(`git remote add origin ${o.gitFullSSHUrl}`,{cwd:s}));let r=process.cwd(),i=g.relative(r,s);console.log(`
Done. Now run:
`),s!==r&&console.log(` cd ${i.includes(" ")?`"${i}"`:i}`);let a=Q()||"npm";switch(a){case"yarn":console.log(" yarn"),console.log(" yarn dev");break;default:console.log(` ${a} install`),console.log(` ${a} run dev`);break}}var z=qe(`
Usage
$ create-app [name] [options]
name The package name
Options
--cwd The current working directory (default: ".")
-e, --example Only create examples
-p, --package Only create packages
--git Only manage git repository
--verbose Display verbose output
-h, --help Display this message
-v, --version Display version number
Examples
$ create-app my-project
$ create-app my-project --template=vue
`,{importMeta:import.meta,booleanDefault:void 0,helpIndent:0,flags:{cwd:{type:"string"},verbose:{type:"boolean",default:process.env.NODE_ENV==="development"},example:{shortFlag:"e",type:"boolean"},package:{shortFlag:"p",type:"boolean"},h:{type:"boolean",default:!1},v:{type:"boolean",default:!1}}}),{input:de,flags:P}=z;if(P.h)z.showHelp(0);else if(P.v)z.showVersion();else{d.enableDebug(P.verbose),d.debug("cli options:",de,P);let e=Object.assign({name:de[0],type:"project"},P);e.cwd||=process.cwd(),e.example??=ye(process.env.CA_EXAMPLE),e.package??=ye(process.env.CA_PACKAGE),d.debug("final options:",e),e.type=Me(e),await le(e)}function Me(e){return e.example?"example":e.package?"package":"project"}function ye(e){return e==="1"||e==="true"}