liuliu_cli 1.0.0 → 1.0.1

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/README.md CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  - 用于快速搭建前端项目的命令行工具
8
8
 
9
+
9
10
  ## 模板
10
11
 
11
12
  - 快速生成vue3+TS+VITE+ElmentPlus+Pina+Axios+Mock的模板
@@ -28,4 +29,12 @@ dawei create [projectName]
28
29
  #查看脚手架版本
29
30
  dawei -v
30
31
  dawei -version
32
+ ```
33
+
34
+ ## npm 包
31
35
 
36
+ 1. 让项目累加版本号
37
+ 1. package.json 内 0.0.1+
38
+ 2. npm version patch
39
+ 2. 发布新版本
40
+ 1. npm publish
@@ -0,0 +1,12 @@
1
+ export interface TemplateInfo {
2
+ name: string;
3
+ downloadUrl: string;
4
+ description: string;
5
+ branch: string;
6
+ }
7
+ export declare function isOverwrite(fileName: string): Promise<boolean> & {
8
+ cancel: () => void;
9
+ };
10
+ export declare const templateList: Map<string, TemplateInfo>;
11
+ export declare function create(projectName?: string): Promise<void>;
12
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/command/create.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAA;CACjB;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM;;EAS3C;AAED,eAAO,MAAM,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAajD,CAAA;AAEF,wBAAsB,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,iBA8ChD"}
package/bin/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare function add(a: number, b: number): number;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,UAEvC"}
package/bin/index.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";var e=require("commander"),o=require("path"),r=require("fs-extra"),t=require("@inquirer/prompts"),n=require("simple-git"),s=require("progress-estimator");const l=(e=0)=>o=>`[${o+e}m`,i=(e=0)=>o=>`[${38+e};5;${o}m`,c=(e=0)=>(o,r,t)=>`[${38+e};2;${o};${r};${t}m`,a={modifier:{reset:[0,0],bold:[1,22],dim:[2,22],italic:[3,23],underline:[4,24],overline:[53,55],inverse:[7,27],hidden:[8,28],strikethrough:[9,29]},color:{black:[30,39],red:[31,39],green:[32,39],yellow:[33,39],blue:[34,39],magenta:[35,39],cyan:[36,39],white:[37,39],blackBright:[90,39],gray:[90,39],grey:[90,39],redBright:[91,39],greenBright:[92,39],yellowBright:[93,39],blueBright:[94,39],magentaBright:[95,39],cyanBright:[96,39],whiteBright:[97,39]},bgColor:{bgBlack:[40,49],bgRed:[41,49],bgGreen:[42,49],bgYellow:[43,49],bgBlue:[44,49],bgMagenta:[45,49],bgCyan:[46,49],bgWhite:[47,49],bgBlackBright:[100,49],bgGray:[100,49],bgGrey:[100,49],bgRedBright:[101,49],bgGreenBright:[102,49],bgYellowBright:[103,49],bgBlueBright:[104,49],bgMagentaBright:[105,49],bgCyanBright:[106,49],bgWhiteBright:[107,49]}};Object.keys(a.modifier);Object.keys(a.color),Object.keys(a.bgColor);const g=function(){const e=new Map;for(const[o,r]of Object.entries(a)){for(const[o,t]of Object.entries(r))a[o]={open:`[${t[0]}m`,close:`[${t[1]}m`},r[o]=a[o],e.set(t[0],t[1]);Object.defineProperty(a,o,{value:r,enumerable:!1})}return Object.defineProperty(a,"codes",{value:e,enumerable:!1}),a.color.close="",a.bgColor.close="",a.color.ansi=l(),a.color.ansi256=i(),a.color.ansi16m=c(),a.bgColor.ansi=l(10),a.bgColor.ansi256=i(10),a.bgColor.ansi16m=c(10),Object.defineProperties(a,{rgbToAnsi256:{value:(e,o,r)=>e===o&&o===r?e<8?16:e>248?231:Math.round((e-8)/247*24)+232:16+36*Math.round(e/255*5)+6*Math.round(o/255*5)+Math.round(r/255*5),enumerable:!1},hexToRgb:{value(e){const o=/[a-f\d]{6}|[a-f\d]{3}/i.exec(e.toString(16));if(!o)return[0,0,0];let[r]=o;3===r.length&&(r=[...r].map(e=>e+e).join(""));const t=Number.parseInt(r,16);return[t>>16&255,t>>8&255,255&t]},enumerable:!1},hexToAnsi256:{value:e=>a.rgbToAnsi256(...a.hexToRgb(e)),enumerable:!1},ansi256ToAnsi:{value(e){if(e<8)return 30+e;if(e<16)return e-8+90;let o,r,t;if(e>=232)o=(10*(e-232)+8)/255,r=o,t=o;else{const n=(e-=16)%36;o=Math.floor(e/36)/5,r=Math.floor(n/6)/5,t=n%6/5}const n=2*Math.max(o,r,t);if(0===n)return 30;let s=30+(Math.round(t)<<2|Math.round(r)<<1|Math.round(o));return 2===n&&(s+=60),s},enumerable:!1},rgbToAnsi:{value:(e,o,r)=>a.ansi256ToAnsi(a.rgbToAnsi256(e,o,r)),enumerable:!1},hexToAnsi:{value:e=>a.ansi256ToAnsi(a.hexToAnsi256(e)),enumerable:!1}}),a}(),u=(()=>{if(!("navigator"in globalThis))return 0;if(globalThis.navigator.userAgentData){const e=navigator.userAgentData.brands.find(({brand:e})=>"Chromium"===e);if(e&&e.version>93)return 3}return/\b(Chrome|Chromium)\//.test(globalThis.navigator.userAgent)?1:0})(),b=0!==u&&{level:u},h={stdout:b,stderr:b};function m(e,o,r){let t=e.indexOf(o);if(-1===t)return e;const n=o.length;let s=0,l="";do{l+=e.slice(s,t)+o+r,s=t+n,t=e.indexOf(o,s)}while(-1!==t);return l+=e.slice(s),l}const{stdout:d,stderr:p}=h,v=Symbol("GENERATOR"),f=Symbol("STYLER"),y=Symbol("IS_EMPTY"),w=["ansi","ansi","ansi256","ansi16m"],O=Object.create(null),C=e=>{const o=(...e)=>e.join(" ");return((e,o={})=>{if(o.level&&!(Number.isInteger(o.level)&&o.level>=0&&o.level<=3))throw new Error("The `level` option should be an integer from 0 to 3");const r=d?d.level:0;e.level=void 0===o.level?r:o.level})(o,e),Object.setPrototypeOf(o,T.prototype),o};function T(e){return C(e)}Object.setPrototypeOf(T.prototype,Function.prototype);for(const[e,o]of Object.entries(g))O[e]={get(){const r=x(this,j(o.open,o.close,this[f]),this[y]);return Object.defineProperty(this,e,{value:r}),r}};O.visible={get(){const e=x(this,this[f],!0);return Object.defineProperty(this,"visible",{value:e}),e}};const A=(e,o,r,...t)=>"rgb"===e?"ansi16m"===o?g[r].ansi16m(...t):"ansi256"===o?g[r].ansi256(g.rgbToAnsi256(...t)):g[r].ansi(g.rgbToAnsi(...t)):"hex"===e?A("rgb",o,r,...g.hexToRgb(...t)):g[r][e](...t),B=["rgb","hex","ansi256"];for(const e of B){O[e]={get(){const{level:o}=this;return function(...r){const t=j(A(e,w[o],"color",...r),g.color.close,this[f]);return x(this,t,this[y])}}};O["bg"+e[0].toUpperCase()+e.slice(1)]={get(){const{level:o}=this;return function(...r){const t=j(A(e,w[o],"bgColor",...r),g.bgColor.close,this[f]);return x(this,t,this[y])}}}}const R=Object.defineProperties(()=>{},{...O,level:{enumerable:!0,get(){return this[v].level},set(e){this[v].level=e}}}),j=(e,o,r)=>{let t,n;return void 0===r?(t=e,n=o):(t=r.openAll+e,n=o+r.closeAll),{open:e,close:o,openAll:t,closeAll:n,parent:r}},x=(e,o,r)=>{const t=(...e)=>k(t,1===e.length?""+e[0]:e.join(" "));return Object.setPrototypeOf(t,R),t[v]=e,t[f]=o,t[y]=r,t},k=(e,o)=>{if(e.level<=0||!o)return e[y]?"":o;let r=e[f];if(void 0===r)return o;const{openAll:t,closeAll:n}=r;if(o.includes(""))for(;void 0!==r;)o=m(o,r.close,r.open),r=r.parent;const s=o.indexOf("\n");return-1!==s&&(o=function(e,o,r,t){let n=0,s="";do{const l="\r"===e[t-1];s+=e.slice(n,l?t-1:t)+o+(l?"\r\n":"\n")+r,n=t+1,t=e.indexOf("\n",n)}while(-1!==t);return s+=e.slice(n),s}(o,n,t,s)),t+o+n};Object.defineProperties(T.prototype,O);const M=T();T({level:p?p.level:0}),process.env.FORCE_COLOR||(process.env.FORCE_COLOR="1"),M.level=3;const S=s({spinner:{interval:100,frames:["-","+","-"].map(e=>M.green(e))}});process.cwd();process.env.FORCE_COLOR||(process.env.FORCE_COLOR="1"),M.level=3;const P=new Map([["Vite-Vue3-Typescript-template",{name:"Vite-Vue-Type",downloadUrl:"https://gitee.com/sohucw/admin-pro.git",description:"Vue3技术开发模板1",branch:"dev11"}],["Vite-Vue3-Typescript-template2",{name:"Vite-Vue-Type",downloadUrl:"git@gitee.com:sohucw/admin-pro.git",description:"Vue3技术开发模板2",branch:"dev10"}]]);async function E(e){const s=Array.from(P).map(e=>{const[o,r]=e;return{name:o,value:o,description:r.description}});e||(e=await t.input({message:"请输入项目名称"}));const l=o.resolve(process.cwd(),e);if(r.existsSync(l)){if(!await(i=e,console.warn(`${i}文件夹存在`),t.select({message:"是否覆盖?",choices:[{name:"覆盖",value:!0},{name:"取消",value:!1}]})))return;await r.remove(l)}var i;const c=await t.select({message:"请选择模板",choices:s}),a=P.get(c);if(a)try{await(async(e,o,r)=>{const t=n();try{await S(t.clone(e,o,r),"代码下载中",{estimate:7e3}),console.log(),console.log(M.green("代码下载成功")),console.log(M.blackBright("========================")),console.log(M.blackBright("==欢迎使用 DALIU脚手架===")),console.log(M.blackBright("========================")),console.log(),console.log(),console.log(M.blackBright("====使用npm install安装依赖========="))}catch(e){throw console.error(M.red("下载失败")),e?.message?.includes("Host key verification failed")?(console.error(M.yellow("\n提示:SSH 主机密钥验证失败")),console.error(M.yellow("解决方案:")),console.error(M.yellow("1. 如果使用 SSH URL,请先配置 SSH 密钥:")),console.error(M.yellow(' ssh-keygen -t rsa -C "your_email@example.com"')),console.error(M.yellow(" 然后将公钥添加到 Git 服务器")),console.error(M.yellow("2. 或者使用 HTTPS URL(需要输入用户名密码)")),console.error(M.yellow("3. 临时解决:手动添加主机密钥")),console.error(M.yellow(" ssh-keyscan gitee.com >> ~/.ssh/known_hosts"))):e?.message?.includes("Could not read from remote repository")?(console.error(M.yellow("\n提示:无法访问远程仓库")),console.error(M.yellow("请检查:")),console.error(M.yellow("1. 仓库地址是否正确")),console.error(M.yellow("2. 是否有访问权限")),console.error(M.yellow("3. 网络连接是否正常"))):(console.error(M.red("\n错误详情:")),console.error(e)),e}})(a.downloadUrl,e,["-b",a.branch])}catch(e){process.exit(1)}else console.error(M.red("未找到模板信息")),process.exit(1)}process.env.FORCE_COLOR||(process.env.FORCE_COLOR="1"),M.level=3;const L=new e.Command("dawei");L.version("1.0.0","-v,--version"),L.command("create").description("创建一个新项目").argument("[name]","项目名称").action(async e=>{E(e)}),L.parse(),exports.add=function(e,o){return e+o};
@@ -0,0 +1,9 @@
1
+ export interface TemplateInfo {
2
+ name: string;
3
+ downloadUrl: string;
4
+ description: string;
5
+ branch: string;
6
+ }
7
+ export declare const templateList: Map<string, TemplateInfo>;
8
+ export declare function create(projectName?: string): Promise<void>;
9
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/command/create.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAA;CACjB;AAED,eAAO,MAAM,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAajD,CAAA;AAEF,wBAAsB,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,iBAkChD"}
@@ -0,0 +1,2 @@
1
+ export declare function add(a: number, b: number): number;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAYA,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,UAEvC"}
@@ -0,0 +1,2 @@
1
+ export declare const clone: (url: string, projectName: string, options: string[]) => Promise<void>;
2
+ //# sourceMappingURL=clone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clone.d.ts","sourceRoot":"","sources":["../../../src/utils/clone.ts"],"names":[],"mappings":"AAgCA,eAAO,MAAM,KAAK,GAAU,KAAK,MAAM,EAAE,aAAa,MAAM,EAAE,SAAS,MAAM,EAAE,kBAuC9E,CAAA"}
@@ -0,0 +1,2 @@
1
+ export declare const clone: (url: string, projectName: string, options: string[]) => Promise<void>;
2
+ //# sourceMappingURL=clone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clone.d.ts","sourceRoot":"","sources":["../../src/utils/clone.ts"],"names":[],"mappings":"AAgCA,eAAO,MAAM,KAAK,GAAU,KAAK,MAAM,EAAE,aAAa,MAAM,EAAE,SAAS,MAAM,EAAE,kBAuC9E,CAAA"}
@@ -0,0 +1,12 @@
1
+ export interface TemplateInfo {
2
+ name: string;
3
+ downloadUrl: string;
4
+ description: string;
5
+ branch: string;
6
+ }
7
+ export declare function isOverwrite(fileName: string): Promise<boolean> & {
8
+ cancel: () => void;
9
+ };
10
+ export declare const templateList: Map<string, TemplateInfo>;
11
+ export declare function create(projectName?: string): Promise<void>;
12
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/command/create.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAA;CACjB;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM;;EAS3C;AAED,eAAO,MAAM,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAajD,CAAA;AAEF,wBAAsB,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,iBA8ChD"}
@@ -0,0 +1,2 @@
1
+ export declare function add(a: number, b: number): number;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,UAEvC"}
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";var e=require("commander"),o=require("path"),r=require("fs-extra"),t=require("@inquirer/prompts"),n=require("simple-git"),s=require("progress-estimator");const l=(e=0)=>o=>`[${o+e}m`,i=(e=0)=>o=>`[${38+e};5;${o}m`,c=(e=0)=>(o,r,t)=>`[${38+e};2;${o};${r};${t}m`,a={modifier:{reset:[0,0],bold:[1,22],dim:[2,22],italic:[3,23],underline:[4,24],overline:[53,55],inverse:[7,27],hidden:[8,28],strikethrough:[9,29]},color:{black:[30,39],red:[31,39],green:[32,39],yellow:[33,39],blue:[34,39],magenta:[35,39],cyan:[36,39],white:[37,39],blackBright:[90,39],gray:[90,39],grey:[90,39],redBright:[91,39],greenBright:[92,39],yellowBright:[93,39],blueBright:[94,39],magentaBright:[95,39],cyanBright:[96,39],whiteBright:[97,39]},bgColor:{bgBlack:[40,49],bgRed:[41,49],bgGreen:[42,49],bgYellow:[43,49],bgBlue:[44,49],bgMagenta:[45,49],bgCyan:[46,49],bgWhite:[47,49],bgBlackBright:[100,49],bgGray:[100,49],bgGrey:[100,49],bgRedBright:[101,49],bgGreenBright:[102,49],bgYellowBright:[103,49],bgBlueBright:[104,49],bgMagentaBright:[105,49],bgCyanBright:[106,49],bgWhiteBright:[107,49]}};Object.keys(a.modifier);Object.keys(a.color),Object.keys(a.bgColor);const g=function(){const e=new Map;for(const[o,r]of Object.entries(a)){for(const[o,t]of Object.entries(r))a[o]={open:`[${t[0]}m`,close:`[${t[1]}m`},r[o]=a[o],e.set(t[0],t[1]);Object.defineProperty(a,o,{value:r,enumerable:!1})}return Object.defineProperty(a,"codes",{value:e,enumerable:!1}),a.color.close="",a.bgColor.close="",a.color.ansi=l(),a.color.ansi256=i(),a.color.ansi16m=c(),a.bgColor.ansi=l(10),a.bgColor.ansi256=i(10),a.bgColor.ansi16m=c(10),Object.defineProperties(a,{rgbToAnsi256:{value:(e,o,r)=>e===o&&o===r?e<8?16:e>248?231:Math.round((e-8)/247*24)+232:16+36*Math.round(e/255*5)+6*Math.round(o/255*5)+Math.round(r/255*5),enumerable:!1},hexToRgb:{value(e){const o=/[a-f\d]{6}|[a-f\d]{3}/i.exec(e.toString(16));if(!o)return[0,0,0];let[r]=o;3===r.length&&(r=[...r].map(e=>e+e).join(""));const t=Number.parseInt(r,16);return[t>>16&255,t>>8&255,255&t]},enumerable:!1},hexToAnsi256:{value:e=>a.rgbToAnsi256(...a.hexToRgb(e)),enumerable:!1},ansi256ToAnsi:{value(e){if(e<8)return 30+e;if(e<16)return e-8+90;let o,r,t;if(e>=232)o=(10*(e-232)+8)/255,r=o,t=o;else{const n=(e-=16)%36;o=Math.floor(e/36)/5,r=Math.floor(n/6)/5,t=n%6/5}const n=2*Math.max(o,r,t);if(0===n)return 30;let s=30+(Math.round(t)<<2|Math.round(r)<<1|Math.round(o));return 2===n&&(s+=60),s},enumerable:!1},rgbToAnsi:{value:(e,o,r)=>a.ansi256ToAnsi(a.rgbToAnsi256(e,o,r)),enumerable:!1},hexToAnsi:{value:e=>a.ansi256ToAnsi(a.hexToAnsi256(e)),enumerable:!1}}),a}(),u=(()=>{if(!("navigator"in globalThis))return 0;if(globalThis.navigator.userAgentData){const e=navigator.userAgentData.brands.find(({brand:e})=>"Chromium"===e);if(e&&e.version>93)return 3}return/\b(Chrome|Chromium)\//.test(globalThis.navigator.userAgent)?1:0})(),b=0!==u&&{level:u},h={stdout:b,stderr:b};function m(e,o,r){let t=e.indexOf(o);if(-1===t)return e;const n=o.length;let s=0,l="";do{l+=e.slice(s,t)+o+r,s=t+n,t=e.indexOf(o,s)}while(-1!==t);return l+=e.slice(s),l}const{stdout:d,stderr:p}=h,v=Symbol("GENERATOR"),f=Symbol("STYLER"),y=Symbol("IS_EMPTY"),w=["ansi","ansi","ansi256","ansi16m"],O=Object.create(null),C=e=>{const o=(...e)=>e.join(" ");return((e,o={})=>{if(o.level&&!(Number.isInteger(o.level)&&o.level>=0&&o.level<=3))throw new Error("The `level` option should be an integer from 0 to 3");const r=d?d.level:0;e.level=void 0===o.level?r:o.level})(o,e),Object.setPrototypeOf(o,T.prototype),o};function T(e){return C(e)}Object.setPrototypeOf(T.prototype,Function.prototype);for(const[e,o]of Object.entries(g))O[e]={get(){const r=x(this,j(o.open,o.close,this[f]),this[y]);return Object.defineProperty(this,e,{value:r}),r}};O.visible={get(){const e=x(this,this[f],!0);return Object.defineProperty(this,"visible",{value:e}),e}};const A=(e,o,r,...t)=>"rgb"===e?"ansi16m"===o?g[r].ansi16m(...t):"ansi256"===o?g[r].ansi256(g.rgbToAnsi256(...t)):g[r].ansi(g.rgbToAnsi(...t)):"hex"===e?A("rgb",o,r,...g.hexToRgb(...t)):g[r][e](...t),B=["rgb","hex","ansi256"];for(const e of B){O[e]={get(){const{level:o}=this;return function(...r){const t=j(A(e,w[o],"color",...r),g.color.close,this[f]);return x(this,t,this[y])}}};O["bg"+e[0].toUpperCase()+e.slice(1)]={get(){const{level:o}=this;return function(...r){const t=j(A(e,w[o],"bgColor",...r),g.bgColor.close,this[f]);return x(this,t,this[y])}}}}const R=Object.defineProperties(()=>{},{...O,level:{enumerable:!0,get(){return this[v].level},set(e){this[v].level=e}}}),j=(e,o,r)=>{let t,n;return void 0===r?(t=e,n=o):(t=r.openAll+e,n=o+r.closeAll),{open:e,close:o,openAll:t,closeAll:n,parent:r}},x=(e,o,r)=>{const t=(...e)=>k(t,1===e.length?""+e[0]:e.join(" "));return Object.setPrototypeOf(t,R),t[v]=e,t[f]=o,t[y]=r,t},k=(e,o)=>{if(e.level<=0||!o)return e[y]?"":o;let r=e[f];if(void 0===r)return o;const{openAll:t,closeAll:n}=r;if(o.includes(""))for(;void 0!==r;)o=m(o,r.close,r.open),r=r.parent;const s=o.indexOf("\n");return-1!==s&&(o=function(e,o,r,t){let n=0,s="";do{const l="\r"===e[t-1];s+=e.slice(n,l?t-1:t)+o+(l?"\r\n":"\n")+r,n=t+1,t=e.indexOf("\n",n)}while(-1!==t);return s+=e.slice(n),s}(o,n,t,s)),t+o+n};Object.defineProperties(T.prototype,O);const M=T();T({level:p?p.level:0}),process.env.FORCE_COLOR||(process.env.FORCE_COLOR="1"),M.level=3;const S=s({spinner:{interval:100,frames:["-","+","-"].map(e=>M.green(e))}});process.cwd();process.env.FORCE_COLOR||(process.env.FORCE_COLOR="1"),M.level=3;const P=new Map([["Vite-Vue3-Typescript-template",{name:"Vite-Vue-Type",downloadUrl:"https://gitee.com/sohucw/admin-pro.git",description:"Vue3技术开发模板1",branch:"dev11"}],["Vite-Vue3-Typescript-template2",{name:"Vite-Vue-Type",downloadUrl:"git@gitee.com:sohucw/admin-pro.git",description:"Vue3技术开发模板2",branch:"dev10"}]]);async function E(e){const s=Array.from(P).map(e=>{const[o,r]=e;return{name:o,value:o,description:r.description}});e||(e=await t.input({message:"请输入项目名称"}));const l=o.resolve(process.cwd(),e);if(r.existsSync(l)){if(!await(i=e,console.warn(`${i}文件夹存在`),t.select({message:"是否覆盖?",choices:[{name:"覆盖",value:!0},{name:"取消",value:!1}]})))return;await r.remove(l)}var i;const c=await t.select({message:"请选择模板",choices:s}),a=P.get(c);if(a)try{await(async(e,o,r)=>{const t=n();try{await S(t.clone(e,o,r),"代码下载中",{estimate:7e3}),console.log(),console.log(M.green("代码下载成功")),console.log(M.blackBright("========================")),console.log(M.blackBright("==欢迎使用 DALIU脚手架===")),console.log(M.blackBright("========================")),console.log(),console.log(),console.log(M.blackBright("====使用npm install安装依赖========="))}catch(e){throw console.error(M.red("下载失败")),e?.message?.includes("Host key verification failed")?(console.error(M.yellow("\n提示:SSH 主机密钥验证失败")),console.error(M.yellow("解决方案:")),console.error(M.yellow("1. 如果使用 SSH URL,请先配置 SSH 密钥:")),console.error(M.yellow(' ssh-keygen -t rsa -C "your_email@example.com"')),console.error(M.yellow(" 然后将公钥添加到 Git 服务器")),console.error(M.yellow("2. 或者使用 HTTPS URL(需要输入用户名密码)")),console.error(M.yellow("3. 临时解决:手动添加主机密钥")),console.error(M.yellow(" ssh-keyscan gitee.com >> ~/.ssh/known_hosts"))):e?.message?.includes("Could not read from remote repository")?(console.error(M.yellow("\n提示:无法访问远程仓库")),console.error(M.yellow("请检查:")),console.error(M.yellow("1. 仓库地址是否正确")),console.error(M.yellow("2. 是否有访问权限")),console.error(M.yellow("3. 网络连接是否正常"))):(console.error(M.red("\n错误详情:")),console.error(e)),e}})(a.downloadUrl,e,["-b",a.branch])}catch(e){process.exit(1)}else console.error(M.red("未找到模板信息")),process.exit(1)}process.env.FORCE_COLOR||(process.env.FORCE_COLOR="1"),M.level=3;const L=new e.Command("dawei");L.version("1.0.0","-v,--version"),L.command("create").description("创建一个新项目").argument("[name]","项目名称").action(async e=>{E(e)}),L.parse(),exports.add=function(e,o){return e+o};
@@ -0,0 +1,9 @@
1
+ export interface TemplateInfo {
2
+ name: string;
3
+ downloadUrl: string;
4
+ description: string;
5
+ branch: string;
6
+ }
7
+ export declare const templateList: Map<string, TemplateInfo>;
8
+ export declare function create(projectName?: string): Promise<void>;
9
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/command/create.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAA;CACjB;AAED,eAAO,MAAM,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAajD,CAAA;AAEF,wBAAsB,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,iBAkChD"}
@@ -0,0 +1,2 @@
1
+ export declare function add(a: number, b: number): number;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAYA,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,UAEvC"}
@@ -0,0 +1,2 @@
1
+ export declare const clone: (url: string, projectName: string, options: string[]) => Promise<void>;
2
+ //# sourceMappingURL=clone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clone.d.ts","sourceRoot":"","sources":["../../../src/utils/clone.ts"],"names":[],"mappings":"AAgCA,eAAO,MAAM,KAAK,GAAU,KAAK,MAAM,EAAE,aAAa,MAAM,EAAE,SAAS,MAAM,EAAE,kBAuC9E,CAAA"}
@@ -0,0 +1,2 @@
1
+ export declare const clone: (url: string, projectName: string, options: string[]) => Promise<void>;
2
+ //# sourceMappingURL=clone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clone.d.ts","sourceRoot":"","sources":["../../src/utils/clone.ts"],"names":[],"mappings":"AAgCA,eAAO,MAAM,KAAK,GAAU,KAAK,MAAM,EAAE,aAAa,MAAM,EAAE,SAAS,MAAM,EAAE,kBAuC9E,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "liuliu_cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "scripts": {
5
5
  "build": "rollup -c rollup.config.js --bundleConfigAsCjs"
6
6
  },
@@ -24,5 +24,14 @@
24
24
  "@inquirer/prompts": "^7.10.1",
25
25
  "chalk": "^5.6.2",
26
26
  "fs-extra": "^11.3.3"
27
- }
27
+ },
28
+ "author": "liuliu",
29
+ "bin": {
30
+ "liuliu": "bin/index.js"
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "bin",
35
+ "README.md"
36
+ ]
28
37
  }
package/rollup.config.js DELETED
@@ -1,75 +0,0 @@
1
- import { defineConfig } from "rollup";
2
- import nodeResolve from "@rollup/plugin-node-resolve"; //支持rollup打包node.js模块
3
- import commonjs from "@rollup/plugin-commonjs"; //支持rollup打包commonjs模块
4
- import json from "@rollup/plugin-json"; //支持rollup打包json文件
5
- import terser from "@rollup/plugin-terser"; //terser:压缩打包代码
6
- import typescript from "rollup-plugin-typescript2"; //支持rollup打包ts文件
7
- import { readFileSync } from "fs";
8
-
9
- // 读取 package.json 获取依赖列表
10
- const pkg = JSON.parse(readFileSync("./package.json", "utf-8"));
11
- const dependencies = Object.keys(pkg.dependencies || {});
12
- const devDependencies = Object.keys(pkg.devDependencies || {});
13
-
14
- export default defineConfig([
15
- {
16
- input: {
17
- index: "src/index.ts", // 打包入口文件
18
- },
19
- output: [
20
- {
21
- dir: "dist", // 输出目标文件夹
22
- format: "cjs", // 输出 commonjs 文件
23
- },
24
- ],
25
- // 手动配置外部依赖,不打包这些依赖
26
- // 注意:chalk v5 是纯 ES Module,需要被打包,不能作为 external
27
-
28
- // 在 Rollup 配置中,external(中文译为「外部依赖」)是核心配置项之一,作用是:告诉 Rollup「这些模块 / 依赖不需要被打包进最终的输出文件里」,打包时会跳过它们,仅保留对这些模块的引用(比如 require('xxx')/import xxx from 'xxx')。
29
-
30
- // 若不把它们设为 external,Rollup 会把这些包的代码全部打包到你的 dist/index.js 里,导致:
31
- // 打包后的文件体积暴增(比如几 KB 的代码变成几十 MB);
32
- // 存在重复代码(用户安装时会再下载一份,你的包内又包含一份)。
33
- external: [
34
- // 1. 所有生产依赖(除了chalk)都设为外部依赖,不打包
35
- ...dependencies.filter((dep) => dep !== "chalk"), // 排除 chalk,让它被打包
36
- // 2. 所有开发依赖都设为外部依赖(开发依赖仅打包时用,运行时不需要)
37
- ...devDependencies,
38
- // 3. Node.js 内置模块(以node:开头的新写法,如node:fs)
39
- // Node.js 内置模块
40
- /^node:/,
41
- // 4. Node.js 内置模块的旧写法(兼容老代码)
42
- "fs",
43
- "path",
44
- "util",
45
- "os",
46
- "crypto",
47
- "stream",
48
- "events",
49
- "buffer",
50
- "url",
51
- "http",
52
- "https",
53
- "net",
54
- "tls",
55
- "zlib",
56
- "child_process",
57
- ],
58
- // 这些依赖的作用上文提到过
59
- // 插件直接放到plungins里面即可
60
- plugins: [
61
- nodeResolve(),
62
- typescript({
63
- // 使用项目根目录的 tsconfig.json
64
- tsconfig: "./tsconfig.json",
65
- // 排除 565 目录(独立的 Vue 项目,不应该被编译)
66
- tsconfigOverride: {
67
- exclude: ["565/**/*", "dist/**/*", "node_modules/**/*"],
68
- },
69
- }),
70
- json(),
71
- commonjs(),
72
- terser(),
73
- ],
74
- },
75
- ]);
@@ -1,93 +0,0 @@
1
- import path from 'path'
2
- import fs from 'fs-extra'
3
- import { input, select } from '@inquirer/prompts'
4
- import { clone } from '../utils/clone'
5
- import chalk from 'chalk'
6
-
7
- // 强制启用颜色支持(Windows 终端需要)
8
- // 设置环境变量强制启用颜色
9
- if (!process.env.FORCE_COLOR) {
10
- process.env.FORCE_COLOR = '1';
11
- }
12
- // 直接设置 chalk 的颜色级别(0=无颜色, 1=16色, 2=256色, 3=真彩色)
13
- chalk.level = 3;
14
- export interface TemplateInfo {
15
- name: string,//模板名称
16
- downloadUrl: string,//模板下载地址
17
- description: string,//模板描述
18
- branch: string//模板分支
19
- }
20
-
21
- export function isOverwrite(fileName: string) {
22
- console.warn(`${fileName}文件夹存在`);
23
- return select({
24
- message: '是否覆盖?',
25
- choices: [
26
- { name: '覆盖', value: true },
27
- { name: '取消', value: false }
28
- ]
29
- })
30
- }
31
-
32
- export const templateList: Map<string, TemplateInfo> = new Map([
33
- ['Vite-Vue3-Typescript-template', {
34
- name: 'Vite-Vue-Type',
35
- downloadUrl: 'https://gitee.com/sohucw/admin-pro.git',
36
- description: 'Vue3技术开发模板1',
37
- branch: 'dev11'
38
- }],
39
- ['Vite-Vue3-Typescript-template2', {
40
- name: 'Vite-Vue-Type',
41
- downloadUrl: 'git@gitee.com:sohucw/admin-pro.git',
42
- description: 'Vue3技术开发模板2',
43
- branch: 'dev10'
44
- }]
45
- ])
46
-
47
- export async function create(projectName?: string) {
48
- // 初始化模板列表
49
- const templateList2 = Array.from(templateList).map((item: [string, TemplateInfo]) => {
50
- const [name, info] = item;
51
- return {
52
- name,
53
- value: name,
54
- description: info.description
55
- }
56
- })
57
-
58
- if (!projectName) {
59
- projectName = await input({
60
- message: '请输入项目名称'
61
- })
62
- }
63
-
64
- // 如果文件夹存在,则提示是否覆盖
65
- const filePath = path.resolve(process.cwd(), projectName)
66
- if (fs.existsSync(filePath)) {
67
- const run = await isOverwrite(projectName)
68
- if (run) {
69
- await fs.remove(filePath)
70
- } else {
71
- return;//不做任何处理
72
- }
73
- }
74
-
75
-
76
- const templateName = await select({
77
- message: '请选择模板',
78
- choices: templateList2
79
- })
80
-
81
- const info = templateList.get(templateName);
82
- if (info) {
83
- try {
84
- await clone(info.downloadUrl, projectName, ['-b', info.branch])
85
- } catch (error) {
86
- // clone 函数已经处理了错误信息,这里只需要退出
87
- process.exit(1);
88
- }
89
- } else {
90
- console.error(chalk.red('未找到模板信息'))
91
- process.exit(1);
92
- }
93
- }
package/src/index.ts DELETED
@@ -1,27 +0,0 @@
1
- import { Command } from 'commander'
2
- import { version } from '../package.json'
3
- import { create } from './command/create'
4
- import chalk from 'chalk'
5
-
6
- // 强制启用颜色支持(Windows 终端需要)
7
- // 设置环境变量强制启用颜色
8
- if (!process.env.FORCE_COLOR) {
9
- process.env.FORCE_COLOR = '1';
10
- }
11
- // 直接设置 chalk 的颜色级别(0=无颜色, 1=16色, 2=256色, 3=真彩色)
12
- chalk.level = 3;
13
- export function add(a: number, b: number) {
14
- return a + b
15
- }
16
-
17
- // 指令名称
18
- const program = new Command('dawei')
19
-
20
- program.version(version, '-v,--version')
21
-
22
- program.command('create').description('创建一个新项目').argument('[name]', '项目名称').action(async (dirName) => {
23
- create(dirName)
24
- })
25
-
26
- program.parse();
27
- // node ./dist/index.js dawei -v 最终命令就是这个
@@ -1,72 +0,0 @@
1
- import simpleGit from 'simple-git'
2
- import type { SimpleGitOptions } from 'simple-git'; // 类型导入必须加 type
3
- import createLogger from "progress-estimator";
4
- import chalk from 'chalk'
5
-
6
- // 强制启用颜色支持(Windows 终端需要)
7
- // 设置环境变量强制启用颜色
8
- if (!process.env.FORCE_COLOR) {
9
- process.env.FORCE_COLOR = '1';
10
- }
11
- // 直接设置 chalk 的颜色级别(0=无颜色, 1=16色, 2=256色, 3=真彩色)
12
- chalk.level = 3;
13
-
14
- // 核心修复:map 回调直接返回 chalk 处理后的字符串
15
- const spinnerFrames = ['-', '+', '-'].map(item => chalk.green(item));
16
-
17
- // 初始化进度条
18
- const logger = createLogger({
19
- spinner: {
20
- interval: 100,
21
- frames: spinnerFrames
22
- }
23
- })
24
-
25
- // Partial 是个语法糖,可以让其他属性变成可选地
26
- const gitOptions: Partial<SimpleGitOptions> = {
27
- baseDir: process.cwd(),//当前工作目录
28
- binary: 'git',//制定git 二进制文件路径
29
- maxConcurrentProcesses: 6,//最大并发进程
30
- trimmed: false
31
- }
32
-
33
- export const clone = async (url: string, projectName: string, options: string[]) => {
34
- const git = simpleGit();
35
- try {
36
- await logger(git.clone(url, projectName, options), '代码下载中', {
37
- estimate: 7000,//预计下载时间
38
- })
39
- console.log();
40
- console.log(chalk.green('代码下载成功'))
41
- console.log(chalk.blackBright('========================'))
42
- console.log(chalk.blackBright('==欢迎使用 DALIU脚手架==='))
43
- console.log(chalk.blackBright('========================'))
44
- console.log();
45
- console.log();
46
- console.log(chalk.blackBright('====使用npm install安装依赖========='));
47
- } catch (error: any) {
48
- console.error(chalk.red('下载失败'))
49
-
50
- // 提供更友好的错误信息
51
- if (error?.message?.includes('Host key verification failed')) {
52
- console.error(chalk.yellow('\n提示:SSH 主机密钥验证失败'))
53
- console.error(chalk.yellow('解决方案:'))
54
- console.error(chalk.yellow('1. 如果使用 SSH URL,请先配置 SSH 密钥:'))
55
- console.error(chalk.yellow(' ssh-keygen -t rsa -C "your_email@example.com"'))
56
- console.error(chalk.yellow(' 然后将公钥添加到 Git 服务器'))
57
- console.error(chalk.yellow('2. 或者使用 HTTPS URL(需要输入用户名密码)'))
58
- console.error(chalk.yellow('3. 临时解决:手动添加主机密钥'))
59
- console.error(chalk.yellow(' ssh-keyscan gitee.com >> ~/.ssh/known_hosts'))
60
- } else if (error?.message?.includes('Could not read from remote repository')) {
61
- console.error(chalk.yellow('\n提示:无法访问远程仓库'))
62
- console.error(chalk.yellow('请检查:'))
63
- console.error(chalk.yellow('1. 仓库地址是否正确'))
64
- console.error(chalk.yellow('2. 是否有访问权限'))
65
- console.error(chalk.yellow('3. 网络连接是否正常'))
66
- } else {
67
- console.error(chalk.red('\n错误详情:'))
68
- console.error(error)
69
- }
70
- throw error; // 重新抛出错误,让调用者知道失败了
71
- }
72
- }
package/tsconfig.json DELETED
@@ -1,21 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "ESNext",
5
- "lib": ["ES2020", "ESNext"],
6
- "moduleResolution": "node",
7
- "resolveJsonModule": true,
8
- "esModuleInterop": true,
9
- "allowSyntheticDefaultImports": true,
10
- "strict": true,
11
- "skipLibCheck": true,
12
- "forceConsistentCasingInFileNames": true,
13
- "declaration": true,
14
- "declarationMap": true,
15
- "outDir": "./dist",
16
- "rootDir": "./src",
17
- "types": ["node"]
18
- },
19
- "include": ["src/**/*"],
20
- "exclude": ["node_modules", "dist", "565"]
21
- }
@@ -1,690 +0,0 @@
1
- ## 前言
2
- 开发的脚手架地址:
3
- dawei-cli [https://www.npmjs.com/package/dawei-cli](https://www.npmjs.com/package/dawei-cli)
4
-
5
- 如何注册github 教程:[https://blog.csdn.net/weixin_51674304/article/details/121525251](https://blog.csdn.net/weixin_51674304/article/details/121525251)
6
- 如何注册npm 教程: [https://blog.csdn.net/u014388408/article/details/130638284](https://blog.csdn.net/u014388408/article/details/130638284)
7
-
8
- ## 注意的问题
9
- rollup-plugin-node-externals 需要使用5.1.2的版本 不要使用最新的6.1.2版本
10
- chalk版本降级到4.1.2的版本
11
-
12
- ## 初始化项目
13
- ```bash
14
- npm init -y
15
- ```
16
- 生成:typescript配置文件 tsconfig.json
17
- ```bash
18
- npx tsc --init
19
- ```
20
-
21
- ## package.json里面的依赖说明
22
- ```typescript
23
- "devDependencies": {
24
- // 用于命令行交互。
25
- "@inquirer/prompts": "^3.2.0",
26
- // Rollup 相关的插件,用于模块打包
27
- "@rollup/plugin-commonjs": "^25.0.3", // 支持rollup打包commonjs模块
28
- "@rollup/plugin-json": "^6.0.1", // 支持rollup打包json文件
29
- "@rollup/plugin-node-resolve": "^15.1.0", // 用于帮助 Rollup 解析和处理 Node.js 模块(Node.js 的 CommonJS 模块规范)
30
- "@rollup/plugin-terser": "^0.4.3", // Rollup 构建过程中对生成的 JavaScript 代码进行压缩和混淆,以减小最终输出文件的体积。
31
- // TypeScript 的类型定义文件
32
- "@types/fs-extra": "^11.0.2",
33
- "@types/lodash": "^4.14.199",
34
- "@types/node": "^16.18.40",
35
- // 用于发起 HTTP 请求。
36
- "axios": "^1.5.0",
37
- // 在命令行中输出彩色文本。
38
- "chalk": "^4.1.2",
39
- // 命令行界面的解决方案
40
- "commander": "^11.0.0",
41
- // 扩展了标准 fs 模块的文件系统操作
42
- "fs-extra": "^11.1.1",
43
- // 一个提供实用函数的 JavaScript 库。
44
- "lodash": "^4.17.21",
45
- // 在命令行中显示日志符号。
46
- "log-symbols": "^4.1.0",
47
- // 创建可旋转的加载器
48
- "ora": "5",
49
- // 估算操作进度。
50
- "progress-estimator": "^0.3.1",
51
- // 一个特定于项目或定制的 CLI 工具
52
- "pure-thin-cli": "^0.1.8",
53
-
54
- "rollup": "^4.6.1",
55
- "rollup-plugin-dts": "^5.3.0", // 是一个 Rollup 插件,它的主要作用是处理 TypeScript 的声明文件(.d.ts 文件)
56
- "rollup-plugin-esbuild": "^5.0.0",
57
- "rollup-plugin-node-externals": "^5.1.2", // 使rollup自动识别外部依赖
58
- "rollup-plugin-typescript2": "^0.36.0", // 支持rollup打包ts文件
59
-
60
- // 用于 Git 命令的 Node.js 封装。
61
- "simple-git": "^3.19.1",
62
- // TypeScript 运行时库。
63
- "tslib": "^2.6.1",
64
- "typescript": "^5.2.2"
65
- },
66
- ```
67
-
68
- ## 基础结构
69
-
70
- - 我们这次的目标就是搭建一个类似于vue-cli,create-react-app等cli工具类似的工具包。要实现的核心功能就是使用命令行交互的效果去生成我们需要的Vue项目模板。
71
- - 首先把项目文件结构创建一下,一步步教大家实现。
72
-
73
- ```bash
74
-
75
- dawei-cli/
76
- |- src/ # 项目资源
77
- |- command/ # 命令逻辑
78
- |- utils/ # 公共方法
79
- |- index.ts # 命令入口文件
80
- ```
81
- ### 用到的依赖
82
-
83
- - 命令行交互
84
- - commander:解析命令行指令
85
- - ora:终端加载动画
86
- - progress-estimator:终端加载条动画
87
- - log-symbols:终端输出符号
88
- - chalk:终端字体美化
89
- - @inquirer/prompts:终端输入交互
90
- > 建议大家可以预先去了解下这些依赖的用途和一些基础的使用方法
91
-
92
-
93
- - 打包工具
94
- - rollup(打包工具有很多选择,webpack)
95
- - 这里选用 rollup 是因为它相对更适合 npm 包的打包,自己对webpack5太熟悉了,跳出自己的舒适圈
96
- - @rollup/plugin-node-resolve:支持rollup打包node.js模块
97
- - @rollup/plugin-commonjs:支持rollup打包commonjs模块
98
- - @rollup/plugin-json:支持rollup打包json文件
99
- - rollup-plugin-typescript2:支持rollup打包ts文件
100
- - @rollup/plugin-terser:压缩打包代码
101
- - rollup-plugin-node-externals:使rollup自动识别外部依赖
102
- :::warning
103
- 注意这里的rollup-plugin-node-externals版本是5的版本 不是最新的6的版本
104
- "rollup-plugin-node-externals": "^5.1.2",
105
- :::
106
- ### 配置打包命令
107
-
108
- - 我们先解决打包的问题,安装好需要的依赖,然后按照下面的配置文件内容,新建一个rollup.config.js。
109
- ```bash
110
-
111
- pnpm add -D rollup @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-json rollup-plugin-typescript2 @rollup/plugin-terser rollup-plugin-node-externals
112
- ```
113
- ```typescript
114
-
115
- import { defineConfig } from 'rollup';
116
- import nodeResolve from '@rollup/plugin-node-resolve';
117
- import commonjs from '@rollup/plugin-commonjs';
118
- import externals from "rollup-plugin-node-externals";
119
- import json from "@rollup/plugin-json";
120
- import terser from "@rollup/plugin-terser";
121
- import typescript from 'rollup-plugin-typescript2';
122
-
123
- export default defineConfig([
124
- {
125
- input: {
126
- index: 'src/index.ts', // 打包入口文件
127
- },
128
- output: [
129
- {
130
- dir: 'dist', // 输出目标文件夹
131
- format: 'cjs', // 输出 commonjs 文件
132
- }
133
- ],
134
- // 这些依赖的作用上文提到过
135
- plugins: [
136
- nodeResolve(),
137
- externals({
138
- devDeps: false, // 可以识别我们 package.json 中的依赖当作外部依赖处理 不会直接将其中引用的方法打包出来
139
- }),
140
- typescript(),
141
- json(),
142
- commonjs(),
143
- terser(),
144
- ],
145
- },
146
- ]);
147
- ```
148
- 我们还需要在package.json中配置一个打包命令。
149
-
150
- - -c 指定 rollup 配置文件,--bundleConfigAsCjs 将配置转为 commonjs 执行。
151
- ```json
152
- {
153
- // ...
154
- "build": "rollup -c rollup.config.js --bundleConfigAsCjs"
155
- }
156
- ```
157
- ## 编写指令
158
- ### 指令交互
159
-
160
- - 这里带大家写一个create指令,在我们的入口文件src/index.ts编写。
161
- - 我们需要用到commander,可以帮助我们解析用户在命令行输入的指令。
162
- - 这里会给大家讲解一些基础的用法,更详细的使用方式请查阅官方文档:[commander.js](https://github.com/tj/commander.js)。
163
-
164
- 首先初始化一个Command对象,传入的参数作为我们的指令名称。
165
- ```typescript
166
- import { Command } from 'commander'
167
- // 这里我们用 dawei 当作我的指令名称
168
- // 命令行中使用 dawei xxx 即可触发
169
- const program = new Command('dawei');
170
- ```
171
- 接下来我们就可以配置我们需要的命令了。
172
-
173
- - 使用version可以实现最基础的查看版本的指令。
174
- ```typescript
175
- import { version } from '../package.json'
176
- // .vesion 表示可以使用 -V --version 参数查看当前SDK版本
177
- // 我们直接使用 package.json 中的 version 即可
178
- program
179
- .version(version);
180
- // 调用 version 的参数可以自定义
181
- // .version(version, '-v --version')
182
- ```
183
-
184
- - 使用command与action实现自定义指令。
185
- - command 为我们需要的命令名称。
186
- - description 为命令添加描述。
187
- - action 为指令触发执行的回调。
188
- - argument 为我们命令需要的参数,[]包裹代表可选,<>包裹代表必填。
189
-
190
- 下面的示例就是我们编写好的指令,指令回调我们稍后实现,输入dawei update会打印update command,输入dawei create test,会打印create test。action 回调中会将 argument 中的参数传入。
191
- ```typescript
192
- // ...
193
-
194
- program
195
- .command('update')
196
- .description('更新 dawei 至最新版本')
197
- .action(async () => {
198
- console.log('update command')
199
- });
200
-
201
- program
202
- .command('create')
203
- .description('创建一个新项目')
204
- .argument('[name]', '项目名称')
205
- .action(async (name) => {
206
- if(name) console.log(`create ${name}`)
207
- else console.log(`create command`)
208
- });
209
- ```
210
-
211
- - 解析指令
212
- ```typescript
213
- // ...
214
-
215
- // parse 会解析 process.argv 中的内容
216
- // 也就是我们输入的指令
217
- program.parse();
218
- ```
219
-
220
- ### 下载项目
221
- 我们先实现create命令,可以让用户选择下载我们预设的模板。
222
-
223
- - 在src/command/create.ts文件下编写create命令核心代码。
224
- - 导出一个可以传入项目名称的方法,如果用户直接传入了项目名称则让用户选择模板,否则需要先让用户输入项目名称。
225
- - 这里我们用到了@inquirer/prompts,可以帮助我们让用户在终端进行输入或选择的操作,更多使用方法请查阅官方文档:[inquirer.js](https://github.com/SBoudrias/Inquirer.js)。
226
- ```typescript
227
- import { select, input } from '@inquirer/prompts';
228
- export default async function create(prjName?: string) {
229
- // 文件名称未传入需要输入
230
- if (!prjName) prjName = await input({ message: '请输入项目名称' });
231
- // 如果文件已存在需要让用户判断是否覆盖原文件
232
- const filePath = path.resolve(process.cwd(), prjName)
233
- if (fs.existsSync(filePath)) {
234
- const run = await isOverwrite(prjName)
235
- if (run) {
236
- await fs.remove(filePath)
237
- } else {
238
- return // 不覆盖直接结束
239
- }
240
- }
241
- }
242
- ```
243
-
244
- - 在src/command/create.ts添加一个判断用户是否覆盖的公共方法。
245
- ```typescript
246
- import { select } from '@inquirer/prompts';
247
- import log from "./log";
248
-
249
- export const isOverwrite = async (fileName: string) => {
250
- log.warning(`${fileName} 文件已存在 !`)
251
- return select({
252
- message: '是否覆盖原文件: ',
253
- choices: [
254
- {name: '覆盖', value: true},
255
- {name: '取消', value: false}
256
- ]
257
- });
258
- }
259
- ```
260
-
261
- - 然后我们就需要让用户选择我们的预设模板,在src/command/create.ts中添加模板信息,定义成map的形式是方便我们根据key获取项目的信息。
262
- - 下载模板的方式有很多种,可以将模板文件保存在 SDK 中,使用 cjs 或者其他方法动态选择生成,使用 fs 模块写入,或者存放在 git 仓库中进行 clone,我们这里把代码放到gitee中的代码仓库中
263
- - 这里我定义了 TemplateInfo 类型,可以根据自己的需求自行定义,需要存储项目名称,下载地址,描述,代码分支。
264
- ```typescript
265
-
266
-
267
- export interface TemplateInfo {
268
- name: string; // 项目名称
269
- downloadUrl: string; // 下载地址
270
- description: string; // 项目描述
271
- branch: string; // 项目分支
272
- }
273
- // 这里保存了我写好了咱们的之前开发的模板
274
- export const templates: Map<string, TemplateInfo> = new Map(
275
- [
276
- ["Vite4-Vue3-Typescript-template", {
277
- name: "admin-template",
278
- downloadUrl: 'git@gitee.com:sohucw/admin-pro.git',
279
- description: 'Vue3技术栈开发模板',
280
- branch: 'dev6'
281
- }]
282
- ]
283
- )
284
- ```
285
-
286
- - 接下来我们就可以让用户选择需要的模板。
287
- ```typescript
288
- import { select, input } from '@inquirer/prompts';
289
-
290
- import log from "../utils/log";
291
-
292
- export interface TemplateInfo {
293
- name: string; // 项目名称
294
- downloadUrl: string; // 下载地址
295
- description: string; // 项目描述
296
- branch: string; // 项目分支
297
- }
298
- // 这里保存了我写好的预设模板
299
- export const templates: Map<string, TemplateInfo> = new Map(
300
- [
301
- ["Vite4-Vue3-Typescript-template", {
302
- name: "admin-template",
303
- downloadUrl: 'git@gitee.com:sohucw/admin-pro.git',
304
- description: 'Vue3技术栈开发模板',
305
- branch: 'dev8'
306
- }]
307
- ]
308
- )
309
-
310
- export default async function create(prjName?: string) {
311
- // ...
312
-
313
- // 我们需要将我们的 map 处理成 @inquirer/prompts select 需要的形式
314
- // 大家也可以封装成一个方法去处理
315
- const templateList = [...templates.entries()].map((item: [string, TemplateInfo]) => {
316
- const [name, info] = item;
317
- return {
318
- name,
319
- value: name,
320
- description: info.description
321
- }
322
- })
323
-
324
- // 选择模板
325
- const templateName = await select({
326
- message: '请选择需要初始化的模板:',
327
- choices: templateList,
328
- });
329
-
330
- // 下载模板
331
- const gitRepoInfo = templates.get(templateName)
332
- if (gitRepoInfo) {
333
- await clone(gitRepoInfo.downloadUrl , prjName, ['-b', `${gitRepoInfo.branch}`])
334
- } else {
335
- log.error(`${templateName} 模板不存在`)
336
- }
337
- }
338
- ```
339
-
340
- - 我们还需要实现我们刚刚使用过的clone方法,下载仓库中的模板。
341
- - 我们在src/utils/clone.ts中实现下。
342
- - 这里我们用到,simple-git用于拉取 git 仓库,progress-estimator设置预估git clone的时间并展示进度条。
343
- - 这里我就直接展示代码和注释了,思路都很简单。
344
- ```typescript
345
- import simpleGit, { SimpleGit, SimpleGitOptions } from 'simple-git';
346
- import log from "./log";
347
- import createLogger from "progress-estimator";
348
- import chalk from "chalk";
349
-
350
- const logger = createLogger({ // 初始化进度条
351
- spinner: {
352
- interval: 300, // 变换时间 ms
353
- frames: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'].map(item=>chalk.blue(item)) // 设置加载动画
354
- }
355
- })
356
-
357
- const gitOptions: Partial<SimpleGitOptions> = {
358
- baseDir: process.cwd(), // 根目录
359
- binary: 'git',
360
- maxConcurrentProcesses: 6, // 最大并发进程数
361
- };
362
-
363
- export const clone = async (url: string, prjName: string, options: string[]): Promise<any> => {
364
- const git: SimpleGit = simpleGit(gitOptions)
365
- try {
366
- // 开始下载代码并展示预估时间进度条
367
- await logger(git.clone(url, prjName, options), '代码下载中: ', {
368
- estimate: 8000 // 展示预估时间
369
- })
370
-
371
- // 下面就是一些相关的提示
372
- console.log()
373
- console.log(chalk.blueBright(`==================================`))
374
- console.log(chalk.blueBright(`=== 欢迎使用 dawei-cli 脚手架 ===`))
375
- console.log(chalk.blueBright(`==================================`))
376
- console.log()
377
-
378
- log.success(`项目创建成功 ${chalk.blueBright(prjName)}`)
379
- log.success(`执行以下命令启动项目:`)
380
- log.info(`cd ${chalk.blueBright(prjName)}`)
381
- log.info(`${chalk.yellow('pnpm')} install`)
382
- log.info(`${chalk.yellow('pnpm')} run dev`)
383
- } catch (err: any) {
384
- log.error("下载失败")
385
- log.error(String(err))
386
- }
387
- }
388
- ```
389
-
390
- - 至此,我们的create命令就编写完毕了,我们可以将其添加到src/index.ts中去调用。
391
- ```typescript
392
- // ...
393
- program
394
- .command('create')
395
- .description('创建一个新项目')
396
- .argument('[name]', '项目名称')
397
- .action(async (dirName) => {
398
- // 添加create方法
399
- await create(dirName);
400
- });
401
- // ...
402
- ```
403
- ### 检测项目更新
404
-
405
- - 当我们更新模板后,希望用户第一时间用到,可以在用户使用过程中添加一些更新提示。
406
-
407
- 在src/command/create.ts中编写方法,用于获取npm包的信息及版本号。
408
- ```typescript
409
- // npm 包提供了根据包名称查询包信息的接口
410
- // 我们在这里直接使用 axios 请求调用即可
411
- export const getNpmInfo = async (npmName: string) => {
412
- const npmUrl = 'https://registry.npmjs.org/' + npmName
413
- let res = {}
414
- try {
415
- res = await axios.get(npmUrl)
416
- } catch (err) {
417
- log.error(err as string)
418
- }
419
- return res
420
- }
421
- ```
422
- npm包信息中包含了该包的最新版本,我们在这里直接引用即可。
423
- ```typescript
424
- export const getNpmLatestVersion = async (npmName: string) => {
425
- // data['dist-tags'].latest 为最新版本号
426
- const { data } = (await getNpmInfo(npmName)) as AxiosResponse
427
- return data['dist-tags'].latest
428
- }
429
- ```
430
- 然后对比版本号版本,判断是否需要更新,如需更新进行提示。
431
- ```typescript
432
- export const checkVersion = async (name: string, curVersion: string) => {
433
- const latestVersion = await getNpmLatestVersion(name)
434
- const need = lodash.gt(latestVersion, curVersion)
435
- if(need) {
436
- log.info(`检测到 dawei 最新版:${chalk.blueBright(latestVersion)} 当前版本:${chalk.blueBright(curVersion)} ~`)
437
- log.info(`可使用 ${chalk.yellow('pnpm')} install dawei-cli@latest 更新 ~`)
438
- }
439
- return need
440
- }
441
- ```
442
- 然后我们将这个判断更新的方法添加到create方法中。
443
- ```typescript
444
- export default async function create(prjName?: string) {
445
- // ...
446
- await checkVersion(name, version) // 检测版本更新
447
- // ...
448
- }
449
- ```
450
- 当我们发布新的版本,用户可以第一时间看到。
451
- ## 如何调用
452
-
453
- - 我们已经完成了核心的代码逻辑,现在想要使用命令行去调用我们编写好的逻辑,我们可以先自己在本地执行测试,然后将其上传到 npm 就可以供他人使用了。
454
- ### 本地调试
455
-
456
- - 我们之前已经配置好了 rollup 打包的脚本,接下来就可以执行 npm run build,打包后的代码会输出到dist/index.js中。
457
- - 我们可以使用node在本地执行,先测试一下我们编写好的create命令。
458
- ```
459
- node .\dist\index.js create
460
- ```
461
- 不出意外是可以看到我们写好的交互逻辑,如果有报错,大家可以根据对应的问题查询下
462
- ### 发布npm包
463
-
464
- - 本地调试没有问题后我们就可以将其发布在npm上。
465
- - npm账号注册、登录等基础操作,这里就不过多赘述了,主要讲一下如何让发布的包能以dawei作为命令名调用。
466
- - 需要把代码提交到github上 如果没有github账号也需要 注册一下 ,具体怎么注册 登录不详细说了
467
-
468
-
469
- 需要我们修改一下package.json文件,下面是一些必要的配置,都加上了注释,我们需要重点关注bin这一项。
470
-
471
- - bin中的配置是一个对象,需要有 "key" 和 "value"。
472
- - key 会被放置在 node_modules 的 .bin 目录中,value 是 key 对应需要执行的文件。
473
- - 我们使用 npx dawei 就会调用我们的 bin/index.js。
474
- - 当我们全局安装对应包的时候会放在全局的 node_modules 的 .bin 目录中,相当于添加了系统环境变量,这样我们就可以直接在终端中调用。
475
- ```json
476
- {
477
- "name": "dawei-cli", // 包名称
478
- "version": "x.x.x", // 包版本
479
- "description": "dawei-cli脚手架", // 包描述
480
- "main": "dist/index.js", // 库入口文件
481
- "keywords": [ // 包查询关键词 提升SEO
482
- "Vite-Vue4-TypeScript-template"
483
- ],
484
- "files": [ // npm 包需要上传的文件
485
- "dist",
486
- "bin",
487
- "README.md"
488
- ],
489
- "author": { // 作者信息
490
- "name": "dawei"
491
- },
492
- "bin": {
493
- "dawei": "bin/index.js" // npm 会在 .bin 目录中配置 dawei 执行 bin/index.js
494
- },
495
- "devDependencies": {
496
- "@inquirer/prompts": "^3.3.0",
497
- "@rollup/plugin-commonjs": "^25.0.3",
498
- "@rollup/plugin-json": "^6.0.1",
499
- "@rollup/plugin-node-resolve": "^15.1.0",
500
- "@rollup/plugin-terser": "^0.4.3",
501
- "@types/fs-extra": "^11.0.2",
502
- "@types/lodash": "^4.14.202",
503
- "@types/node": "^20.10.4",
504
- "axios": "^1.6.2",
505
- "chalk": "^4.1.2",
506
- "commander": "^11.1.0",
507
- "figlet": "^1.7.0",
508
- "fs-extra": "^11.1.1",
509
- "lodash": "^4.17.21",
510
- "log-symbols": "4.1.0",
511
- "ora": "5",
512
- "progress-estimator": "^0.3.1",
513
- "rollup": "^4.6.1",
514
- "rollup-plugin-node-externals": "^5.1.2",
515
- "rollup-plugin-typescript2": "^0.36.0",
516
- "simple-git": "^3.21.0",
517
- "tslib": "^2.6.2",
518
- "typescript": "^5.3.3"
519
- }
520
- }
521
- ```
522
- 编写bin/index.js
523
- ```
524
- #!/usr/bin/env node
525
- require('../dist'); // 执行我们打包好的 dist/index.js 文件
526
- ```
527
- 需要在第一行加入#!/usr/bin/env node,/usr/bin/env就是告诉系统可以在PATH目录中查找,#!/usr/bin/env node就是解决了不同的用户node路径不同的问题,可以让系统动态的去查找node来执行你的脚本文件。
528
-
529
- ### 开发发包
530
- #### 检查 npm 源,如果是淘宝源,则需要改回 npm 源
531
- ```bash
532
- // 查看npm镜像源地址
533
- npm config get registry
534
-
535
- // 切换npm镜像源
536
-
537
- // 设置npm默认源
538
- npm config set registry https://registry.npmjs.org/
539
- // 设置npm镜像源为淘宝镜像
540
- npm config set registry https://registry.npmmirror.com/
541
-
542
- ```
543
-
544
- > npm、yarn 和 pnpm 淘宝镜像
545
-
546
- ```bash
547
- // 设置
548
- npm config set registry https://registry.npmmirror.com/
549
- yarn config set registry https://registry.npmmirror.com/
550
- pnpm config set registry https://registry.npmmirror.com/
551
- // 查看
552
- npm config get registry
553
- yarn config get registry
554
- pnpm config get registry
555
- ```
556
-
557
- #### 在终端中切换到项目目录下,运行登陆命令,之后按照终端提示输入用户名、密码等信息即可
558
- ```bash
559
- // 登陆
560
- npm login
561
-
562
- // 控制台会提示输入相关信息
563
- Log in on https://registry.npmjs.org/
564
- Username: // 用户名
565
- Password: // 密码
566
- Email: (this IS public) // 邮箱
567
- Enter one-time password: // 如果之前做过 双因素身份验证 (2FA),需要生成一次性密钥
568
- Logged in as xxx on https://registry.npmjs.org/.
569
-
570
- ```
571
-
572
- #### 运行发布命令
573
- ```bash
574
- // 发布命令
575
- npm publish
576
- ```
577
-
578
- 发布成功后,就可以登陆 [npm](https://www.npmjs.com/package/daweitest-cli) 网站,查看发布包的情况了
579
- [https://www.npmjs.com/package/daweitest-cli](https://www.npmjs.com/package/daweitest-cli)
580
- #### 更新npm版本,
581
- 修改完代码,需要先提交到github上
582
- ```bash
583
-
584
- // 控制台会返回下一个小版本号 如v1.0.1
585
- npm version patch
586
-
587
- // 重新发布
588
- npm publish
589
- ```
590
-
591
-
592
- > 一些常见的发布命令
593
-
594
- ```bash
595
- npm login # 发布前需要先登录下
596
-
597
- npm publish # 会按照我们 package.json 中的 files 配置的文件发布 name 作为包名称
598
-
599
- # 如果需要迭代包的版本 要先修改版本号再发布
600
-
601
- # patch:补丁号,修复bug,小变动
602
- npm version patch # 0.0.0 -> 0.0.1
603
- # minor:次版本号,增加新功能
604
- npm version minor # 0.0.0 -> 0.1.0
605
-
606
- # major:主版本号,不兼容的修改
607
- npm version major # 0.0.0 -> 1.0.0
608
- ```
609
-
610
- 发布成功的图
611
- ![image.png](https://cdn.nlark.com/yuque/0/2023/png/207857/1702909664734-b76cb5ab-f327-4d75-8e85-0610191890d7.png#averageHue=%2330343b&clientId=ue89a8a46-436c-4&from=paste&height=374&id=uefe8142c&originHeight=674&originWidth=1710&originalType=binary&ratio=1.7999999523162842&rotation=0&showTitle=false&size=448905&status=done&style=none&taskId=uba41f344-265b-41cb-8d7b-be6a5aa9550&title=&width=950.0000251664062)
612
-
613
- 发布完成后我们就可以安装npm全局包然后进行使用啦。
614
- ```bash
615
- npm install dawei-cli -g
616
- ```
617
-
618
-
619
-
620
- ### 输出提示
621
-
622
- - 在编写命令代码前我们先封装一个公共方法。
623
- - 在src/utils/log.ts中封装一个带icon的输出提示。
624
- - 我们需要用到log-symbols,他内置了 error,success,warning,info 对应的 icon ,并且帮我们兼容不支持 icon 的终端。并且后续我们需要用到的ora作为加载动画,它也是用的log-symbols进行提示,我们这里保持一致
625
- :::warning
626
- 注意这里的log-symbols版本是 4的版本 不是最新的6的版本
627
- "log-symbols": "^4.1.0",
628
- ora需要使用5的版本
629
- "ora": "5",
630
- :::
631
- ```typescript
632
- import logSymbols from 'log-symbols'
633
-
634
- export const log = {
635
- error: (msg: string) => {
636
- console.log(logSymbols.error, msg)
637
- },
638
- success: (msg: string) => {
639
- console.log(logSymbols.success, msg)
640
- },
641
- warning: (msg: string) => {
642
- console.log(logSymbols.warning, msg)
643
- },
644
- info: (msg: string) => {
645
- console.log(logSymbols.info, msg)
646
- },
647
- }
648
-
649
- export default log
650
- ```
651
-
652
- 控制台更好的打印
653
- 修改src/utils/clone.ts 方法
654
- ```bash
655
- pnpm add figlet -D
656
- ```
657
- ```typescript
658
- const goodPrinter = async () => {
659
- const data = await figlet('欢迎使用 dawei-cli 脚手架');
660
- console.log(chalk.rgb(40, 156, 193).visible(data));
661
- };
662
-
663
- // 下面就是一些相关的提示
664
- ..........
665
- goodPrinter();
666
- .......
667
- ```
668
-
669
- 官网地址:[https://www.npmjs.com/package/figlet](https://www.npmjs.com/package/figlet)
670
- 执行成功后控制台打印
671
- ![image.png](https://cdn.nlark.com/yuque/0/2023/png/207857/1702974006014-3e9f5de7-285b-4534-8067-f3268b7fcd6f.png#averageHue=%232b3039&clientId=u16fc6f16-11a0-4&from=paste&height=153&id=ozKWe&originHeight=275&originWidth=507&originalType=binary&ratio=1.7999999523162842&rotation=0&showTitle=false&size=28176&status=done&style=none&taskId=u92dc7e07-08a2-4b52-b60f-92689ef6f0e&title=&width=281.66667412828536)
672
-
673
- ### 其他
674
- [https://shields.io/badges](https://shields.io/badges) 这个网站可以生成小图标
675
-
676
- 图标库地址:
677
- [https://emojipedia.org/](https://emojipedia.org/)
678
- [https://gist.github.com/rxaviers/7360908](https://gist.github.com/rxaviers/7360908)
679
- ### 结语
680
- 到这里我们就解决了所有的问题,实现了一个简易的cli工具,做到了clone在git仓库中的固定模板
681
-
682
- ### 简历中的亮点
683
- 如何当做我们简历中的亮点
684
- 你的项目中有什么难点? 挑战
685
-
686
- 管理系统
687
- h5 移动端
688
- 小程序
689
- 门户
690
-