@qlover/create-app 2.0.5 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # @qlover/create-app
2
2
 
3
+ ## 3.0.0
4
+
5
+ ### Major Changes
6
+
7
+ #### ✨ Features
8
+
9
+ - **docs:** enhance README for create-app with detailed usage and features ([ab65210](https://github.com/qlover/fe-base/commit/ab6521083f0bc951c085ec8bab147b9f586e7516)) ([#604](https://github.com/qlover/fe-base/pull/604))
10
+ - Updated the English and Chinese README files to provide a comprehensive overview of the @qlover/create-app tool.
11
+ - Added descriptions of the template fetching process from GitHub, including post-processing of dependencies in generated projects.
12
+ - Introduced a TL;DR section for quick usage instructions and clarified the interactive flow for project setup.
13
+ - Expanded CLI options and examples to improve usability and understanding for developers.
14
+
15
+ These changes aim to enhance the documentation's clarity and accessibility for users of the create-app tool.
16
+
17
+ ## 2.0.7
18
+
19
+ ### Patch Changes
20
+
21
+ #### ✨ Features
22
+
23
+ - **next-seed:** update dependencies and enhance .env.template ([eeac3ca](https://github.com/qlover/fe-base/commit/eeac3ca951e4b62dcde307bdb41608378d0cf4da)) ([#601](https://github.com/qlover/fe-base/pull/601))
24
+ - Added new dependencies: `https-proxy-agent` and `node-fetch` to support proxy functionality in server requests.
25
+ - Updated `.env.template` to include additional configuration options for API keys and logging settings, enhancing flexibility for environment setup.
26
+ - Updated `.gitignore` to exclude `.env` and `.cache` files for better security and cleanliness.
27
+ - Introduced a new SQL script for creating base tables in Supabase, including login audit logs with user-scoped row-level security.
28
+
29
+ These changes aim to improve the server's capability to handle API requests through proxies and streamline the environment configuration process.
30
+
3
31
  ## 2.0.5
4
32
 
5
33
  ### Patch Changes
package/README.md CHANGED
@@ -1,9 +1,15 @@
1
1
  # @qlover/create-app
2
2
 
3
- 快速创建和初始化新的应用程序的命令行工具。
3
+ 从 GitHub 仓库的 `examples/` 拉取模板列表,下载并解压模板到本地目录;生成后会把模板中 `workspace:*` 依赖解析为 npm 上的具体版本(失败时回退为 `latest`),以便项目可独立安装。
4
4
 
5
5
  👉 中文文档 | [English Docs](./README_EN.md)
6
6
 
7
+ ## TL;DR
8
+
9
+ ```bash
10
+ npx @qlover/create-app@latest
11
+ ```
12
+
7
13
  ## 安装
8
14
 
9
15
  ```bash
@@ -12,25 +18,49 @@ npm install -g @qlover/create-app
12
18
 
13
19
  ## 使用方法
14
20
 
15
- 你可以通过以下方式使用:
21
+ 推荐使用 npx(避免全局安装):
16
22
 
17
23
  ```bash
18
24
  npx @qlover/create-app
19
25
  ```
20
26
 
27
+ 也可以全局安装后直接运行二进制:
28
+
29
+ ```bash
30
+ create-app
31
+ ```
32
+
33
+ ## 交互流程
34
+
35
+ 运行后会依次询问:
36
+
37
+ - **Project name**:项目目录名(若目录已存在会提示更换)
38
+ - **Template name**:模板名(从 GitHub `qlover/fe-base` 的 `examples/` 动态获取)
39
+
40
+ ## CLI 参数
41
+
42
+ - **`-v, --version`**:输出版本号
43
+ - **`-d, --dry-run`**:不写入文件(用于预览生成过程)
44
+ - **`-V, --verbose`**:输出更多信息
45
+
46
+ 示例:
47
+
48
+ ```bash
49
+ npx @qlover/create-app@latest --dry-run --verbose
50
+ ```
51
+
21
52
  ## 特性
22
53
 
23
54
  - 快速项目脚手架
24
- - 多种项目模板
25
55
  - 交互式命令行界面
26
- - 自动依赖安装
56
+ - 模板来自 GitHub `examples/`,无需在本地维护模板清单
57
+ - 创建后自动处理模板中的 `workspace:*` 依赖(解析为 npm 最新版本;失败时回退为 `latest`)
27
58
 
28
59
  ## 可用模板
29
60
 
30
- - React 应用
31
- - Next.js 应用
32
- - 更多...
33
-
34
- ## 许可证
61
+ 模板列表来自 GitHub,随仓库内容变化。通常会包含类似:
35
62
 
36
- ISC
63
+ - `react-seed`
64
+ - `next-seed`
65
+ - `taro-seed`
66
+ - `browser-plugin-seed`
package/dist/index.cjs CHANGED
@@ -8,4 +8,4 @@ ${u}`,Ae=Object.getOwnPropertyDescriptor(Function.prototype,"toString"),xe=Objec
8
8
  `))this.#u+=Math.max(1,Math.ceil(Tu(i,{countAnsiEscapeCodes:!0})/u))}get isEnabled(){return this.#a&&!this.#F}set isEnabled(u){if(typeof u!="boolean")throw new TypeError("The `isEnabled` option must be a boolean");this.#a=u}get isSilent(){return this.#F}set isSilent(u){if(typeof u!="boolean")throw new TypeError("The `isSilent` option must be a boolean");this.#F=u}frame(){let u=Date.now();(this.#r===-1||u-this.#c>=this.interval)&&(this.#r=++this.#r%this.#i.frames.length,this.#c=u);let{frames:e}=this.#i,D=e[this.#r];this.color&&(D=h[this.color](D));let r=typeof this.#s=="string"&&this.#s!==""?this.#s+" ":"",i=typeof this.text=="string"?" "+this.text:"",n=typeof this.#o=="string"&&this.#o!==""?" "+this.#o:"";return r+D+i+n}clear(){if(!this.#a||!this.#D.isTTY)return this;this.#D.cursorTo(0);for(let u=0;u<this.#n;u++)u>0&&this.#D.moveCursor(0,-1),this.#D.clearLine(1);return(this.#l||this.lastIndent!==this.#l)&&this.#D.cursorTo(this.#l),this.lastIndent=this.#l,this.#n=0,this}render(){return this.#F?this:(this.clear(),this.#D.write(this.frame()),this.#n=this.#u,this)}start(u){return u&&(this.text=u),this.#F?this:this.#a?this.isSpinning?this:(this.#t.hideCursor&&Bu.hide(this.#D),this.#t.discardStdin&&k.default.stdin.isTTY&&(this.#e=!0,Iu.start()),this.render(),this.#f=setInterval(this.render.bind(this),this.interval),this):(this.text&&this.#D.write(`- ${this.text}
9
9
  `),this)}stop(){return this.#a?(clearInterval(this.#f),this.#f=void 0,this.#r=0,this.clear(),this.#t.hideCursor&&Bu.show(this.#D),this.#t.discardStdin&&k.default.stdin.isTTY&&this.#e&&(Iu.stop(),this.#e=!1),this):this}succeed(u){return this.stopAndPersist({symbol:G.success,text:u})}fail(u){return this.stopAndPersist({symbol:G.error,text:u})}warn(u){return this.stopAndPersist({symbol:G.warning,text:u})}info(u){return this.stopAndPersist({symbol:G.info,text:u})}stopAndPersist(u={}){if(this.#F)return this;let e=u.prefixText??this.#s,D=this.#m(e," "),r=u.symbol??" ",i=u.text??this.text,s=typeof i=="string"?(r?" ":"")+i:"",o=u.suffixText??this.#o,l=this.#p(o," "),_=D+r+s+l+`
10
10
  `;return this.stop(),this.#D.write(_),this}};function eu(t){return new Ou(t)}async function yt(t,u){let e=typeof t=="function",D=typeof t.then=="function";if(!e&&!D)throw new TypeError("Parameter `action` must be a Function or a Promise");let{successText:r,failText:i}=typeof u=="object"?u:{successText:void 0,failText:void 0},n=eu(u).start();try{let o=await(e?t(n):t);return n.succeed(r===void 0?void 0:typeof r=="string"?r:r(o)),o}catch(s){throw n.fail(i===void 0?void 0:typeof i=="string"?i:i(s)),s}}var S=require("path"),x=require("fs"),Gt=c(Nt(),1);var nu=require("fs"),W=class{static ensureDir(u){(0,nu.existsSync)(u)||(0,nu.mkdirSync)(u,{recursive:!0})}};var{copyFile:CD,stat:hD}=x.promises,su=class t{constructor(u="",e=t.IGNORE_FILE){this.ignoreTargetPath=u;this.ignoreFile=e}static IGNORE_FILE=".gitignore.template";getIg(u=this.ignoreTargetPath){if(!u)return;let e=(0,S.join)(u,this.ignoreFile);if(!(0,x.existsSync)(e))return;let i=(0,x.readFileSync)(e,"utf8").split(`
11
- `).map(n=>n.trim()).filter(n=>n&&!n.startsWith("#"));return(0,Gt.default)().add(i)}async copyFiles(u,e,D,r){let i=await x.promises.readdir(u);await Promise.all(i.map(async n=>{let s=(0,S.join)(u,n),o=(0,S.join)(e,n);if(D&&D.ignores(n))return;if(W.ensureDir((0,S.dirname)(o)),(await hD(s)).isDirectory())await this.copyFiles(s,o,D,r);else{if(r&&await r(s,o))return;await CD(s,o)}}))}copyPaths({sourcePath:u,targetPath:e,copyCallback:D,ignorePath:r}){W.ensureDir(e);let i=this.getIg(r??this.ignoreTargetPath);return this.copyFiles(u,e,i,D)}};var U=require("undici"),b=require("path"),d=require("fs/promises"),ju=require("os"),Wu=require("tar"),mD="https://api.github.com",pD="https://github.com/{owner}/{repo}/archive/refs/heads/{branch}.tar.gz",dD=3e4,_D=12e4,Lt=6e4,Mt=3,gD=2e3,BD=new U.Agent({connectTimeout:Lt}),AD=new U.Agent({connectTimeout:Lt}),kt={owner:"qlover",repo:"fe-base",branch:"master",examplesPath:"examples"};function xD(t){let u=new AbortController,e=setTimeout(()=>u.abort(),t),D=u.signal;return D._clear=()=>clearTimeout(e),D}function bD(t){return new Promise(u=>setTimeout(u,t))}async function $t(t,u){let{timeoutMs:e,dispatcher:D,...r}=u,i=D?U.fetch:fetch,n;for(let s=1;s<=Mt;s++){let o=xD(e);try{let l={...r,signal:o,...D&&{dispatcher:D}},_=await i(t,l);return typeof o._clear=="function"&&o._clear?.(),_}catch(l){n=l,s<Mt&&await bD(gD)}}throw n}async function jt(t={}){let u={...kt,...t},e=`${mD}/repos/${u.owner}/${u.repo}/contents/${u.examplesPath}`,D=await $t(e,{timeoutMs:dD,dispatcher:BD,headers:{Accept:"application/vnd.github.v3+json"}});if(!D.ok)throw new Error(`Failed to fetch template list: ${D.status} ${D.statusText}`);return(await D.json()).filter(i=>i.type==="dir").map(i=>i.name)}async function Wt(t,u={}){let{onProgress:e,...D}=u,r={...kt,...D},i=pD.replace("{owner}",r.owner).replace("{repo}",r.repo).replace("{branch}",r.branch),n=process.env.CREATE_APP_GITHUB_MIRROR?.trim();n&&(i=n.replace(/\/$/,"")+"/"+i);let s=await $t(i,{timeoutMs:_D,dispatcher:AD});if(!s.ok)throw new Error(`Failed to download archive: ${s.status} ${s.statusText}`);let o=s.headers.get("Content-Length"),l=o?parseInt(o,10):void 0,_=0,Yu=s.body?.getReader();if(!Yu){let y=await s.arrayBuffer(),B=await(0,d.mkdtemp)((0,b.join)((0,ju.tmpdir)(),"create-app-")),zu=(0,b.join)(B,"repo.tar.gz");await(0,d.writeFile)(zu,new Uint8Array(y));let Jt=(0,b.join)(B,`${r.repo}-${r.branch}`,r.examplesPath,t);return await(0,Wu.extract)({file:zu,cwd:B}),{templatePath:Jt,cleanup:async()=>{await(0,d.rm)(B,{recursive:!0,force:!0})}}}let qu=[];for(;;){let{done:y,value:B}=await Yu.read();if(y)break;qu.push(B),_+=B.length,e?.(_,l)}let Vu=new Uint8Array(_),Xu=0;for(let y of qu)Vu.set(y,Xu),Xu+=y.length;let H=await(0,d.mkdtemp)((0,b.join)((0,ju.tmpdir)(),"create-app-")),Ku=(0,b.join)(H,"repo.tar.gz");await(0,d.writeFile)(Ku,Vu),await(0,Wu.extract)({file:Ku,cwd:H});let zt=`${r.repo}-${r.branch}`;return{templatePath:(0,b.join)(H,zt,r.examplesPath,t),cleanup:async()=>{await(0,d.rm)(H,{recursive:!0,force:!0})}}}var Vt=require("fs");var g=require("fs"),Ut=require("path"),yD="https://registry.npmjs.org",vD=["dependencies","devDependencies","peerDependencies","optionalDependencies"];function TD(t){let u=t.includes("/")?t.replace("/","%2F"):encodeURIComponent(t);return`${yD}/${u}/latest`}async function wD(t){try{let u=TD(t),e=await fetch(u);if(!e.ok)return"latest";let D=await e.json();return typeof D?.version=="string"?D.version:"latest"}catch{return"latest"}}async function RD(t){let u=(0,g.readFileSync)(t,"utf-8"),e;try{e=JSON.parse(u)}catch{return}let D=!1;for(let r of vD){let i=e[r];if(!(!i||typeof i!="object"))for(let n of Object.keys(i)){if(i[n]!=="workspace:*")continue;let s=await wD(n);i[n]=s,D=!0}}D&&(0,g.writeFileSync)(t,JSON.stringify(e,null,2),"utf-8")}async function Uu(t){let u=(0,g.readdirSync)(t);for(let e of u){if(e==="node_modules")continue;let D=(0,Ut.join)(t,e);(0,g.statSync)(D).isDirectory()?await Uu(D):e==="package.json"&&await RD(D)}}var ou=class{ora;context;templateList;copyer;constructor(u){this.ora=yt,this.context=new Ht.ScriptContext("create-app",u),this.templateList=this.context.options.templateList??[],this.copyer=new su}get logger(){return this.context.logger}async steps(u){try{return await Yt.default.prompt(u)}catch(e){throw e.isTtyError,e.name==="ExitPromptError"||this.logger.error(e),e}}async action({label:u,task:e}){let D=e();D instanceof Promise||(D=Promise.resolve(D));let r=u;return this.ora(D,r),D}async getGeneratorContext(){let u=ut(this.templateList),e=await this.steps(u);return e.targetPath=(0,qt.join)(process.cwd(),e.projectName),e}async generate(){let u=await this.getGeneratorContext();if(this.logger.debug("context is:",u),(0,Vt.existsSync)(u.targetPath))throw new Error(`The directory already exists: ${u.targetPath}. Please choose another project name or remove the existing directory.`);let e,D,r="Download template from GitHub",i=eu(r).start();try{let n=await Wt(u.template,{onProgress:(s,o)=>{if(o!=null&&o>0)i.text=`${r} ${Math.round(s/o*100)}%`;else{let l=(s/1024/1024).toFixed(2);i.text=`${r} ${l} MB`}}});e=n.templatePath,D=n.cleanup,i.succeed(r)}catch(n){throw i.fail(r),n}await this.action({label:"Generate directory",task:async()=>{try{await this.copyer.copyPaths({sourcePath:e,targetPath:u.targetPath,ignorePath:e})}finally{await D()}}}),await this.action({label:"Replace workspace:* with concrete versions",task:async()=>{await Uu(u.targetPath)}})}};var Hu={name:"@qlover/create-app",version:"2.0.5",description:"Create a new app with a single command",private:!1,type:"module",files:["dist","package.json","README.md","CHANGELOG.md"],bin:{"create-app":"dist/index.js"},scripts:{lint:"eslint src --fix",build:"tsup","type-check":"tsc --noEmit","create:app":"node ./dist/index.js"},repository:{type:"git",url:"git+https://github.com/qlover/fe-base.git",directory:"packages/create-app"},homepage:"https://github.com/qlover/fe-base#readme",keywords:["create-app","fe-scripts","scripts"],author:"qlover",license:"ISC",publishConfig:{access:"public"},devDependencies:{"@qlover/logger":"workspace:*",ignore:"^7.0.3",ora:"^8.1.1"},dependencies:{"@qlover/scripts-context":"workspace:*",commander:"^13.1.0",inquirer:"^12.3.2",tar:"^7.5.0",undici:"^7.0.0"}};function SD(){let t=new Xt.Command;return t.version(Hu.version,"-v, --version","Show version").description(Hu.description).option("-d, --dry-run","Do not touch or write anything, but show the commands").option("-V, --verbose","Show more information"),t.parse(),t.opts()}async function Kt(){let{dryRun:t,verbose:u}=SD(),e=await jt();e?.length||(console.error("No templates found from GitHub examples."),process.exit(1));let D={templateList:e};await new ou({dryRun:t,verbose:u,options:D}).generate()}Kt().catch(t=>{t.name==="ExitPromptError"&&process.exit(130),console.error(t),process.exit(1)});
11
+ `).map(n=>n.trim()).filter(n=>n&&!n.startsWith("#"));return(0,Gt.default)().add(i)}async copyFiles(u,e,D,r){let i=await x.promises.readdir(u);await Promise.all(i.map(async n=>{let s=(0,S.join)(u,n),o=(0,S.join)(e,n);if(D&&D.ignores(n))return;if(W.ensureDir((0,S.dirname)(o)),(await hD(s)).isDirectory())await this.copyFiles(s,o,D,r);else{if(r&&await r(s,o))return;await CD(s,o)}}))}copyPaths({sourcePath:u,targetPath:e,copyCallback:D,ignorePath:r}){W.ensureDir(e);let i=this.getIg(r??this.ignoreTargetPath);return this.copyFiles(u,e,i,D)}};var U=require("undici"),b=require("path"),d=require("fs/promises"),ju=require("os"),Wu=require("tar"),mD="https://api.github.com",pD="https://github.com/{owner}/{repo}/archive/refs/heads/{branch}.tar.gz",dD=3e4,_D=12e4,Lt=6e4,Mt=3,gD=2e3,BD=new U.Agent({connectTimeout:Lt}),AD=new U.Agent({connectTimeout:Lt}),kt={owner:"qlover",repo:"fe-base",branch:"master",examplesPath:"examples"};function xD(t){let u=new AbortController,e=setTimeout(()=>u.abort(),t),D=u.signal;return D._clear=()=>clearTimeout(e),D}function bD(t){return new Promise(u=>setTimeout(u,t))}async function $t(t,u){let{timeoutMs:e,dispatcher:D,...r}=u,i=D?U.fetch:fetch,n;for(let s=1;s<=Mt;s++){let o=xD(e);try{let l={...r,signal:o,...D&&{dispatcher:D}},_=await i(t,l);return typeof o._clear=="function"&&o._clear?.(),_}catch(l){n=l,s<Mt&&await bD(gD)}}throw n}async function jt(t={}){let u={...kt,...t},e=`${mD}/repos/${u.owner}/${u.repo}/contents/${u.examplesPath}`,D=await $t(e,{timeoutMs:dD,dispatcher:BD,headers:{Accept:"application/vnd.github.v3+json"}});if(!D.ok)throw new Error(`Failed to fetch template list: ${D.status} ${D.statusText}`);return(await D.json()).filter(i=>i.type==="dir").map(i=>i.name)}async function Wt(t,u={}){let{onProgress:e,...D}=u,r={...kt,...D},i=pD.replace("{owner}",r.owner).replace("{repo}",r.repo).replace("{branch}",r.branch),n=process.env.CREATE_APP_GITHUB_MIRROR?.trim();n&&(i=n.replace(/\/$/,"")+"/"+i);let s=await $t(i,{timeoutMs:_D,dispatcher:AD});if(!s.ok)throw new Error(`Failed to download archive: ${s.status} ${s.statusText}`);let o=s.headers.get("Content-Length"),l=o?parseInt(o,10):void 0,_=0,Yu=s.body?.getReader();if(!Yu){let y=await s.arrayBuffer(),B=await(0,d.mkdtemp)((0,b.join)((0,ju.tmpdir)(),"create-app-")),zu=(0,b.join)(B,"repo.tar.gz");await(0,d.writeFile)(zu,new Uint8Array(y));let Jt=(0,b.join)(B,`${r.repo}-${r.branch}`,r.examplesPath,t);return await(0,Wu.extract)({file:zu,cwd:B}),{templatePath:Jt,cleanup:async()=>{await(0,d.rm)(B,{recursive:!0,force:!0})}}}let qu=[];for(;;){let{done:y,value:B}=await Yu.read();if(y)break;qu.push(B),_+=B.length,e?.(_,l)}let Vu=new Uint8Array(_),Xu=0;for(let y of qu)Vu.set(y,Xu),Xu+=y.length;let H=await(0,d.mkdtemp)((0,b.join)((0,ju.tmpdir)(),"create-app-")),Ku=(0,b.join)(H,"repo.tar.gz");await(0,d.writeFile)(Ku,Vu),await(0,Wu.extract)({file:Ku,cwd:H});let zt=`${r.repo}-${r.branch}`;return{templatePath:(0,b.join)(H,zt,r.examplesPath,t),cleanup:async()=>{await(0,d.rm)(H,{recursive:!0,force:!0})}}}var Vt=require("fs");var g=require("fs"),Ut=require("path"),yD="https://registry.npmjs.org",vD=["dependencies","devDependencies","peerDependencies","optionalDependencies"];function TD(t){let u=t.includes("/")?t.replace("/","%2F"):encodeURIComponent(t);return`${yD}/${u}/latest`}async function wD(t){try{let u=TD(t),e=await fetch(u);if(!e.ok)return"latest";let D=await e.json();return typeof D?.version=="string"?D.version:"latest"}catch{return"latest"}}async function RD(t){let u=(0,g.readFileSync)(t,"utf-8"),e;try{e=JSON.parse(u)}catch{return}let D=!1;for(let r of vD){let i=e[r];if(!(!i||typeof i!="object"))for(let n of Object.keys(i)){if(i[n]!=="workspace:*")continue;let s=await wD(n);i[n]=s,D=!0}}D&&(0,g.writeFileSync)(t,JSON.stringify(e,null,2),"utf-8")}async function Uu(t){let u=(0,g.readdirSync)(t);for(let e of u){if(e==="node_modules")continue;let D=(0,Ut.join)(t,e);(0,g.statSync)(D).isDirectory()?await Uu(D):e==="package.json"&&await RD(D)}}var ou=class{ora;context;templateList;copyer;constructor(u){this.ora=yt,this.context=new Ht.ScriptContext("create-app",u),this.templateList=this.context.options.templateList??[],this.copyer=new su}get logger(){return this.context.logger}async steps(u){try{return await Yt.default.prompt(u)}catch(e){throw e.isTtyError,e.name==="ExitPromptError"||this.logger.error(e),e}}async action({label:u,task:e}){let D=e();D instanceof Promise||(D=Promise.resolve(D));let r=u;return this.ora(D,r),D}async getGeneratorContext(){let u=ut(this.templateList),e=await this.steps(u);return e.targetPath=(0,qt.join)(process.cwd(),e.projectName),e}async generate(){let u=await this.getGeneratorContext();if(this.logger.debug("context is:",u),(0,Vt.existsSync)(u.targetPath))throw new Error(`The directory already exists: ${u.targetPath}. Please choose another project name or remove the existing directory.`);let e,D,r="Download template from GitHub",i=eu(r).start();try{let n=await Wt(u.template,{onProgress:(s,o)=>{if(o!=null&&o>0)i.text=`${r} ${Math.round(s/o*100)}%`;else{let l=(s/1024/1024).toFixed(2);i.text=`${r} ${l} MB`}}});e=n.templatePath,D=n.cleanup,i.succeed(r)}catch(n){throw i.fail(r),n}await this.action({label:"Generate directory",task:async()=>{try{await this.copyer.copyPaths({sourcePath:e,targetPath:u.targetPath,ignorePath:e})}finally{await D()}}}),await this.action({label:"Replace workspace:* with concrete versions",task:async()=>{await Uu(u.targetPath)}})}};var Hu={name:"@qlover/create-app",version:"3.0.0",description:"Create a new app with a single command",private:!1,type:"module",files:["dist","package.json","README.md","CHANGELOG.md"],bin:{"create-app":"dist/index.js"},scripts:{lint:"eslint src --fix",build:"tsup","type-check":"tsc --noEmit","create:app":"node ./dist/index.js"},repository:{type:"git",url:"git+https://github.com/qlover/fe-base.git",directory:"packages/create-app"},homepage:"https://github.com/qlover/fe-base#readme",keywords:["create-app","fe-scripts","scripts"],author:"qlover",license:"ISC",publishConfig:{access:"public"},devDependencies:{"@qlover/logger":"workspace:*",ignore:"^7.0.3",ora:"^8.1.1"},dependencies:{"@qlover/scripts-context":"workspace:*",commander:"^13.1.0",inquirer:"^12.3.2",tar:"^7.5.0",undici:"^7.0.0"}};function SD(){let t=new Xt.Command;return t.version(Hu.version,"-v, --version","Show version").description(Hu.description).option("-d, --dry-run","Do not touch or write anything, but show the commands").option("-V, --verbose","Show more information"),t.parse(),t.opts()}async function Kt(){let{dryRun:t,verbose:u}=SD(),e=await jt();e?.length||(console.error("No templates found from GitHub examples."),process.exit(1));let D={templateList:e};await new ou({dryRun:t,verbose:u,options:D}).generate()}Kt().catch(t=>{t.name==="ExitPromptError"&&process.exit(130),console.error(t),process.exit(1)});
package/dist/index.js CHANGED
@@ -8,4 +8,4 @@ ${u}`,me=Object.getOwnPropertyDescriptor(Function.prototype,"toString"),pe=Objec
8
8
  `))this.#u+=Math.max(1,Math.ceil(_u(i,{countAnsiEscapeCodes:!0})/u))}get isEnabled(){return this.#a&&!this.#F}set isEnabled(u){if(typeof u!="boolean")throw new TypeError("The `isEnabled` option must be a boolean");this.#a=u}get isSilent(){return this.#F}set isSilent(u){if(typeof u!="boolean")throw new TypeError("The `isSilent` option must be a boolean");this.#F=u}frame(){let u=Date.now();(this.#r===-1||u-this.#c>=this.interval)&&(this.#r=++this.#r%this.#i.frames.length,this.#c=u);let{frames:e}=this.#i,D=e[this.#r];this.color&&(D=E[this.color](D));let r=typeof this.#s=="string"&&this.#s!==""?this.#s+" ":"",i=typeof this.text=="string"?" "+this.text:"",n=typeof this.#o=="string"&&this.#o!==""?" "+this.#o:"";return r+D+i+n}clear(){if(!this.#a||!this.#D.isTTY)return this;this.#D.cursorTo(0);for(let u=0;u<this.#n;u++)u>0&&this.#D.moveCursor(0,-1),this.#D.clearLine(1);return(this.#l||this.lastIndent!==this.#l)&&this.#D.cursorTo(this.#l),this.lastIndent=this.#l,this.#n=0,this}render(){return this.#F?this:(this.clear(),this.#D.write(this.frame()),this.#n=this.#u,this)}start(u){return u&&(this.text=u),this.#F?this:this.#a?this.isSpinning?this:(this.#t.hideCursor&&Eu.hide(this.#D),this.#t.discardStdin&&X.stdin.isTTY&&(this.#e=!0,xu.start()),this.render(),this.#f=setInterval(this.render.bind(this),this.interval),this):(this.text&&this.#D.write(`- ${this.text}
9
9
  `),this)}stop(){return this.#a?(clearInterval(this.#f),this.#f=void 0,this.#r=0,this.clear(),this.#t.hideCursor&&Eu.show(this.#D),this.#t.discardStdin&&X.stdin.isTTY&&this.#e&&(xu.stop(),this.#e=!1),this):this}succeed(u){return this.stopAndPersist({symbol:R.success,text:u})}fail(u){return this.stopAndPersist({symbol:R.error,text:u})}warn(u){return this.stopAndPersist({symbol:R.warning,text:u})}info(u){return this.stopAndPersist({symbol:R.info,text:u})}stopAndPersist(u={}){if(this.#F)return this;let e=u.prefixText??this.#s,D=this.#m(e," "),r=u.symbol??" ",i=u.text??this.text,s=typeof i=="string"?(r?" ":"")+i:"",o=u.suffixText??this.#o,l=this.#p(o," "),p=D+r+s+l+`
10
10
  `;return this.stop(),this.#D.write(p),this}};function K(t){return new bu(t)}async function ht(t,u){let e=typeof t=="function",D=typeof t.then=="function";if(!e&&!D)throw new TypeError("Parameter `action` must be a Function or a Promise");let{successText:r,failText:i}=typeof u=="object"?u:{successText:void 0,failText:void 0},n=K(u).start();try{let o=await(e?t(n):t);return n.succeed(r===void 0?void 0:typeof r=="string"?r:r(o)),o}catch(s){throw n.fail(i===void 0?void 0:typeof i=="string"?i:i(s)),s}}var vt=eu(bt(),1);import{dirname as lD,join as Su}from"path";import{existsSync as cD,readFileSync as fD,promises as yt}from"fs";import{existsSync as aD,mkdirSync as FD}from"fs";var N=class{static ensureDir(u){aD(u)||FD(u,{recursive:!0})}};var{copyFile:ED,stat:CD}=yt,Z=class t{constructor(u="",e=t.IGNORE_FILE){this.ignoreTargetPath=u;this.ignoreFile=e}static IGNORE_FILE=".gitignore.template";getIg(u=this.ignoreTargetPath){if(!u)return;let e=Su(u,this.ignoreFile);if(!cD(e))return;let i=fD(e,"utf8").split(`
11
- `).map(n=>n.trim()).filter(n=>n&&!n.startsWith("#"));return(0,vt.default)().add(i)}async copyFiles(u,e,D,r){let i=await yt.readdir(u);await Promise.all(i.map(async n=>{let s=Su(u,n),o=Su(e,n);if(D&&D.ignores(n))return;if(N.ensureDir(lD(o)),(await CD(s)).isDirectory())await this.copyFiles(s,o,D,r);else{if(r&&await r(s,o))return;await ED(s,o)}}))}copyPaths({sourcePath:u,targetPath:e,copyCallback:D,ignorePath:r}){N.ensureDir(e);let i=this.getIg(r??this.ignoreTargetPath);return this.copyFiles(u,e,i,D)}};import{Agent as Ot,fetch as hD}from"undici";import{join as v}from"path";import{mkdtemp as Tt,writeFile as wt,rm as Rt}from"fs/promises";import{tmpdir as Pt}from"os";import{extract as St}from"tar";var mD="https://api.github.com",pD="https://github.com/{owner}/{repo}/archive/refs/heads/{branch}.tar.gz",dD=3e4,_D=12e4,Nt=6e4,It=3,gD=2e3,BD=new Ot({connectTimeout:Nt}),AD=new Ot({connectTimeout:Nt}),Gt={owner:"qlover",repo:"fe-base",branch:"master",examplesPath:"examples"};function xD(t){let u=new AbortController,e=setTimeout(()=>u.abort(),t),D=u.signal;return D._clear=()=>clearTimeout(e),D}function bD(t){return new Promise(u=>setTimeout(u,t))}async function Mt(t,u){let{timeoutMs:e,dispatcher:D,...r}=u,i=D?hD:fetch,n;for(let s=1;s<=It;s++){let o=xD(e);try{let l={...r,signal:o,...D&&{dispatcher:D}},p=await i(t,l);return typeof o._clear=="function"&&o._clear?.(),p}catch(l){n=l,s<It&&await bD(gD)}}throw n}async function Lt(t={}){let u={...Gt,...t},e=`${mD}/repos/${u.owner}/${u.repo}/contents/${u.examplesPath}`,D=await Mt(e,{timeoutMs:dD,dispatcher:BD,headers:{Accept:"application/vnd.github.v3+json"}});if(!D.ok)throw new Error(`Failed to fetch template list: ${D.status} ${D.statusText}`);return(await D.json()).filter(i=>i.type==="dir").map(i=>i.name)}async function kt(t,u={}){let{onProgress:e,...D}=u,r={...Gt,...D},i=pD.replace("{owner}",r.owner).replace("{repo}",r.repo).replace("{branch}",r.branch),n=process.env.CREATE_APP_GITHUB_MIRROR?.trim();n&&(i=n.replace(/\/$/,"")+"/"+i);let s=await Mt(i,{timeoutMs:_D,dispatcher:AD});if(!s.ok)throw new Error(`Failed to download archive: ${s.status} ${s.statusText}`);let o=s.headers.get("Content-Length"),l=o?parseInt(o,10):void 0,p=0,Nu=s.body?.getReader();if(!Nu){let g=await s.arrayBuffer(),d=await Tt(v(Pt(),"create-app-")),$u=v(d,"repo.tar.gz");await wt($u,new Uint8Array(g));let Wt=v(d,`${r.repo}-${r.branch}`,r.examplesPath,t);return await St({file:$u,cwd:d}),{templatePath:Wt,cleanup:async()=>{await Rt(d,{recursive:!0,force:!0})}}}let Gu=[];for(;;){let{done:g,value:d}=await Nu.read();if(g)break;Gu.push(d),p+=d.length,e?.(p,l)}let Mu=new Uint8Array(p),Lu=0;for(let g of Gu)Mu.set(g,Lu),Lu+=g.length;let G=await Tt(v(Pt(),"create-app-")),ku=v(G,"repo.tar.gz");await wt(ku,Mu),await St({file:ku,cwd:G});let jt=`${r.repo}-${r.branch}`;return{templatePath:v(G,jt,r.examplesPath,t),cleanup:async()=>{await Rt(G,{recursive:!0,force:!0})}}}import{existsSync as kD}from"fs";import{readFileSync as yD,writeFileSync as vD,readdirSync as TD,statSync as wD}from"fs";import{join as RD}from"path";var PD="https://registry.npmjs.org",SD=["dependencies","devDependencies","peerDependencies","optionalDependencies"];function ID(t){let u=t.includes("/")?t.replace("/","%2F"):encodeURIComponent(t);return`${PD}/${u}/latest`}async function OD(t){try{let u=ID(t),e=await fetch(u);if(!e.ok)return"latest";let D=await e.json();return typeof D?.version=="string"?D.version:"latest"}catch{return"latest"}}async function ND(t){let u=yD(t,"utf-8"),e;try{e=JSON.parse(u)}catch{return}let D=!1;for(let r of SD){let i=e[r];if(!(!i||typeof i!="object"))for(let n of Object.keys(i)){if(i[n]!=="workspace:*")continue;let s=await OD(n);i[n]=s,D=!0}}D&&vD(t,JSON.stringify(e,null,2),"utf-8")}async function Iu(t){let u=TD(t);for(let e of u){if(e==="node_modules")continue;let D=RD(t,e);wD(D).isDirectory()?await Iu(D):e==="package.json"&&await ND(D)}}var uu=class{ora;context;templateList;copyer;constructor(u){this.ora=ht,this.context=new GD("create-app",u),this.templateList=this.context.options.templateList??[],this.copyer=new Z}get logger(){return this.context.logger}async steps(u){try{return await MD.prompt(u)}catch(e){throw e.isTtyError,e.name==="ExitPromptError"||this.logger.error(e),e}}async action({label:u,task:e}){let D=e();D instanceof Promise||(D=Promise.resolve(D));let r=u;return this.ora(D,r),D}async getGeneratorContext(){let u=Wu(this.templateList),e=await this.steps(u);return e.targetPath=LD(process.cwd(),e.projectName),e}async generate(){let u=await this.getGeneratorContext();if(this.logger.debug("context is:",u),kD(u.targetPath))throw new Error(`The directory already exists: ${u.targetPath}. Please choose another project name or remove the existing directory.`);let e,D,r="Download template from GitHub",i=K(r).start();try{let n=await kt(u.template,{onProgress:(s,o)=>{if(o!=null&&o>0)i.text=`${r} ${Math.round(s/o*100)}%`;else{let l=(s/1024/1024).toFixed(2);i.text=`${r} ${l} MB`}}});e=n.templatePath,D=n.cleanup,i.succeed(r)}catch(n){throw i.fail(r),n}await this.action({label:"Generate directory",task:async()=>{try{await this.copyer.copyPaths({sourcePath:e,targetPath:u.targetPath,ignorePath:e})}finally{await D()}}}),await this.action({label:"Replace workspace:* with concrete versions",task:async()=>{await Iu(u.targetPath)}})}};var Ou={name:"@qlover/create-app",version:"2.0.5",description:"Create a new app with a single command",private:!1,type:"module",files:["dist","package.json","README.md","CHANGELOG.md"],bin:{"create-app":"dist/index.js"},scripts:{lint:"eslint src --fix",build:"tsup","type-check":"tsc --noEmit","create:app":"node ./dist/index.js"},repository:{type:"git",url:"git+https://github.com/qlover/fe-base.git",directory:"packages/create-app"},homepage:"https://github.com/qlover/fe-base#readme",keywords:["create-app","fe-scripts","scripts"],author:"qlover",license:"ISC",publishConfig:{access:"public"},devDependencies:{"@qlover/logger":"workspace:*",ignore:"^7.0.3",ora:"^8.1.1"},dependencies:{"@qlover/scripts-context":"workspace:*",commander:"^13.1.0",inquirer:"^12.3.2",tar:"^7.5.0",undici:"^7.0.0"}};function WD(){let t=new jD;return t.version(Ou.version,"-v, --version","Show version").description(Ou.description).option("-d, --dry-run","Do not touch or write anything, but show the commands").option("-V, --verbose","Show more information"),t.parse(),t.opts()}async function $t(){let{dryRun:t,verbose:u}=WD(),e=await Lt();e?.length||(console.error("No templates found from GitHub examples."),process.exit(1));let D={templateList:e};await new uu({dryRun:t,verbose:u,options:D}).generate()}$t().catch(t=>{t.name==="ExitPromptError"&&process.exit(130),console.error(t),process.exit(1)});
11
+ `).map(n=>n.trim()).filter(n=>n&&!n.startsWith("#"));return(0,vt.default)().add(i)}async copyFiles(u,e,D,r){let i=await yt.readdir(u);await Promise.all(i.map(async n=>{let s=Su(u,n),o=Su(e,n);if(D&&D.ignores(n))return;if(N.ensureDir(lD(o)),(await CD(s)).isDirectory())await this.copyFiles(s,o,D,r);else{if(r&&await r(s,o))return;await ED(s,o)}}))}copyPaths({sourcePath:u,targetPath:e,copyCallback:D,ignorePath:r}){N.ensureDir(e);let i=this.getIg(r??this.ignoreTargetPath);return this.copyFiles(u,e,i,D)}};import{Agent as Ot,fetch as hD}from"undici";import{join as v}from"path";import{mkdtemp as Tt,writeFile as wt,rm as Rt}from"fs/promises";import{tmpdir as Pt}from"os";import{extract as St}from"tar";var mD="https://api.github.com",pD="https://github.com/{owner}/{repo}/archive/refs/heads/{branch}.tar.gz",dD=3e4,_D=12e4,Nt=6e4,It=3,gD=2e3,BD=new Ot({connectTimeout:Nt}),AD=new Ot({connectTimeout:Nt}),Gt={owner:"qlover",repo:"fe-base",branch:"master",examplesPath:"examples"};function xD(t){let u=new AbortController,e=setTimeout(()=>u.abort(),t),D=u.signal;return D._clear=()=>clearTimeout(e),D}function bD(t){return new Promise(u=>setTimeout(u,t))}async function Mt(t,u){let{timeoutMs:e,dispatcher:D,...r}=u,i=D?hD:fetch,n;for(let s=1;s<=It;s++){let o=xD(e);try{let l={...r,signal:o,...D&&{dispatcher:D}},p=await i(t,l);return typeof o._clear=="function"&&o._clear?.(),p}catch(l){n=l,s<It&&await bD(gD)}}throw n}async function Lt(t={}){let u={...Gt,...t},e=`${mD}/repos/${u.owner}/${u.repo}/contents/${u.examplesPath}`,D=await Mt(e,{timeoutMs:dD,dispatcher:BD,headers:{Accept:"application/vnd.github.v3+json"}});if(!D.ok)throw new Error(`Failed to fetch template list: ${D.status} ${D.statusText}`);return(await D.json()).filter(i=>i.type==="dir").map(i=>i.name)}async function kt(t,u={}){let{onProgress:e,...D}=u,r={...Gt,...D},i=pD.replace("{owner}",r.owner).replace("{repo}",r.repo).replace("{branch}",r.branch),n=process.env.CREATE_APP_GITHUB_MIRROR?.trim();n&&(i=n.replace(/\/$/,"")+"/"+i);let s=await Mt(i,{timeoutMs:_D,dispatcher:AD});if(!s.ok)throw new Error(`Failed to download archive: ${s.status} ${s.statusText}`);let o=s.headers.get("Content-Length"),l=o?parseInt(o,10):void 0,p=0,Nu=s.body?.getReader();if(!Nu){let g=await s.arrayBuffer(),d=await Tt(v(Pt(),"create-app-")),$u=v(d,"repo.tar.gz");await wt($u,new Uint8Array(g));let Wt=v(d,`${r.repo}-${r.branch}`,r.examplesPath,t);return await St({file:$u,cwd:d}),{templatePath:Wt,cleanup:async()=>{await Rt(d,{recursive:!0,force:!0})}}}let Gu=[];for(;;){let{done:g,value:d}=await Nu.read();if(g)break;Gu.push(d),p+=d.length,e?.(p,l)}let Mu=new Uint8Array(p),Lu=0;for(let g of Gu)Mu.set(g,Lu),Lu+=g.length;let G=await Tt(v(Pt(),"create-app-")),ku=v(G,"repo.tar.gz");await wt(ku,Mu),await St({file:ku,cwd:G});let jt=`${r.repo}-${r.branch}`;return{templatePath:v(G,jt,r.examplesPath,t),cleanup:async()=>{await Rt(G,{recursive:!0,force:!0})}}}import{existsSync as kD}from"fs";import{readFileSync as yD,writeFileSync as vD,readdirSync as TD,statSync as wD}from"fs";import{join as RD}from"path";var PD="https://registry.npmjs.org",SD=["dependencies","devDependencies","peerDependencies","optionalDependencies"];function ID(t){let u=t.includes("/")?t.replace("/","%2F"):encodeURIComponent(t);return`${PD}/${u}/latest`}async function OD(t){try{let u=ID(t),e=await fetch(u);if(!e.ok)return"latest";let D=await e.json();return typeof D?.version=="string"?D.version:"latest"}catch{return"latest"}}async function ND(t){let u=yD(t,"utf-8"),e;try{e=JSON.parse(u)}catch{return}let D=!1;for(let r of SD){let i=e[r];if(!(!i||typeof i!="object"))for(let n of Object.keys(i)){if(i[n]!=="workspace:*")continue;let s=await OD(n);i[n]=s,D=!0}}D&&vD(t,JSON.stringify(e,null,2),"utf-8")}async function Iu(t){let u=TD(t);for(let e of u){if(e==="node_modules")continue;let D=RD(t,e);wD(D).isDirectory()?await Iu(D):e==="package.json"&&await ND(D)}}var uu=class{ora;context;templateList;copyer;constructor(u){this.ora=ht,this.context=new GD("create-app",u),this.templateList=this.context.options.templateList??[],this.copyer=new Z}get logger(){return this.context.logger}async steps(u){try{return await MD.prompt(u)}catch(e){throw e.isTtyError,e.name==="ExitPromptError"||this.logger.error(e),e}}async action({label:u,task:e}){let D=e();D instanceof Promise||(D=Promise.resolve(D));let r=u;return this.ora(D,r),D}async getGeneratorContext(){let u=Wu(this.templateList),e=await this.steps(u);return e.targetPath=LD(process.cwd(),e.projectName),e}async generate(){let u=await this.getGeneratorContext();if(this.logger.debug("context is:",u),kD(u.targetPath))throw new Error(`The directory already exists: ${u.targetPath}. Please choose another project name or remove the existing directory.`);let e,D,r="Download template from GitHub",i=K(r).start();try{let n=await kt(u.template,{onProgress:(s,o)=>{if(o!=null&&o>0)i.text=`${r} ${Math.round(s/o*100)}%`;else{let l=(s/1024/1024).toFixed(2);i.text=`${r} ${l} MB`}}});e=n.templatePath,D=n.cleanup,i.succeed(r)}catch(n){throw i.fail(r),n}await this.action({label:"Generate directory",task:async()=>{try{await this.copyer.copyPaths({sourcePath:e,targetPath:u.targetPath,ignorePath:e})}finally{await D()}}}),await this.action({label:"Replace workspace:* with concrete versions",task:async()=>{await Iu(u.targetPath)}})}};var Ou={name:"@qlover/create-app",version:"3.0.0",description:"Create a new app with a single command",private:!1,type:"module",files:["dist","package.json","README.md","CHANGELOG.md"],bin:{"create-app":"dist/index.js"},scripts:{lint:"eslint src --fix",build:"tsup","type-check":"tsc --noEmit","create:app":"node ./dist/index.js"},repository:{type:"git",url:"git+https://github.com/qlover/fe-base.git",directory:"packages/create-app"},homepage:"https://github.com/qlover/fe-base#readme",keywords:["create-app","fe-scripts","scripts"],author:"qlover",license:"ISC",publishConfig:{access:"public"},devDependencies:{"@qlover/logger":"workspace:*",ignore:"^7.0.3",ora:"^8.1.1"},dependencies:{"@qlover/scripts-context":"workspace:*",commander:"^13.1.0",inquirer:"^12.3.2",tar:"^7.5.0",undici:"^7.0.0"}};function WD(){let t=new jD;return t.version(Ou.version,"-v, --version","Show version").description(Ou.description).option("-d, --dry-run","Do not touch or write anything, but show the commands").option("-V, --verbose","Show more information"),t.parse(),t.opts()}async function $t(){let{dryRun:t,verbose:u}=WD(),e=await Lt();e?.length||(console.error("No templates found from GitHub examples."),process.exit(1));let D={templateList:e};await new uu({dryRun:t,verbose:u,options:D}).generate()}$t().catch(t=>{t.name==="ExitPromptError"&&process.exit(130),console.error(t),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qlover/create-app",
3
- "version": "2.0.5",
3
+ "version": "3.0.0",
4
4
  "description": "Create a new app with a single command",
5
5
  "private": false,
6
6
  "type": "module",