forked from jd-opensource/nutui
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcreateComponentMode.js
244 lines (225 loc) · 8.21 KB
/
createComponentMode.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
// 创建模板
const inquirer = require('inquirer');
const ora = require('ora');
const fs = require('fs');
const path = require('path');
const esprima = require('esprima');
const estraverse = require('estraverse');
const escodegen = require('escodegen');
const demoModel = require('./demo');
const config = require('../src/config.json');
const nav = config.nav;
let spinner;
let componentConfig = {
version: '3.0.0', //版本
name: '', //组件名称
cType: '', //组件属于哪种类型
cName: '', //组件中文名称
desc: '', //组件描述
show: '', //组件是否显示在demo/文档中
tarodoc: false, //是否显示taro文档
type: 'component',
// taro: true, //是否生成.taro.vue文件,因为目前默认组件都会生成,所以,此项目前用不到
// exportEmpty: true, //表示是否要在生成运行时文件时导出组件模块,目前用不到
// exportEmptyTaro: true, //表示是否要在生成taro运行文件时导出组件模块,目前用不到
author: ''
};
const questions = [
{
type: 'input',
name: 'name',
message: '组件英文名(每个单词的首字母都大写,例如InputNumber):',
validate(value) {
value = value.trim();
if (!value) return '组件名称不能为空';
if (!/^[A-Z][a-zA-Z]*$/.test(value)) return '组件名称采用驼峰式命名,且首字母大写,如InputNumber';
for (let i = 0; i < nav.length; i++) {
const item = nav[i];
const cItem = item.packages.find((values) => values.name === value);
if (cItem) return `${value}已存在!`;
}
return true;
}
},
{
type: 'input',
name: 'cName',
message: '组件中文名(10字以内):',
validate(value) {
value = value.trim();
if (value && value.length <= 10) return true;
return `组件名称不能为空,并且在10字以内`;
}
},
{
type: 'input',
name: 'desc',
message: '组件描述(50字以内):',
validate(value) {
value = value.trim();
if (value && value.length <= 50) return true;
return `组件描述不能为空,并且在50字以内`;
}
},
{
type: 'rawlist',
name: 'cType',
message: '请选择组件的分类',
choices: nav.map((item) => `${item.name}`),
validate(value) {
value = +value.trim();
if (value && /\d+$/.test(value) && value <= nav.length) return true;
return `您的输入有误,请输入编号`;
}
},
{
type: 'confrim',
name: 'type',
message: '组件是否支持函数式调用(y/n)',
default: 'n'
},
{
type: 'confrim',
name: 'show',
message: '组件是否显示在文档和demo中(y/n),如:SwiperItem则不需要',
default: 'y'
},
{
type: 'input',
name: 'author',
message: '请输入组件作者(可署化名)'
}
];
const traverseAst = (ast, componentName, componentType) => {
estraverse.traverse(ast, {
enter: (node) => {
if (node.type === 'VariableDeclarator' && node.id.name === 'subpackages') {
node.init.elements.forEach((item) => {
const itemKey = item.properties.find((value) => value.key.name === 'root').value.value;
const itemValue = item.properties.find((value) => value.key.name === 'pages').value.elements;
const path = `pages/${componentName}/index`;
if (itemKey === componentType && !itemValue.find((subItem) => subItem.value === path)) {
itemValue.push({
type: 'Literal',
value: path,
raw: "'pages/'" + componentName + "'/index'"
});
}
});
}
}
});
};
const generateToFile = (ast, taroConfigPath) => {
const code = escodegen.generate(ast);
fs.writeFileSync(taroConfigPath, code, 'utf8');
};
const createSource = async (paths) => {
/**生成 vue .taro.vue 文件 */
const sourcePath = paths.sourcePath;
const name = sourcePath.substring(sourcePath.lastIndexOf('/') + 1);
const content = demoModel(name).source;
const filePath = path.join(sourcePath, 'index.vue');
const taroFilePath = path.join(sourcePath, 'index.taro.vue');
if (!fs.existsSync(filePath)) fs.writeFileSync(filePath, content);
if (!fs.existsSync(taroFilePath)) fs.writeFileSync(taroFilePath, content);
};
const createDemo = (paths) => {
/**生成 demo tarodemo taro配置文件 */
const sourcePath = paths.sourcePath;
const name = sourcePath.substring(sourcePath.lastIndexOf('/') + 1);
const taroPath = `${paths.taroPath}/pages/${name}`;
const demo = demoModel(name).demo;
const taroDemo = demoModel(name).taroDemo;
const filePath = path.join(sourcePath, 'demo.vue');
const taroFilePath = path.join(taroPath, 'index.vue');
const taroConfigPath = path.join(taroPath, 'index.config.ts');
if (!fs.existsSync(filePath)) fs.writeFileSync(filePath, demo);
if (!fs.existsSync(taroFilePath)) fs.writeFileSync(taroFilePath, taroDemo);
if (!fs.existsSync(taroConfigPath))
fs.writeFileSync(taroConfigPath, `export default { navigationBarTitleText: '${componentConfig.name}' }`);
};
const createDoc = (paths) => {
/**生成doc文档 */
const sourcePath = paths.sourcePath;
const name = sourcePath.substring(sourcePath.lastIndexOf('/') + 1);
const doc = demoModel(name).doc;
const filePath = path.join(sourcePath, 'doc.md');
if (!fs.existsSync(filePath)) fs.writeFileSync(filePath, doc);
};
const createScss = (paths) => {
/**生成scss文件 */
const sourcePath = paths.sourcePath;
const name = sourcePath.substring(sourcePath.lastIndexOf('/') + 1);
const scss = `.nut-${name} {}`;
const filePath = path.join(sourcePath, 'index.scss');
if (!fs.existsSync(filePath)) fs.writeFileSync(filePath, scss);
};
const createTest = (paths) => {
/**生成测试文件 */
const sourcePath = paths.sourcePath;
const name = sourcePath.substring(sourcePath.lastIndexOf('/') + 1);
const testPath = path.join(`src/packages/__VUE/${name}/__tests__`);
if (!fs.existsSync(testPath)) fs.mkdirSync(testPath);
const testFilePath = path.join(testPath, `${name}.spec.ts`);
if (!fs.existsSync(testFilePath)) fs.writeFileSync(testFilePath, `import { mount } from '@vue/test-utils';`);
};
const updateConfig = () => {
/**更新 config 文件 */
const componentTypeItem = nav.find((navitem) => navitem.name === componentConfig.cType);
if (!componentTypeItem.packages.find((item) => item.name === componentConfig.name)) {
componentTypeItem.packages.push(componentConfig);
}
const filePath = path.join(`src/config.json`);
const tempfile = JSON.stringify(config, null, 2);
fs.writeFileSync(filePath, tempfile);
};
const createDir = () => {
const componentName = componentConfig.name.toLowerCase();
const componentType = nav.find((navitem) => navitem.name === componentConfig.cType).enName;
const sourcePath = path.join(`src/packages/__VUE/${componentName}`);
const taroPath = path.join(`src/sites/mobile-taro/vue/src/${componentType}`);
if (!fs.existsSync(sourcePath)) fs.mkdirSync(sourcePath);
if (!fs.existsSync(taroPath)) fs.mkdirSync(`${taroPath}/pages`);
if (!fs.existsSync(`${taroPath}/pages/${componentName}`)) fs.mkdirSync(`${taroPath}/pages/${componentName}`);
const taroConfigPath = path.join(`src/sites/mobile-taro/vue/src/app.config.ts`);
try {
const taroConfigData = fs.readFileSync(taroConfigPath, 'utf8');
const ast = esprima.parseModule(taroConfigData);
traverseAst(ast, componentName, componentType);
generateToFile(ast, taroConfigPath);
} catch (err) {
console.log(err);
}
return {
sourcePath,
taroPath
};
};
const createFile = (filePath) => {
createSource(filePath);
createDemo(filePath);
createDoc(filePath);
createScss(filePath);
createTest(filePath);
updateConfig();
spinner.succeed('组件模板生成完毕,请开始你的表演~');
process.exit();
};
const create = () => {
const filePath = createDir();
createFile(filePath);
};
const init = () => {
inquirer.prompt(questions).then((answers) => {
answers.show = answers.show === 'y' ? true : false;
answers.type = answers.type === 'y' ? 'methods' : 'component';
componentConfig = Object.assign(componentConfig, answers);
spinner = ora('正在生成组件模版,请稍后...').start();
create();
});
};
const createComponent = () => {
init();
};
createComponent();