create-vuepress-theme-hope
Version:
Create vuepress-theme-hope demo project helper
80 lines (69 loc) • 14.1 kB
JavaScript
import{spawnSync as D,execSync as h,spawn as V}from"node:child_process";import{existsSync as w,readFileSync as y,writeFileSync as v,readdirSync as N,mkdirSync as U,statSync as E,createReadStream as L,createWriteStream as K}from"node:fs";import{resolve as n,join as z,dirname as G}from"node:path";import{select as f,confirm as b,input as k}from"@inquirer/prompts";import{createCommand as Q}from"commander";import{fileURLToPath as M}from"node:url";const O=["vite","webpack"],P=["blog","docs"],X=()=>{try{return D("pnpm --version",[],{stdio:"ignore",shell:!0}).status===0}catch{return!1}},Z=()=>{try{return D("yarn --version",[],{stdio:"ignore",shell:!0}).status===0}catch{return!1}},T=["npm"];X()&&T.unshift("pnpm"),Z()&&T.unshift("yarn");const ee=e=>`
node_modules/
${e}/.vuepress/.cache/
${e}/.vuepress/.temp/
${e}/.vuepress/dist/
`,q=(e,t=process.cwd())=>{const r=n(t,".gitignore"),s=w(r)?y(r,{encoding:"utf-8"}):"";v(r,`${s}${ee(e)}`,{encoding:"utf-8"})},te=(e,t,r,{workflow:s})=>{const o=h("git branch --show-current",{cwd:t,encoding:"utf8"}).trim();return`
name: ${s.name}
on:
push:
branches:
- ${o}
permissions:
contents: write
jobs:
deploy-gh-pages:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
# ${s.submodule}
# submodules: true
${e==="pnpm"?` - name: ${s.setupPnpm}
uses: pnpm/action-setup@v4
`:""}
- name: ${s.setupNode}
uses: actions/setup-node@v4
with:
node-version: 22
cache: ${e}
- name: ${s.install}
run: |
corepack enable
${e==="npm"?"npm ci":`${e} install --frozen-lockfile`}
- name: ${s.build}
env:
NODE_OPTIONS: --max_old_space_size=8192
run: |-
${e} run docs:build
> ${z(r,".vuepress/dist/.nojekyll").replace(/\\/g,"/")}
- name: ${s.deploy}
uses: JamesIves/github-pages-deploy-action@v4
with:
# ${s.deploy}
branch: gh-pages
folder: ${z(r,".vuepress/dist").replace(/\\/g,"/")}
`},se=/^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/u,re=/^[0-9]+\.[0-9]+\.(?:[0-9]+|[0-9]+-[a-z]+\.[0-9])$/u,{isArray:oe}=Array,x=e=>Object.prototype.toString.call(e)==="[object Object]",$=(e,...t)=>{if(t.length===0)return e;const r=t.shift();return r&&Object.entries(r).forEach(([s,o])=>{s==="__proto__"||s==="constructor"||(x(e[s])&&x(o)?$(e[s],o):oe(o)?e[s]=[...o]:x(o)?e[s]={...o}:e[s]=r[s])}),$(e,...t)},S=e=>{try{N(e)}catch{try{U(e,{recursive:!0})}catch{}}},C=(e,t)=>{const r=G(t);S(r);const s=L(e),o=K(t);s.pipe(o)},_=(e,t)=>{S(t),N(e,{withFileTypes:!0}).forEach(r=>{r.isFile()?C(`${e}/${r.name}`,`${t}/${r.name}`):r.isDirectory()&&_(`${e}/${r.name}`,`${t}/${r.name}`)})},d=(e,t)=>{E(e).isDirectory()?_(e,t):E(e).isFile()&&C(e,t)},ne=(e=process.cwd())=>{try{return h("git status",{cwd:e,stdio:"ignore"}),!0}catch{return!1}},ae=()=>{try{return h("git --version",{stdio:"ignore"}),!0}catch{return!1}},ie="https://registry.npmmirror.com/",ce=(e,t)=>h(`${e} config get ${t?"npmRegistryServer":"registry"}`,{encoding:"utf8"}).trim(),le=async(e,t)=>{const r=e==="yarn"&&!D(`${e} --version`,{shell:!0}).stdout.toString().startsWith("1"),s=ce(e,r);return t==="zh"&&await f({message:"选择你想使用的源",choices:["国内镜像源","当前源"].map(o=>({name:o,value:o}))})==="国内镜像源"?ie:s},pe=M(import.meta.url),u=G(pe),A=async({cwd:e=process.cwd(),targetDir:t,lang:r,locale:s,preset:o,packageManager:a})=>{o??=await f({message:s.question.preset,choices:P.map(p=>({name:p,value:p}))});const c=await b({message:s.question.i18n,default:!1});console.log(s.flow.generateTemplate);const i=o;d(n(u,"../template/public"),n(e,t,"./.vuepress/public")),d(n(u,"../template",i,"config/base"),n(e,t,".vuepress")),c?(d(n(u,"../template",i,"en"),n(e,t)),d(n(u,"../template",i,"zh"),n(e,t,"zh")),d(n(u,"../template",i,"config/multi"),n(e,t,".vuepress"))):r==="zh"?(d(n(u,"../template",i,"zh"),n(e,t)),d(n(u,"../template",i,"config/zh"),n(e,t,".vuepress"))):(d(n(u,"../template",i,"en"),n(e,t)),d(n(u,"../template",i,"config/en"),n(e,t,".vuepress")));let l=ne(e);if(l?q(t,e):ae()&&await b({message:s.question.git,default:!0})&&(h("git init -b main",{cwd:e,stdio:"ignore"}),q(t,e),l=!0),l&&await b({message:s.question.workflow,default:!0})){const p=n(e,".github/workflows");S(p),v(n(p,"deploy-docs.yml"),te(a,e,t,s),{encoding:"utf-8"})}},m=JSON.parse(y(M(import.meta.resolve("create-vuepress-theme-hope/package.json")),"utf-8")),de={flow:{getVersion:"Getting latest version of deps...",createPackage:"Generating package.json...",updatePackage:"Updating package.json...",createTsConfig:"Generating tsconfig.json...",updateTsConfig:"Updating tsconfig.json...",generateTemplate:"Generating Template...",install:"Installing Deps...",devServer:`Staring dev server...
After the dev server starts running, please visit the given server link ('localhost:8080' by default)`},question:{i18n:"Does the project need multiple languages?",git:"Initialize a git repository?",workflow:"Do you need a GitHub workflow to deploy docs on GitHub pages?",bundler:"Which bundler do you want to use?",preset:"What type of project do you want to create?",packageManager:"Choose package manager",devServer:"Would you like to preview template now?",name:"Your project name",version:"Your project version",description:"Your project description",license:"Your project license"},hint:{install:`This may take a few minutes, please be patient.
We can not correctly output progress bar from child process, so the process may look stuck.`,finish:"Successful Generated!",devServer:e=>`Hint: You should execute "${e} run docs:dev" to start dev server.`},workflow:{name:"Deploy Docs",checkout:"Checkout code",submodule:"if your docs needs submodules, uncomment the following line",setupPnpm:"Setup pnpm",setupNode:"Setup Node.js",install:"Install Deps",build:"Build Docs",deploy:"Deploy Docs",deployBranch:"This is the branch where the docs are deployed to"},error:{name:"package name should only contain lowercase characters, numbers and dash",version:"This version is not a valid one. Version should be like 'x.x.x'",bundler:'bundler (--bundler) only support "vite" or "webpack"',preset:'preset (--preset) only support "docs" or "blog"',outputDirHint:e=>`The angle brackets in "<dir>" means it is a required argument, you should replace it with folder name you want to use! E.g.: "my-blog", "project-docs"
For example: "${e} init vuepress-theme-hope project-docs"`,addDirHint:e=>`The brackets in "<dir>" means it is a required argument, you should replace it with folder name you want to use! E.g.: "src", "docs"
For example: "${e} init vuepress-theme-hope add docs"`,dirNotEmpty:e=>`Target folder "${e}" is not empty, please choose an empty folder or delete files in it.`}},ue={flow:{getVersion:"获取依赖的最新版本...",createPackage:"生成 package.json...",updatePackage:"更新 package.json...",createTsConfig:"生成 tsconfig.json...",updateTsConfig:"更新 tsconfig.json...",generateTemplate:"生成模板...",install:"安装依赖...",devServer:`启动开发服务器...
启动成功后,请在浏览器输入给出的开发服务器地址(默认为 'localhost:8080')`},question:{packageManager:"选择包管理器",i18n:"项目需要用到多语言么?",git:"是否初始化 Git 仓库?",workflow:"是否需要一个自动部署文档到 GitHub Pages 的工作流?",bundler:"你想要使用哪个打包器?",preset:"你想要创建什么类型的项目?",devServer:"是否想要现在启动 Demo 查看?",name:"设置应用名称",version:"设置应用版本号",description:"设置应用描述",license:"设置协议"},hint:{install:`这可能需要数分钟,请耐心等待.
我们无法正确输出子进程的进度条,所以进程可能会看似未响应`,finish:"模板已成功生成!",devServer:e=>`提示: 请使用 "${e} run docs:dev" 命令启动开发服务器`},workflow:{name:"部署文档",checkout:"检出代码",submodule:"如果你文档需要 Git 子模块,取消注释下一行",setupPnpm:"设置 pnpm",setupNode:"设置 Node.js",install:"安装依赖",build:"构建文档",deploy:"部署文档",deployBranch:"这是文档部署到的分支名称"},error:{name:"应用名称应只包含小写字母、数字和连接线 (-)",version:"此版本无效,版本号应为 'x.x.x'",bundler:"打包器 (--bundler) 仅支持 vite 或 webpack",preset:"预设 (--preset) 仅支持 docs 或 blog",outputDirHint:e=>`"<dir>" 的尖括号表示此处为一个必填参数,你应该替换为自己想使用的文件夹名称,如 "my-blog", "project-docs" 等!
例如: "${e} init vuepress-theme-hope project-docs"`,addDirHint:e=>`"<dir>" 的尖括号表示此处为一个必填参数,你应该替换为自己想使用的文件夹名称,如 "src", "docs" 等!
例如: "${e} init vuepress-theme-hope add docs"`,dirNotEmpty:e=>`目标文件夹 "${e}" 不为空,请选择一个空文件夹或者手动删除文件夹中的文件`}},ge={en:de,zh:ue},me=async()=>{const e=await f({message:"Select a language to display / 选择显示语言",choices:[{name:"English",value:"en"},{name:"简体中文",value:"zh"}]});return{lang:e,locale:ge[e]}},he=(e,t,r)=>({"docs:build":`vuepress-${t} build ${r}`,"docs:clean-dev":`vuepress-${t} dev ${r} --clean-cache`,"docs:dev":`vuepress-${t} dev ${r}`,"docs:update-package":`${e==="npm"?"npx":`${e} dlx`} vp-update`}),H=async({packageManager:e,locale:t,source:r,bundler:s,cwd:o=process.cwd()})=>{s??=await f({message:t.question.bundler,choices:O.map(p=>({name:p,value:p}))});const a=n(o,"package.json"),c=he(e,s,r),i={[`@vuepress/bundler-${s}`]:m.devDependencies.vuepress,"sass-embedded":m.devDependencies["sass-embedded"],...s==="webpack"?{"sass-loader":m.devDependencies["sass-loader"]}:{},vue:m.devDependencies.vue,vuepress:m.devDependencies.vuepress,"vuepress-theme-hope":m.devDependencies["vuepress-theme-hope"]},l={scripts:c,devDependencies:i,...e==="pnpm"?{pnpm:{onlyBuiltDependencies:["esbuild"]}}:{}};if(w(a)){console.log(t.flow.updatePackage);const p=JSON.parse(y(a,{encoding:"utf-8"}));$(p,l),v(a,`${JSON.stringify(p,null,2)}
`,{encoding:"utf-8"})}else{console.log(t.flow.createPackage);const p=await k({message:t.question.name,default:"vuepress-theme-hope-template",validate:j=>se.exec(j)?!0:t.error.name}),W=await k({message:t.question.description,default:"A project of vuepress-theme-hope"}),F=await k({message:t.question.version,default:"2.0.0",validate:j=>re.exec(j)?!0:t.error.version}),B=await k({message:t.question.license,default:"MIT"}),Y={name:p,description:W,version:F,license:B,type:"module",...l};v(a,`${JSON.stringify(Y,null,2)}
`,{encoding:"utf-8"})}},J=({source:e,locale:t,cwd:r=process.cwd()})=>{const s=n(r,"tsconfig.json");if(w(s)){console.log(t.flow.updateTsConfig);const o=JSON.parse(y(s,{encoding:"utf-8"}));$(o,{compilerOptions:{module:"NodeNext",moduleResolution:"NodeNext"},include:[...o.include??[],`${e}/.vuepress/**/*.ts`,`${e}/.vuepress/**/*.vue`]}),v(s,`${JSON.stringify(o,null,2)}
`,{encoding:"utf-8"})}else{console.log(t.flow.createTsConfig);const o={compilerOptions:{module:"NodeNext",moduleResolution:"NodeNext",target:"ES2022"},include:[`${e}/.vuepress/**/*.ts`,`${e}/.vuepress/**/*.vue`],exclude:["node_modules"]};v(s,`${JSON.stringify(o,null,2)}
`,{encoding:"utf-8"})}},g=Q("create-vuepress-theme-hope"),R=async(e,{bundler:t,preset:r,add:s})=>{const{lang:o,locale:a}=await me();t&&!O.includes(t)&&g.error(a.error.bundler),r&&!P.includes(r)&&g.error(a.error.preset);const c=n(process.cwd(),e);w(c)&&N(c).length&&g.error(a.error.dirNotEmpty(e));const i=await f({message:a.question.packageManager,choices:T.map(l=>({name:l,value:l}))});return e.startsWith("<")&&e.endsWith(">")&&g.error(a.error[s?"addDirHint":"outputDirHint"](i)),S(c),{lang:o,locale:a,packageManager:i}},I=async({cwd:e=process.cwd(),lang:t,locale:r,packageManager:s})=>{const o=s==="pnpm"?"":await le(s,t);console.log(r.flow.install),console.warn(r.hint.install),h(`${s} install ${o?`--registry ${o}`:""}`,{cwd:e,stdio:"inherit"}),console.log(r.hint.finish),await b({message:r.question.devServer,default:!0})?(console.log(r.flow.devServer),await new Promise((a,c)=>{const i=V(`${s} run docs:dev`,[],{cwd:e,stdio:"inherit",shell:!0});i.on("close",l=>{l===0?a():c(new Error(`code: ${l}`))}),i.on("error",l=>{c(new Error(`commandError: ${l}`))})})):console.info(r.hint.devServer(s))};g.description(`Generate a new vuepress-theme-hope template
· pnpm create vuepress-theme-hope <dir>
· npm init vuepress-theme-hope@latest <dir>
· yarn create vuepress-theme-hope <dir>
`).option("-b, --bundler [bundler]","Bundler to use, vite or webpack only").option("-p, --preset [preset]","Preset to use, docs or blog only").argument("<dir>","Dir to create the template in").action(async(e,{bundler:t,preset:r})=>{const s=n(process.cwd(),e),{lang:o,locale:a,packageManager:c}=await R(e,{bundler:t,preset:r,add:!1});await H({bundler:t,locale:a,packageManager:c,cwd:e,source:"src"}),J({cwd:e,source:"src",locale:a}),await A({preset:r,lang:o,locale:a,packageManager:c,cwd:s,targetDir:"src"}),await I({lang:o,locale:a,packageManager:c,cwd:s})}),g.command("add").alias("inject").summary("Add template to <dir> inside project").description(`Add vuepress-theme-hope template in <dir> under current project
· pnpm create vuepress-theme-hope add <dir>
· npm init vuepress-theme-hope@latest add <dir>
· yarn create vuepress-theme-hope add <dir>
`).usage("").option("-b, --bundler [bundler]","Choose bundler to use").option("-p, --preset [preset]","Choose preset to use").argument("<dir>","Dir to create the template in").action(async(e,{bundler:t,preset:r})=>{const{lang:s,locale:o,packageManager:a}=await R(e,{bundler:t,preset:r,add:!0});await H({bundler:t,packageManager:a,locale:o,source:e}),J({source:e,locale:o}),await A({packageManager:a,lang:s,locale:o,preset:r,targetDir:e}),await I({lang:s,locale:o,packageManager:a})}),g.version(m.version),g.showHelpAfterError("add --help for additional information"),await g.parseAsync();