qcf 0.1.12 → 0.2.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/dist/config.js CHANGED
@@ -1 +1 @@
1
- import kebabCase from"lodash.kebabcase";const prompts={type:"input",name:"inputName",message:"请输入组件名称",validate(e){return!!RegExp(/^[A-Za-z]+[/a-z0-9A-Z_-]*$/).test(e.trim())||"格式错误"}};function onlyName(e){var a=e.split("/");return 1<a.length?a[a.length-1]:e}const obj={component:{description:"这是一个创建组件的生成器",prompts:[{...prompts}],actions(e){var e=e.inputName.split("/").map(e=>kebabCase(e)),a=(console.log(42,e),e[e.length-1]);return[{type:"add",path:`src/components/${e.join("/")}/{{kebabCase name}}.vue`,templateFile:"plop-templates/component/template.hbs",data:{name:a}},{type:"add",path:`src/components/${e.join("/")}/{{kebabCase name}}.less`,templateFile:"plop-templates/component/less.hbs",data:{name:a}}]}},reactComponent:{description:"这是一个创建React组件的生成器",prompts:[{...prompts}],actions(e){var e=e.inputName.split("/").map(e=>kebabCase(e)),a=(console.log(42,e),e[e.length-1]);return[{type:"add",path:`src/components/${e.join("/")}/{{kebabCase name}}.tsx`,templateFile:"react-templates/component/template.hbs",data:{name:a}},{type:"add",path:`src/components/${e.join("/")}/{{kebabCase name}}.module.less`,templateFile:"react-templates/component/less.hbs",data:{name:a}}]}},page:{description:"add views | 新增页面",prompts:[{...prompts,message:"请输入页面名称"}],actions(e){var a=e.inputName.split("/").map(e=>kebabCase(e));return a&&a[1]&&-1<a[1].indexOf("components")?(console.log(`不能新增${a[1]}子页面,此名称为系统保留名称`),new Error(`不能新增${a[1]}子页面,此名称为系统保留名称`)):[{type:"add",path:`src/views/${a.join("/")}/{{kebabCase name}}.vue`,templateFile:"plop-templates/views/template.hbs",data:{name:onlyName(e.inputName)}},{type:"add",path:`src/views/${a.join("/")}/{{kebabCase name}}.less`,templateFile:"plop-templates/views/less.hbs",data:{name:onlyName(e.inputName)}}]}},reactPage:{description:"add pages | 新增react模版页面",prompts:[{...prompts,message:"请输入react页面名称"}],actions(e){var a=e.inputName.split("/").map(e=>kebabCase(e));return a&&a[1]&&-1<a[1].indexOf("components")?(console.log(`不能新增${a[1]}子页面,此名称为系统保留名称`),new Error(`不能新增${a[1]}子页面,此名称为系统保留名称`)):[{type:"add",path:`src/pages/${a.join("/")}/{{kebabCase name}}.tsx`,templateFile:"react-templates/pages/template.hbs",data:{name:onlyName(e.inputName)}},{type:"add",path:`src/pages/${a.join("/")}/{{kebabCase name}}.module.less`,templateFile:"react-templates/pages/less.hbs",data:{name:onlyName(e.inputName)}}]}},pageComp:{description:"add views Components | 新增页面内组件",prompts:[{type:"list",message:" (。♥ᴗ♥。) 请选择你要给哪个页面增加子组件: (。♥ᴗ♥。) ",name:"parentFolder",choices:[]},{...prompts,message:"请输入页面内部组件名称"}],actions(e){var a=e.parentFolder,t=(console.log(a,e.inputName),kebabCase(e.inputName)),t=(console.log(42,t),`src/views/${a}/components/`);return[{type:"add",path:t+"{{kebabCase name}}/index.vue",templateFile:"plop-templates/views-component/template.hbs",data:{name:onlyName(e.inputName)}},{type:"add",path:t+"{{kebabCase name}}/index.less",templateFile:"plop-templates/views-component/less.hbs",data:{name:onlyName(e.inputName)}}]}},reactPageComp:{description:"add react pages Components | 新增react页面内组件",prompts:[{type:"list",message:" (。♥ᴗ♥。) 请选择你要给哪个页面增加子组件: (。♥ᴗ♥。) ",name:"parentFolder",choices:[]},{...prompts,message:"请输入页面内部组件名称"}],actions(e){var a=e.parentFolder,t=(console.log(a,e.inputName),kebabCase(e.inputName)),t=(console.log(42,t),`src/pages/${a}/components/`);return[{type:"add",path:t+"{{kebabCase name}}/index.tsx",templateFile:"react-templates/pages-component/template.hbs",data:{name:onlyName(e.inputName)}},{type:"add",path:t+"{{kebabCase name}}/index.module.less",templateFile:"react-templates/pages-component/less.hbs",data:{name:onlyName(e.inputName)}}]}},service:{description:"add API service | 新增API服务",prompts:[{...prompts,message:"请输入API服务名称"}],actions(e){e.inputName.split("/").map(e=>kebabCase(e));return[{type:"add",path:"src/api/{{kebabCase name}}-service.js",templateFile:"plop-templates/api/template.hbs",data:{name:onlyName(e.inputName)}}]}},tService:{description:"add API Typescript service | 新增API Typescript服务",prompts:[{...prompts,message:"请输入API服务名称"}],actions(e){e.inputName.split("/").map(e=>kebabCase(e));return[{type:"add",path:"src/api/{{kebabCase name}}-service.ts",templateFile:"plop-templates/api/template.hbs",data:{name:onlyName(e.inputName)}}]}},icon:{description:"add SVG Icon | 新增SVG Icon",prompts:[{...prompts,message:"请输入SVG Icon名称"}],actions(e){return[{type:"add",path:"src/components/icons/{{bigCamelCase name}}.vue",templateFile:"plop-templates/icon/template.hbs",data:{name:onlyName(e.inputName)}}]}}};export default obj;
1
+ import e from"lodash.kebabcase";import t from"path";import a from"fs";const s={type:"input",name:"inputName",message:"请输入组件名称",validate:e=>!!RegExp(/^[A-Za-z]+[/a-z0-9A-Z_-]*$/).test(e.trim())||"格式错误"};function p(e){const t=e.split("/");return t.length>1?t[t.length-1]:e}function n(){if("true"===process.env.QCF_USE_TS)return!0;if("false"===process.env.QCF_USE_TS)return!1;const e=t.resolve(),s=t.join(e,"tsconfig.json");if(a.existsSync(s))return!0;const p=t.join(e,"package.json");if(a.existsSync(p))try{const e=JSON.parse(a.readFileSync(p,"utf-8")),t={...e.dependencies,...e.devDependencies,...e.peerDependencies};if(t.typescript||t["@vue/cli-plugin-typescript"])return!0}catch(e){}return!1}const o={component:{description:"这是一个创建组件的生成器",prompts:[{...s}],actions(t){const a=t.inputName.split("/").map(t=>e(t));console.log(42,a);const s=a[a.length-1],p=n()?"plop-templates/component/template-ts.hbs":"plop-templates/component/template.hbs";return[{type:"add",path:`src/components/${a.join("/")}/{{kebabCase name}}.vue`,templateFile:p,data:{name:s}},{type:"add",path:`src/components/${a.join("/")}/{{kebabCase name}}.less`,templateFile:"plop-templates/component/less.hbs",data:{name:s}}]}},reactComponent:{description:"这是一个创建React组件的生成器",prompts:[{...s}],actions(t){const a=t.inputName.split("/").map(t=>e(t));console.log(42,a);const s=a[a.length-1];return[{type:"add",path:`src/components/${a.join("/")}/{{kebabCase name}}.tsx`,templateFile:"react-templates/component/template.hbs",data:{name:s}},{type:"add",path:`src/components/${a.join("/")}/{{kebabCase name}}.module.less`,templateFile:"react-templates/component/less.hbs",data:{name:s}}]}},page:{description:"add views | 新增页面",prompts:[{...s,message:"请输入页面名称"}],actions(t){const a=t.inputName.split("/").map(t=>e(t));if(a&&a[1]&&a[1].indexOf("components")>-1)return console.log(`不能新增${a[1]}子页面,此名称为系统保留名称`),new Error(`不能新增${a[1]}子页面,此名称为系统保留名称`);const s=n()?"plop-templates/views/template-ts.hbs":"plop-templates/views/template.hbs";return[{type:"add",path:`src/views/${a.join("/")}/{{kebabCase name}}.vue`,templateFile:s,data:{name:p(t.inputName)}},{type:"add",path:`src/views/${a.join("/")}/{{kebabCase name}}.less`,templateFile:"plop-templates/views/less.hbs",data:{name:p(t.inputName)}}]}},reactPage:{description:"add pages | 新增react模版页面",prompts:[{...s,message:"请输入react页面名称"}],actions(t){const a=t.inputName.split("/").map(t=>e(t));return a&&a[1]&&a[1].indexOf("components")>-1?(console.log(`不能新增${a[1]}子页面,此名称为系统保留名称`),new Error(`不能新增${a[1]}子页面,此名称为系统保留名称`)):[{type:"add",path:`src/pages/${a.join("/")}/{{kebabCase name}}.tsx`,templateFile:"react-templates/pages/template.hbs",data:{name:p(t.inputName)}},{type:"add",path:`src/pages/${a.join("/")}/{{kebabCase name}}.module.less`,templateFile:"react-templates/pages/less.hbs",data:{name:p(t.inputName)}}]}},pageComp:{description:"add views Components | 新增页面内组件",prompts:[{type:"list",message:" (。♥ᴗ♥。) 请选择你要给哪个页面增加子组件: (。♥ᴗ♥。) ",name:"parentFolder",choices:[]},{...s,message:"请输入页面内部组件名称"}],actions(t){const a=t.parentFolder;console.log(a,t.inputName);const s=e(t.inputName);console.log(42,s);const o=`src/views/${a}/components/`;return[{type:"add",path:`${o}{{kebabCase name}}/index.vue`,templateFile:n()?"plop-templates/views-component/template-ts.hbs":"plop-templates/views-component/template.hbs",data:{name:p(t.inputName)}},{type:"add",path:`${o}{{kebabCase name}}/index.less`,templateFile:"plop-templates/views-component/less.hbs",data:{name:p(t.inputName)}}]}},reactPageComp:{description:"add react pages Components | 新增react页面内组件",prompts:[{type:"list",message:" (。♥ᴗ♥。) 请选择你要给哪个页面增加子组件: (。♥ᴗ♥。) ",name:"parentFolder",choices:[]},{...s,message:"请输入页面内部组件名称"}],actions(t){const a=t.parentFolder;console.log(a,t.inputName);const s=e(t.inputName);console.log(42,s);const n=`src/pages/${a}/components/`;return[{type:"add",path:`${n}{{kebabCase name}}/index.tsx`,templateFile:"react-templates/pages-component/template.hbs",data:{name:p(t.inputName)}},{type:"add",path:`${n}{{kebabCase name}}/index.module.less`,templateFile:"react-templates/pages-component/less.hbs",data:{name:p(t.inputName)}}]}},service:{description:"add API service | 新增API服务",prompts:[{...s,message:"请输入API服务名称"}],actions(t){t.inputName.split("/").map(t=>e(t));return[{type:"add",path:"src/api/{{kebabCase name}}-service.js",templateFile:"plop-templates/api/template.hbs",data:{name:p(t.inputName)}}]}},tService:{description:"add API Typescript service | 新增API Typescript服务",prompts:[{...s,message:"请输入API服务名称"}],actions(t){t.inputName.split("/").map(t=>e(t));return[{type:"add",path:"src/api/{{kebabCase name}}-service.ts",templateFile:"plop-templates/api/template.hbs",data:{name:p(t.inputName)}}]}},icon:{description:"add SVG Icon | 新增SVG Icon",prompts:[{...s,message:"请输入SVG Icon名称"}],actions:e=>[{type:"add",path:"src/components/icons/{{bigCamelCase name}}.vue",templateFile:"plop-templates/icon/template.hbs",data:{name:p(e.inputName)}}]}};export default o;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import kebabCase from"lodash.kebabcase";import camelCase from"lodash.camelcase";import upperFirst from"lodash.upperfirst";import options from"./config.js";import path from"path";import fs from"fs";const generator=function(e){e.setDefaultInclude({generators:!0}),e.setHelper("kebabCase",e=>kebabCase(e)),e.setHelper("bigCamelCase",e=>upperFirst(camelCase(e)));var o=path.join(path.resolve(),"./src/views"),t=path.join(path.resolve(),"./src/pages");fs.existsSync(o)&&(o=fs.readdirSync(o).filter(e=>"index.js"!==e),options.pageComp.prompts[0].choices=o),fs.existsSync(t)&&(o=fs.readdirSync(t).filter(e=>"index.ts"!==e),options.reactPageComp.prompts[0].choices=o),e.setGenerator("service",options.service),e.setGenerator("tService",options.tService),e.setGenerator("component",options.component),e.setGenerator("page",options.page),e.setGenerator("pageComp",options.pageComp),e.setGenerator("icon",options.icon),e.setGenerator("reactComponent",options.reactComponent),e.setGenerator("reactPage",options.reactPage),e.setGenerator("reactPageComp",options.reactPageComp)};export default generator;const reactPlop=function(e){};export{reactPlop};
1
+ import e from"lodash.kebabcase";import r from"lodash.camelcase";import t from"lodash.upperfirst";import o from"./config.js";import a from"path";import s from"fs";export default function(n){n.setDefaultInclude({generators:!0}),n.setHelper("kebabCase",r=>e(r)),n.setHelper("bigCamelCase",e=>t(r(e)));const c=a.join(a.resolve(),"./src/views"),p=a.join(a.resolve(),"./src/pages");if(s.existsSync(c)){const e=s.readdirSync(c).filter(e=>"index.js"!==e);o.pageComp.prompts[0].choices=e}if(s.existsSync(p)){const e=s.readdirSync(p).filter(e=>"index.ts"!==e);o.reactPageComp.prompts[0].choices=e}n.setGenerator("service",o.service),n.setGenerator("tService",o.tService),n.setGenerator("component",o.component),n.setGenerator("page",o.page),n.setGenerator("pageComp",o.pageComp),n.setGenerator("icon",o.icon),n.setGenerator("reactComponent",o.reactComponent),n.setGenerator("reactPage",o.reactPage),n.setGenerator("reactPageComp",o.reactPageComp)}export const reactPlop=function(e){};
@@ -0,0 +1,20 @@
1
+ <template>
2
+ <div class="comp-{{kebabCase name}}">{{bigCamelCase name}}组件</div>
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ import { reactive, ref } from 'vue';
7
+
8
+ interface DataType {
9
+ // 定义数据类型
10
+ }
11
+
12
+ console.log('1-开始创建组件-setup');
13
+ const data = reactive<DataType>({});
14
+ const name = ref<string>('');
15
+ console.log(data, name);
16
+ </script>
17
+ <style scoped lang="less">
18
+ @import './{{kebabCase name}}.less';
19
+ </style>
20
+
@@ -0,0 +1,20 @@
1
+ <template>
2
+ <div class="page-wrap page-{{kebabCase name}}">{{bigCamelCase name}}页面</div>
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ import { reactive, ref } from 'vue';
7
+
8
+ interface DataType {
9
+ // 定义数据类型
10
+ }
11
+
12
+ console.log('1-开始创建组件-setup');
13
+ const data = reactive<DataType>({});
14
+ const name = ref<string>('');
15
+ console.log(data, name);
16
+ </script>
17
+ <style scoped lang="less">
18
+ @import './{{kebabCase name}}.less';
19
+ </style>
20
+
@@ -0,0 +1,20 @@
1
+ <template>
2
+ <div class="page-comp-{{kebabCase name}}">{{bigCamelCase name}}组件</div>
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ import { reactive, ref } from 'vue';
7
+
8
+ interface DataType {
9
+ // 定义数据类型
10
+ }
11
+
12
+ console.log('1-开始创建组件-setup');
13
+ const data = reactive<DataType>({});
14
+ const name = ref<string>('');
15
+ console.log(data, name);
16
+ </script>
17
+ <style scoped lang="less">
18
+ @import './index.less';
19
+ </style>
20
+
@@ -4,7 +4,7 @@ import styles from './{{kebabCase name}}.module.less';
4
4
 
5
5
  const {{bigCamelCase name}}: React.FC = () => {
6
6
  return (
7
- <div className={classnames('qf-comp-{{kebabCase name}}')}>
7
+ <div className={classnames(styles['qf-comp-{{kebabCase name}}'])}>
8
8
  <h1>Hello, 这事一个页面内子组件{{bigCamelCase name}} 页面 !</h1>
9
9
  <p>This is a TypeScript and Less example.</p>
10
10
  </div>
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import classnames from 'classnames';
3
- import styles from './{{kebabCase name}}.module.less';
3
+ import styles from './index.module.less';
4
4
 
5
5
  const {{bigCamelCase name}}: React.FC = () => {
6
6
  return (
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qcf",
3
- "version": "0.1.12",
3
+ "version": "0.2.0",
4
4
  "description": "quick create file(QCF) is a package for quickly creating templates for front-end projects",
5
5
  "author": "xunzou",
6
6
  "type": "module",
@@ -13,7 +13,8 @@
13
13
  "plop": "^3.1.2"
14
14
  },
15
15
  "devDependencies": {
16
- "plop": "^3.1.2"
16
+ "plop": "^3.1.2",
17
+ "terser": "^5.0.0"
17
18
  },
18
19
  "dependencies": {
19
20
  "lodash.camelcase": "^4.3.0",
package/readme.md CHANGED
@@ -1,2 +1,150 @@
1
- # qcf
2
- 一个快速创建vue3 模板的功能;
1
+ # QCF (Quick Create File)
2
+
3
+ 一个基于 [Plop](https://plopjs.com/) 的前端项目代码生成工具,支持快速创建 Vue3 和 React 项目的模板文件。
4
+
5
+ ## 功能特性
6
+
7
+ ### Vue3 支持
8
+
9
+ > **TypeScript 支持**: Vue3 模板(component、page、pageComp)会自动检测项目是否使用 TypeScript,并生成对应的模板。检测优先级:
10
+ > 1. 环境变量 `QCF_USE_TS=true/false`(最高优先级)
11
+ > 2. 检查 `tsconfig.json` 是否存在
12
+ > 3. 检查 `package.json` 中是否包含 `typescript` 依赖
13
+ >
14
+ > 老项目无需任何改动,默认使用 JavaScript 模板;新项目如果配置了 TypeScript,会自动使用 TypeScript 模板。
15
+
16
+ 1. **component** - 创建通用组件
17
+ - 生成位置: `src/components/`
18
+ - 生成文件: `.vue` + `.less`
19
+ - 支持路径嵌套(如: `user/profile`)
20
+ - 自动支持 TypeScript(`<script setup lang="ts">`)
21
+
22
+ 2. **page** - 创建页面
23
+ - 生成位置: `src/views/`
24
+ - 生成文件: `.vue` + `.less`
25
+ - 支持路径嵌套
26
+ - 禁止使用 `components` 作为子路径名
27
+ - 自动支持 TypeScript(`<script setup lang="ts">`)
28
+
29
+ 3. **pageComp** - 创建页面内组件
30
+ - 生成位置: `src/views/{页面名}/components/`
31
+ - 生成文件: `index.vue` + `index.less`
32
+ - 自动检测现有页面列表供选择
33
+ - 自动支持 TypeScript(`<script setup lang="ts">`)
34
+
35
+ 4. **icon** - 创建 SVG 图标组件
36
+ - 生成位置: `src/components/icons/`
37
+ - 生成文件: `.vue`
38
+ - 支持 props: width, height, fill, stroke, color, spin, rtl
39
+
40
+ 5. **service** - 创建 API 服务(JavaScript)
41
+ - 生成位置: `src/api/`
42
+ - 生成文件: `{name}-service.js`
43
+ - 基于 BaseService 类
44
+
45
+ 6. **tService** - 创建 API 服务(TypeScript)
46
+ - 生成位置: `src/api/`
47
+ - 生成文件: `{name}-service.ts`
48
+ - 基于 BaseService 类
49
+
50
+ ### React 支持
51
+
52
+ 1. **reactComponent** - 创建 React 组件
53
+ - 生成位置: `src/components/`
54
+ - 生成文件: `.tsx` + `.module.less`
55
+ - 支持路径嵌套
56
+
57
+ 2. **reactPage** - 创建 React 页面
58
+ - 生成位置: `src/pages/`
59
+ - 生成文件: `.tsx` + `.module.less`
60
+ - 支持路径嵌套
61
+ - 禁止使用 `components` 作为子路径名
62
+
63
+ 3. **reactPageComp** - 创建 React 页面内组件
64
+ - 生成位置: `src/pages/{页面名}/components/`
65
+ - 生成文件: `index.tsx` + `index.module.less`
66
+ - 自动检测现有页面列表供选择
67
+
68
+ ## 安装使用
69
+
70
+ ### 安装
71
+
72
+ ```bash
73
+ npm install qcf --save-dev
74
+ # 或
75
+ yarn add qcf -D
76
+ ```
77
+
78
+ ### 配置
79
+
80
+ 在项目根目录创建 `plopfile.js`:
81
+
82
+ ```javascript
83
+ module.exports = async plop => {
84
+ await plop.load('qcf')
85
+ }
86
+ ```
87
+
88
+ ### 使用
89
+
90
+ 运行 Plop 命令:
91
+
92
+ ```bash
93
+ npx plop
94
+ ```
95
+
96
+ 然后根据提示选择要生成的模板类型,输入组件/页面名称即可。
97
+
98
+ ### TypeScript 支持
99
+
100
+ Vue3 模板会自动检测项目是否使用 TypeScript:
101
+
102
+ **自动检测**(推荐):
103
+ - 如果项目根目录存在 `tsconfig.json`,自动使用 TypeScript 模板
104
+ - 如果 `package.json` 中包含 `typescript` 依赖,自动使用 TypeScript 模板
105
+
106
+ **手动控制**:
107
+ ```bash
108
+ # 强制使用 TypeScript 模板
109
+ QCF_USE_TS=true npx plop
110
+
111
+ # 强制使用 JavaScript 模板
112
+ QCF_USE_TS=false npx plop
113
+ ```
114
+
115
+ 或者在 `plopfile.js` 中设置:
116
+ ```javascript
117
+ process.env.QCF_USE_TS = 'true'; // 或 'false'
118
+ module.exports = async plop => {
119
+ await plop.load('qcf')
120
+ }
121
+ ```
122
+
123
+ ## 命名规则
124
+
125
+ - 输入支持路径嵌套,如: `user/profile` 或 `admin/settings`
126
+ - 自动转换为 kebab-case 作为文件名
127
+ - 自动转换为 PascalCase 作为组件名
128
+ - 自动转换为 camelCase 作为变量名
129
+
130
+ ## 项目结构
131
+
132
+ ```
133
+ qcf/
134
+ ├── dist/ # 构建输出
135
+ ├── example/ # 使用示例
136
+ └── package.json
137
+ ```
138
+
139
+ ## 技术栈
140
+
141
+ - [Plop](https://plopjs.com/) - 代码生成工具
142
+ - [Handlebars](https://handlebarsjs.com/) - 模板引擎
143
+ - lodash.kebabcase - 命名转换
144
+ - lodash.camelcase - 命名转换
145
+ - lodash.upperfirst - 命名转换
146
+
147
+ ## 版本
148
+
149
+ 当前版本: v0.2.0
150
+