Skip to content

Commit f45bc0d

Browse files
committed
feat: Support backend plug-ins and standard plug-ins
1 parent d82df07 commit f45bc0d

18 files changed

+1060
-21
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
node_modules
1+
node_modules
2+
dist

bin/index.js

+133-20
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,135 @@
11
#! /usr/bin/env node
22

3-
const prompts = require('prompts');
4-
5-
const selectPluginTYpe = [
6-
{
7-
type: 'select',
8-
name: 'pluginType',
9-
message: 'What type of plugin do you want to create?',
10-
choices: [
11-
{ title: 'Backend Plugin', value: 'backend' },
12-
{ title: 'Frontend Plugin', value: 'frontend' },
13-
{ title: "Standard Plugin", value: 'standard' }
14-
]
15-
}
16-
];
17-
18-
(async () => {
19-
const { pluginType } = await prompts(selectPluginTYpe);
20-
console.log(pluginType);
21-
}
22-
)();
3+
import prompts from 'prompts'
4+
import path from 'path'
5+
import fs from 'fs'
6+
import humps from 'humps'
7+
import { exec } from 'child_process'
8+
import ora from 'ora'
9+
import { URL } from 'node:url';
10+
11+
const __dirname = path.dirname(new URL(import.meta.url).pathname)
12+
13+
const args = process.argv.slice(2)
14+
15+
const selectPluginType = [
16+
{
17+
type: 'text',
18+
name: 'pluginName',
19+
message: 'What is the name of your plugin?',
20+
initial: args[0] || '',
21+
validate: (name) => {
22+
if (!name) {
23+
return 'Plugin name is required'
24+
}
25+
if (!validatePluginName(name)) {
26+
return 'Invalid plugin name, please use only letters, numbers, underscores, and hyphens, and cannot start with a number.'
27+
}
28+
return true
29+
},
30+
},
31+
{
32+
type: 'select',
33+
name: 'pluginType',
34+
message: 'What type of plugin do you want to create?',
35+
choices: [
36+
{ title: 'Backend Plugin', value: 'backend' },
37+
{ title: 'Standard Plugin', value: 'standard' },
38+
],
39+
},
40+
]
41+
42+
const validatePluginName = (name) => {
43+
const reg = /^[a-zA-Z_][a-zA-Z0-9_-]*$/
44+
return reg.test(name)
45+
}
46+
47+
const createPluginDir = async (name) => {
48+
const pluginName = humps.camelize(name)
49+
const packageName = humps.decamelize(pluginName, { separator: '-' })
50+
const pluginDisplayName = humps.pascalize(pluginName)
51+
const pluginSlugName = humps.decamelize(pluginName, { separator: '_' })
52+
const targetPath = path.resolve(process.cwd(), packageName)
53+
54+
fs.mkdirSync(targetPath)
55+
return {
56+
pluginName,
57+
packageName,
58+
pluginDisplayName,
59+
pluginSlugName,
60+
targetPath,
61+
}
62+
}
63+
64+
const createBackendPlugin = async ({
65+
pluginName,
66+
pluginDisplayName,
67+
pluginSlugName,
68+
targetPath,
69+
}) => {
70+
const templatePath = path.resolve(__dirname, '../template/plugin.go')
71+
const content = fs.readFileSync(templatePath, 'utf-8')
72+
const result = content
73+
.replace(/{{plugin_display_name}}/g, pluginDisplayName)
74+
.replace(/{{plugin_slug_name}}/g, pluginSlugName)
75+
76+
fs.writeFileSync(path.resolve(targetPath, `${pluginName}.go`), result)
77+
}
78+
79+
const createStandardPlugin = async ({ packageName, pluginSlugName, targetPath }) => {
80+
const templatePath = path.resolve(__dirname, '../template/ui')
81+
fs.readdirSync(templatePath).forEach((file) => {
82+
const content = fs.readFileSync(path.resolve(templatePath, file), 'utf-8')
83+
const result = content
84+
.replace(/{{plugin_name}}/g, packageName)
85+
.replace(/{{plugin_slug_name}}/g, pluginSlugName)
86+
fs.writeFileSync(path.resolve(targetPath, file), result)
87+
})
88+
}
89+
90+
const createI18n = async ({ pluginSlugName, targetPath, pluginType }) => {
91+
const i18nDir = path.resolve(__dirname, '../template/i18n')
92+
fs.mkdirSync(path.resolve(targetPath, 'i18n'))
93+
fs.readdirSync(i18nDir).forEach((file) => {
94+
if (pluginType === 'backend' && file === 'index.ts') return
95+
const content = fs.readFileSync(path.resolve(i18nDir, file), 'utf-8')
96+
const result = content.replace(/{{plugin_slug_name}}/g, pluginSlugName)
97+
fs.writeFileSync(path.resolve(targetPath, 'i18n', file), result)
98+
})
99+
}
100+
101+
const createReadme = async ({ pluginName, targetPath }) => {
102+
const name = humps.pascalize(pluginName)
103+
const content = `# ${name} Plugin`
104+
fs.writeFileSync(path.resolve(targetPath, 'README.md'), content)
105+
}
106+
const installGoMod = async ({ packageName }) => {
107+
process.chdir(path.resolve(process.cwd(), packageName))
108+
await exec(`go mod init github.com/apache/incubator-answer-plugins/${packageName} && go mod tidy`)
109+
}
110+
111+
const installNpm = async () => {
112+
await exec(`pnpm install`)
113+
}
114+
115+
;(async () => {
116+
const { pluginType, pluginName } = await prompts(selectPluginType)
117+
const result = await createPluginDir(pluginName)
118+
createI18n({
119+
...result,
120+
pluginType,
121+
})
122+
if (pluginType === 'standard') {
123+
await createStandardPlugin(result)
124+
125+
const spinner = ora('Loading unicorns').start()
126+
await installNpm()
127+
spinner.succeed('Unicorns loaded')
128+
}
129+
130+
await createBackendPlugin(result)
131+
await installGoMod(result)
132+
133+
await createReadme(result)
134+
})()
135+

package.json

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"name": "create-answer-plugin",
33
"version": "1.0.0",
44
"description": "",
5+
"type": "module",
56
"main": "index.js",
67
"bin": {
78
"create-answer-plugin": "bin/index.js"
@@ -12,6 +13,8 @@
1213
"author": "",
1314
"license": "MIT",
1415
"dependencies": {
16+
"humps": "^2.0.1",
17+
"ora": "^8.0.1",
1518
"prompts": "^2.4.2",
1619
"yargs": "^17.7.2"
1720
}

0 commit comments

Comments
 (0)