feima-shortcuts 0.1.5-beta.1 → 0.1.5-beta.2
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/package.json
CHANGED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
function toKebabFromComponent(componentName) {
|
|
5
|
+
const nameWithoutF = componentName.replace(/^F/, '');
|
|
6
|
+
return nameWithoutF
|
|
7
|
+
.replace(/([A-Z])/g, '-$1')
|
|
8
|
+
.toLowerCase()
|
|
9
|
+
.replace(/^-/, '');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function ensureDir(dirPath) {
|
|
13
|
+
if (!fs.existsSync(dirPath)) {
|
|
14
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function buildMarkdown(componentName) {
|
|
19
|
+
const kebab = toKebabFromComponent(componentName);
|
|
20
|
+
const demoName = `Demo${componentName}`;
|
|
21
|
+
return `# ${componentName}\n\nA thin wrapper around Element Plus \`El${componentName.replace(/^F/, '')}\`.\n\n## Preview\n\n:::: demo ${demoName}\n::::\n`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function buildDemoVue(componentName) {
|
|
25
|
+
const tag = componentName;
|
|
26
|
+
return `<template>
|
|
27
|
+
<div>
|
|
28
|
+
<${tag} />
|
|
29
|
+
</div>
|
|
30
|
+
</template>
|
|
31
|
+
<script setup lang="ts">
|
|
32
|
+
import { ${componentName} } from "feima-vue-ui";
|
|
33
|
+
</script>
|
|
34
|
+
`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function updateVitepressConfig(componentName, docsRoot) {
|
|
38
|
+
const configPath = path.resolve(docsRoot, '.vitepress/config.ts');
|
|
39
|
+
if (!fs.existsSync(configPath)) {
|
|
40
|
+
console.log('⚠️ docs/.vitepress/config.ts 不存在,跳过侧边栏更新');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let content = fs.readFileSync(configPath, 'utf8');
|
|
45
|
+
|
|
46
|
+
// Find the components items array inside sidebar
|
|
47
|
+
const itemsSectionMatch = content.match(/(items:\s*\[)([\s\S]*?)(\]\s*,\s*\}\s*,?\s*\]\s*,?\s*\},?)/);
|
|
48
|
+
// Fallback: specifically locate the Components section with text: "组件"
|
|
49
|
+
let sectionStartIndex = -1;
|
|
50
|
+
let sectionEndIndex = -1;
|
|
51
|
+
if (!itemsSectionMatch) {
|
|
52
|
+
const compSectionStart = content.indexOf('{\n text: "组件"');
|
|
53
|
+
if (compSectionStart !== -1) {
|
|
54
|
+
const itemsStart = content.indexOf('items: [', compSectionStart);
|
|
55
|
+
if (itemsStart !== -1) {
|
|
56
|
+
sectionStartIndex = itemsStart + 'items: ['.length;
|
|
57
|
+
// naive bracket matching for the items array
|
|
58
|
+
let depth = 1;
|
|
59
|
+
let i = itemsStart + 'items: ['.length;
|
|
60
|
+
while (i < content.length && depth > 0) {
|
|
61
|
+
const ch = content[i];
|
|
62
|
+
if (ch === '[') depth++;
|
|
63
|
+
if (ch === ']') depth--;
|
|
64
|
+
i++;
|
|
65
|
+
}
|
|
66
|
+
sectionEndIndex = i - 1; // position of closing ']'
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const kebab = toKebabFromComponent(componentName);
|
|
72
|
+
const newItem = ` { text: "${componentName}", link: "/components/f-${kebab}" },`;
|
|
73
|
+
|
|
74
|
+
if (itemsSectionMatch) {
|
|
75
|
+
const fullMatch = itemsSectionMatch[0];
|
|
76
|
+
const inner = itemsSectionMatch[2];
|
|
77
|
+
if (!new RegExp(`text:\\s*"${componentName}"`).test(inner)) {
|
|
78
|
+
const hasTrailingNewline = /\n\s*$/.test(inner);
|
|
79
|
+
const injectedInner = inner + (hasTrailingNewline ? '' : '\n') + newItem + '\n ';
|
|
80
|
+
const replaced = fullMatch.replace(inner, injectedInner);
|
|
81
|
+
content = content.replace(fullMatch, replaced);
|
|
82
|
+
fs.writeFileSync(configPath, content);
|
|
83
|
+
console.log('📝 已更新 docs/.vitepress/config.ts 侧边栏');
|
|
84
|
+
} else {
|
|
85
|
+
console.log(`ℹ️ 侧边栏已包含 ${componentName},跳过`);
|
|
86
|
+
}
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (sectionStartIndex !== -1 && sectionEndIndex !== -1) {
|
|
91
|
+
const inner = content.slice(sectionStartIndex, sectionEndIndex);
|
|
92
|
+
if (!new RegExp(`text:\\s*"${componentName}"`).test(inner)) {
|
|
93
|
+
const before = content.slice(0, sectionStartIndex);
|
|
94
|
+
const after = content.slice(sectionEndIndex);
|
|
95
|
+
const injected = inner + (inner.endsWith('\n') ? '' : '\n') + newItem;
|
|
96
|
+
content = before + injected + after;
|
|
97
|
+
fs.writeFileSync(configPath, content);
|
|
98
|
+
console.log('📝 已更新 docs/.vitepress/config.ts 侧边栏');
|
|
99
|
+
} else {
|
|
100
|
+
console.log(`ℹ️ 侧边栏已包含 ${componentName},跳过`);
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
console.log('⚠️ 未能定位到侧边栏组件分组,跳过更新');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function run(answers) {
|
|
108
|
+
if (!answers || !answers['component-name']) {
|
|
109
|
+
console.log('❌ 请提供组件名称');
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
let componentName = answers['component-name'];
|
|
113
|
+
if (!componentName.startsWith('F')) {
|
|
114
|
+
componentName = 'F' + componentName.charAt(0).toUpperCase() + componentName.slice(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const cwd = process.cwd();
|
|
118
|
+
const docsRoot = path.resolve(cwd, 'docs');
|
|
119
|
+
const compsDir = path.resolve(docsRoot, 'components');
|
|
120
|
+
const examplesDir = path.resolve(docsRoot, 'examples');
|
|
121
|
+
ensureDir(docsRoot);
|
|
122
|
+
ensureDir(compsDir);
|
|
123
|
+
ensureDir(examplesDir);
|
|
124
|
+
|
|
125
|
+
const kebab = toKebabFromComponent(componentName);
|
|
126
|
+
|
|
127
|
+
// Write markdown page
|
|
128
|
+
const mdPath = path.resolve(compsDir, `f-${kebab}.md`);
|
|
129
|
+
if (!fs.existsSync(mdPath)) {
|
|
130
|
+
fs.writeFileSync(mdPath, buildMarkdown(componentName));
|
|
131
|
+
console.log(`✅ 文档已生成: docs/components/f-${kebab}.md`);
|
|
132
|
+
} else {
|
|
133
|
+
console.log(`ℹ️ 文档已存在: docs/components/f-${kebab}.md`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Write demo component
|
|
137
|
+
const demoName = `Demo${componentName}`;
|
|
138
|
+
const demoPath = path.resolve(examplesDir, `${demoName}.vue`);
|
|
139
|
+
if (!fs.existsSync(demoPath)) {
|
|
140
|
+
fs.writeFileSync(demoPath, buildDemoVue(componentName));
|
|
141
|
+
console.log(`✅ 示例已生成: docs/examples/${demoName}.vue`);
|
|
142
|
+
} else {
|
|
143
|
+
console.log(`ℹ️ 示例已存在: docs/examples/${demoName}.vue`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Update vitepress sidebar
|
|
147
|
+
updateVitepressConfig(componentName, docsRoot);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
module.exports = { run };
|
|
151
|
+
|
|
152
|
+
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const docsGenerator = require('./docs');
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* 生成新的Vue组件
|
|
@@ -51,6 +52,13 @@ function generateComponent(answers) {
|
|
|
51
52
|
|
|
52
53
|
// 更新解析器
|
|
53
54
|
updateResolver(componentName);
|
|
55
|
+
|
|
56
|
+
// 生成文档与示例(独立脚本)
|
|
57
|
+
try {
|
|
58
|
+
docsGenerator.run({ 'component-name': componentName });
|
|
59
|
+
} catch (e) {
|
|
60
|
+
console.log('⚠️ 生成文档时发生错误:', e && e.message ? e.message : e);
|
|
61
|
+
}
|
|
54
62
|
|
|
55
63
|
console.log(`✅ 组件 ${componentName} 生成成功!`);
|
|
56
64
|
console.log(`📁 组件目录: ${folderName}`);
|