create-pubinfo 2.0.0 → 2.0.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/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,260 @@
1
+ #!/usr/bin/env node
2
+ import { bootstrop, readPackageJSON, run, validateInput } from "./helper-D73z-FZU.js";
3
+ import { checkbox, confirm, input, select } from "@inquirer/prompts";
4
+ import { Command } from "commander";
5
+ import consola from "consola";
6
+ import { resolve } from "node:path";
7
+ import process from "node:process";
8
+ import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
9
+ import colors from "ansi-colors";
10
+ import { downloadTemplate } from "giget";
11
+ import ora from "ora";
12
+ import { coerce, compare } from "semver";
13
+ import { ofetch } from "ofetch";
14
+ import { rimrafSync } from "rimraf";
15
+ import { readFile, writeFile } from "node:fs/promises";
16
+ import { parseJSON, parseJSONC, stringifyJSON } from "confbox";
17
+
18
+ //#region src/v1/constant.ts
19
+ const REMOTE_URL = "http://106.53.74.49:20000/templates";
20
+ const PKG_NAME = "monorepo-project-template";
21
+ const VERSION_FILE = "version.json";
22
+ const SETTING_FILE_PATH = "src/settings.default.ts";
23
+ const OPENAPI_FILE_PATH = "openapi.config.ts";
24
+ const APPS_DIR = "apps";
25
+ const APPS = [{
26
+ name: "用户权限系统(rbac)",
27
+ value: "rbac"
28
+ }];
29
+ const METADATA_DIR = "configs/metadata";
30
+ const META_FILENAME = "pubinfo.json";
31
+
32
+ //#endregion
33
+ //#region src/v1/download.ts
34
+ const pubinfo = async (input$1, { auth }) => {
35
+ const semver = coerce(input$1);
36
+ return {
37
+ name: "pubinfo",
38
+ version: input$1,
39
+ headers: { Authorization: `token ${auth}` },
40
+ tar: `${REMOTE_URL}/${PKG_NAME}-${semver?.version}.tar.gz`
41
+ };
42
+ };
43
+ async function download(options) {
44
+ const finish = loading();
45
+ try {
46
+ await downloadTemplate(`pubinfo:${PKG_NAME}-${options.version}`, {
47
+ providers: { pubinfo },
48
+ force: true,
49
+ forceClean: true,
50
+ dir: options.dir
51
+ });
52
+ } catch (error) {
53
+ if (error.message.includes(REMOTE_URL)) error.message = error.message.replace(REMOTE_URL, "[REMOTE_URL]");
54
+ consola.error(`下载失败: ${error.message}`);
55
+ process.exit(1);
56
+ } finally {
57
+ finish();
58
+ }
59
+ }
60
+ function loading() {
61
+ const startTime = Date.now();
62
+ const spinner = ora({
63
+ text: "下载中...",
64
+ color: "green"
65
+ });
66
+ spinner.start();
67
+ return () => {
68
+ const { green, yellow } = colors;
69
+ spinner.stop();
70
+ consola.success(`${green("下载完成, 用时")} ${yellow(`${Date.now() - startTime}`)} ${green("ms.")}`);
71
+ };
72
+ }
73
+
74
+ //#endregion
75
+ //#region src/v1/fetch.ts
76
+ async function fetchVersion() {
77
+ const versions = await ofetch(`${REMOTE_URL}/${VERSION_FILE}`);
78
+ versions.sort((v1, v2) => -compare(v1, v2));
79
+ return {
80
+ latest: versions[0],
81
+ list: versions
82
+ };
83
+ }
84
+ async function fetchData() {
85
+ const spinner = ora({
86
+ text: "检测网络连接...",
87
+ color: "blue"
88
+ });
89
+ spinner.start();
90
+ try {
91
+ return { version: await fetchVersion() };
92
+ } catch (error) {
93
+ if (error.message.includes(REMOTE_URL)) error.message = error.message.replace(REMOTE_URL, "[REMOTE_URL]");
94
+ consola.error(`网络连接异常: ${error.message}`);
95
+ process.exit(1);
96
+ } finally {
97
+ spinner.stop();
98
+ }
99
+ }
100
+
101
+ //#endregion
102
+ //#region src/v1/utils.ts
103
+ function assignValues(target, source) {
104
+ for (const [key, value] of Object.entries(source)) target[key] = value;
105
+ }
106
+ async function readJSON(path) {
107
+ const blob = await readFile(path, "utf-8");
108
+ let parsed;
109
+ try {
110
+ parsed = parseJSON(blob);
111
+ } catch {
112
+ parsed = parseJSONC(blob);
113
+ }
114
+ return parsed;
115
+ }
116
+ async function writeJSON(path, json) {
117
+ await writeFile(path, stringifyJSON(json));
118
+ }
119
+ /**
120
+ * 重写文件内容
121
+ */
122
+ function rewriteFile(path, fn) {
123
+ if (!existsSync(path)) {
124
+ consola.error(`RewriteFile fail: ${path} does not exist`);
125
+ return;
126
+ }
127
+ try {
128
+ const content = readFileSync(path, { encoding: "utf-8" });
129
+ writeFileSync(path, fn(content));
130
+ } catch (error) {
131
+ consola.error(`RewriteFile fail: ${error}`);
132
+ }
133
+ }
134
+
135
+ //#endregion
136
+ //#region src/v1/rewrite.ts
137
+ async function rewrite(options) {
138
+ const { dir } = options;
139
+ const root = process.cwd();
140
+ const projectDir = resolve(root, dir);
141
+ writeMetaJSON(resolve(projectDir, METADATA_DIR), options);
142
+ writeApps(resolve(projectDir, APPS_DIR), options);
143
+ }
144
+ /**
145
+ * 写入项目整体配置
146
+ */
147
+ async function writeMetaJSON(metaDir, options) {
148
+ const { key, version, apps, openapi } = options;
149
+ const path = resolve(metaDir, META_FILENAME);
150
+ try {
151
+ const json = await readJSON(path);
152
+ assignValues(json, {
153
+ key,
154
+ version,
155
+ apps,
156
+ openapi
157
+ });
158
+ await writeJSON(path, json);
159
+ } catch (error) {
160
+ consola.error(`初始化 metadata 失败: ${error}`);
161
+ }
162
+ }
163
+ /**
164
+ * 处理目录 `/apps` 下的各个应用
165
+ */
166
+ function writeApps(appsDir, options) {
167
+ const { key, apps = [], openapi } = options;
168
+ if (!existsSync(appsDir)) {
169
+ consola.error(`初始化 apps 失败: ${appsDir} 不存在`);
170
+ return;
171
+ }
172
+ try {
173
+ readdirSync(appsDir).forEach((app) => {
174
+ const appPath = resolve(appsDir, app);
175
+ const settingPath = resolve(appPath, SETTING_FILE_PATH);
176
+ const openapiPath = resolve(appPath, OPENAPI_FILE_PATH);
177
+ if (!apps.includes(app)) {
178
+ rimrafSync(appPath);
179
+ return;
180
+ }
181
+ rewriteFile(settingPath, (content) => {
182
+ return content.replace(/storagePrefix:\s*'([^']*)'/g, `storagePrefix: '${key}'`);
183
+ });
184
+ rewriteFile(openapiPath, (content) => {
185
+ return content.replace(/enabled:[^,]+,/g, `enabled: ${openapi},`);
186
+ });
187
+ });
188
+ } catch (error) {
189
+ consola.error(`初始化 apps 失败: ${error}`);
190
+ }
191
+ }
192
+
193
+ //#endregion
194
+ //#region src/v1/index.ts
195
+ async function runV1() {
196
+ const { version } = await fetchData();
197
+ const answer = {};
198
+ answer.dir = await input({
199
+ message: "目录名称(dir)",
200
+ default: "my-app",
201
+ validate: validateInput
202
+ });
203
+ answer.key = await input({
204
+ message: "项目标识(key)",
205
+ default: answer.dir,
206
+ validate: validateInput
207
+ });
208
+ if (existsSync(answer.dir)) {
209
+ if (!await confirm({ message: `目录 ${colors.cyan(answer.dir)} 已存在,是否覆盖?` })) throw Error;
210
+ }
211
+ answer.openapi = await confirm({
212
+ message: "运行时自动生成接口对接文件,若关闭可通过指令生成(openapi)",
213
+ default: false
214
+ });
215
+ const optionsV1 = JSON.parse(JSON.stringify(answer));
216
+ optionsV1.version = await select({
217
+ message: "框架版本号(version)",
218
+ default: version.latest,
219
+ choices: version.list
220
+ });
221
+ optionsV1.apps = await checkbox({
222
+ message: "选择应用模块(apps)",
223
+ choices: APPS,
224
+ validate: (input$1) => {
225
+ if (input$1.length === 0) return "请至少选择一个应用";
226
+ return true;
227
+ }
228
+ });
229
+ await download(optionsV1);
230
+ await rewrite(optionsV1);
231
+ }
232
+
233
+ //#endregion
234
+ //#region src/cli.ts
235
+ async function main() {
236
+ const program = new Command();
237
+ const pkg = readPackageJSON();
238
+ program.name(pkg.name).description(pkg.description).version(pkg.version);
239
+ program.description("初始化框架").action(async () => {
240
+ bootstrop();
241
+ try {
242
+ if (await select({
243
+ message: "使用 v1/v2 版本?",
244
+ default: "v2",
245
+ choices: ["v1", "v2"]
246
+ }) === "v1") {
247
+ await runV1();
248
+ return;
249
+ }
250
+ await run(pkg.version);
251
+ } catch (error) {
252
+ consola.error(error);
253
+ }
254
+ });
255
+ program.parse();
256
+ }
257
+ main();
258
+
259
+ //#endregion
260
+ export { };
@@ -0,0 +1,217 @@
1
+ import { confirm, input, select } from "@inquirer/prompts";
2
+ import consola from "consola";
3
+ import { dirname, resolve } from "node:path";
4
+ import process, { cwd } from "node:process";
5
+ import { fileURLToPath } from "node:url";
6
+ import { copy } from "fs-extra";
7
+ import fs, { existsSync, renameSync } from "node:fs";
8
+ import colors from "ansi-colors";
9
+ import nodePlop from "node-plop";
10
+ import fonts from "cfonts";
11
+
12
+ //#region src/core/copy.ts
13
+ const __dirname$1 = dirname(fileURLToPath(import.meta.url));
14
+ async function copyTemplate(options) {
15
+ const { templateDir, targetDir, templateName } = {
16
+ templateDir: resolve(__dirname$1, "../templates"),
17
+ targetDir: cwd(),
18
+ ...options
19
+ };
20
+ await copy(resolve(templateDir, templateName), targetDir);
21
+ }
22
+
23
+ //#endregion
24
+ //#region src/core/generate.ts
25
+ const __dirname = dirname(fileURLToPath(import.meta.url));
26
+ async function generate(options) {
27
+ const root = cwd();
28
+ const plop = await nodePlop();
29
+ const { templateName, templateDir, targetDir } = {
30
+ templateDir: resolve(__dirname, "../templates"),
31
+ targetDir: options.dir ? resolve(root, options.dir) : void 0,
32
+ ...options
33
+ };
34
+ plop.setGenerator("generate", { actions: () => {
35
+ return [{
36
+ type: "addMany",
37
+ destination: targetDir,
38
+ base: resolve(templateDir, templateName),
39
+ templateFiles: resolve(templateDir, templateName, "**/*.hbs"),
40
+ globOptions: { dot: true }
41
+ }, async function copyRawFiles() {
42
+ const baseDir = resolve(templateDir, templateName);
43
+ async function walk(dir) {
44
+ const entries = await fs.promises.readdir(dir, { withFileTypes: true });
45
+ for (const e of entries) {
46
+ const srcPath = resolve(dir, e.name);
47
+ const rel = srcPath.replace(`${baseDir}/`, "");
48
+ if (e.isDirectory()) {
49
+ await walk(srcPath);
50
+ continue;
51
+ }
52
+ if (e.name.endsWith(".hbs")) continue;
53
+ const targetPath = resolve(targetDir, rel);
54
+ await fs.promises.mkdir(resolve(targetPath, ".."), { recursive: true });
55
+ await fs.promises.copyFile(srcPath, targetPath);
56
+ }
57
+ }
58
+ await walk(baseDir);
59
+ return `Copied raw files from ${templateName}`;
60
+ }];
61
+ } });
62
+ await plop.getGenerator("generate").runActions(options);
63
+ for (const [oldName, newName] of Object.entries({
64
+ _gitignore: ".gitignore",
65
+ _npmrc: ".npmrc"
66
+ })) {
67
+ const oldPath = resolve(targetDir, oldName);
68
+ const newPath = resolve(targetDir, newName || oldName);
69
+ if (existsSync(oldPath)) renameSync(oldPath, newPath);
70
+ }
71
+ consola.success(`${colors.green("项目模板初始化完成.")}`);
72
+ }
73
+
74
+ //#endregion
75
+ //#region src/utils.ts
76
+ function validateInput(input$1) {
77
+ if (/[<>:"/\\|?*\s]/.test(input$1)) return "错误提示: 该值不能包含空格或者非法字符 (例如, <>:\"/\\|?*).";
78
+ return true;
79
+ }
80
+
81
+ //#endregion
82
+ //#region src/core/interaction.ts
83
+ async function interaction() {
84
+ try {
85
+ const answer = {};
86
+ answer.dir = await input({
87
+ message: "目录名称(dir)",
88
+ default: "my-app",
89
+ validate: validateInput
90
+ });
91
+ answer.key = await input({
92
+ message: "项目标识(key)",
93
+ default: answer.dir,
94
+ validate: validateInput
95
+ });
96
+ if (existsSync(answer.dir)) {
97
+ if (!await confirm({ message: `目录 ${colors.cyan(answer.dir)} 已存在,是否覆盖?` })) throw Error;
98
+ }
99
+ answer.templateName = await select({
100
+ message: "选择模板类型",
101
+ default: "pubinfo-app",
102
+ choices: [{
103
+ name: "Web应用(app)",
104
+ value: "pubinfo-app"
105
+ }, {
106
+ name: "模块插件(module)",
107
+ value: "pubinfo-module"
108
+ }]
109
+ });
110
+ answer.openapi = await confirm({
111
+ message: "运行时自动生成接口对接文件,若关闭可通过指令生成(openapi)",
112
+ default: false
113
+ });
114
+ return answer;
115
+ } catch {
116
+ consola.fail("操作终止");
117
+ process.exit(1);
118
+ }
119
+ }
120
+
121
+ //#endregion
122
+ //#region src/core/index.ts
123
+ async function run(version$1) {
124
+ const options = await interaction();
125
+ generate({
126
+ ...options,
127
+ version: version$1
128
+ });
129
+ }
130
+
131
+ //#endregion
132
+ //#region package.json
133
+ var name = "create-pubinfo";
134
+ var type = "module";
135
+ var version = "2.0.2";
136
+ var description = "初始化项目框架";
137
+ var author = "Werheng <werheng.zhang@gmail.com>";
138
+ var license = "MIT";
139
+ var exports = { ".": {
140
+ "types": "./dist/index.d.ts",
141
+ "default": "./dist/index.js"
142
+ } };
143
+ var main = "./dist/index.js";
144
+ var module = "./dist/index.js";
145
+ var types = "./dist/index.d.ts";
146
+ var bin = { "pubinfo": "./dist/cli.js" };
147
+ var files = ["dist", "templates"];
148
+ var engines = { "node": "^20.19.0 || >=22.12.0" };
149
+ var scripts = {
150
+ "dev": "tsdown --watch src",
151
+ "build": "tsdown",
152
+ "cli": "node dist/cli.js",
153
+ "cli:clean": "rimraf ./my-app",
154
+ "lint": "eslint . --cache --fix"
155
+ };
156
+ var dependencies = {
157
+ "@inquirer/prompts": "catalog:node",
158
+ "ansi-colors": "catalog:node",
159
+ "cfonts": "catalog:node",
160
+ "commander": "catalog:node",
161
+ "confbox": "catalog:node",
162
+ "consola": "catalog:browser",
163
+ "fs-extra": "^11.3.2",
164
+ "giget": "catalog:node",
165
+ "node-plop": "catalog:node",
166
+ "ofetch": "catalog:http",
167
+ "ora": "catalog:node",
168
+ "rimraf": "catalog:node",
169
+ "semver": "catalog:node"
170
+ };
171
+ var devDependencies = { "@types/node": "catalog:ts" };
172
+ var package_default = {
173
+ name,
174
+ type,
175
+ version,
176
+ description,
177
+ author,
178
+ license,
179
+ exports,
180
+ main,
181
+ module,
182
+ types,
183
+ bin,
184
+ files,
185
+ engines,
186
+ scripts,
187
+ dependencies,
188
+ devDependencies
189
+ };
190
+
191
+ //#endregion
192
+ //#region src/helper.ts
193
+ function bootstrop() {
194
+ printLoGo("PUBINFO");
195
+ function printLoGo(logo) {
196
+ fonts.say(logo, {
197
+ font: "simple3d",
198
+ align: "left",
199
+ background: "transparent",
200
+ letterSpacing: 1,
201
+ lineHeight: 1,
202
+ space: true,
203
+ maxLength: 0,
204
+ spaceless: false,
205
+ gradient: ["blue", "magenta"],
206
+ independentGradient: false,
207
+ transitionGradient: false,
208
+ env: "node"
209
+ });
210
+ }
211
+ }
212
+ function readPackageJSON() {
213
+ return package_default;
214
+ }
215
+
216
+ //#endregion
217
+ export { bootstrop, copyTemplate, generate, interaction, readPackageJSON, run, validateInput };
@@ -0,0 +1,113 @@
1
+ //#region src/interface.d.ts
2
+ type TemplateName = 'pubinfo-app' | 'pubinfo-module';
3
+ interface Options {
4
+ /**
5
+ * 目录名称
6
+ */
7
+ dir?: string;
8
+ /**
9
+ * 项目唯一标识
10
+ */
11
+ key: string;
12
+ /**
13
+ * 运行时自动生成接口文件
14
+ */
15
+ openapi?: boolean;
16
+ /**
17
+ * 模板名称
18
+ */
19
+ templateName: TemplateName;
20
+ }
21
+ interface GenerateOptions extends Options {
22
+ /**
23
+ * 版本号
24
+ */
25
+ version: string;
26
+ /**
27
+ * 模板文件夹
28
+ */
29
+ templateDir?: string;
30
+ /**
31
+ * 目标文件夹
32
+ */
33
+ targetDir?: string;
34
+ }
35
+ interface CopyTemplatesOptions {
36
+ /**
37
+ * 模板名称
38
+ */
39
+ templateName: TemplateName;
40
+ /**
41
+ * 模板文件夹
42
+ */
43
+ templateDir?: string;
44
+ /**
45
+ * 目标文件夹
46
+ */
47
+ targetDir?: string;
48
+ }
49
+ //#endregion
50
+ //#region src/core/copy.d.ts
51
+ declare function copyTemplate(options: CopyTemplatesOptions): Promise<void>;
52
+ //#endregion
53
+ //#region src/core/generate.d.ts
54
+ declare function generate(options: GenerateOptions): Promise<void>;
55
+ //#endregion
56
+ //#region src/core/interaction.d.ts
57
+ declare function interaction(): Promise<GenerateOptions>;
58
+ //#endregion
59
+ //#region src/core/index.d.ts
60
+ declare function run(version: string): Promise<void>;
61
+ //#endregion
62
+ //#region src/helper.d.ts
63
+ declare function readPackageJSON(): {
64
+ name: string;
65
+ type: string;
66
+ version: string;
67
+ description: string;
68
+ author: string;
69
+ license: string;
70
+ exports: {
71
+ ".": {
72
+ types: string;
73
+ default: string;
74
+ };
75
+ };
76
+ main: string;
77
+ module: string;
78
+ types: string;
79
+ bin: {
80
+ pubinfo: string;
81
+ };
82
+ files: string[];
83
+ engines: {
84
+ node: string;
85
+ };
86
+ scripts: {
87
+ dev: string;
88
+ build: string;
89
+ cli: string;
90
+ "cli:clean": string;
91
+ lint: string;
92
+ };
93
+ dependencies: {
94
+ "@inquirer/prompts": string;
95
+ "ansi-colors": string;
96
+ cfonts: string;
97
+ commander: string;
98
+ confbox: string;
99
+ consola: string;
100
+ "fs-extra": string;
101
+ giget: string;
102
+ "node-plop": string;
103
+ ofetch: string;
104
+ ora: string;
105
+ rimraf: string;
106
+ semver: string;
107
+ };
108
+ devDependencies: {
109
+ "@types/node": string;
110
+ };
111
+ };
112
+ //#endregion
113
+ export { copyTemplate, generate, interaction, readPackageJSON, run };
package/dist/index.js CHANGED
@@ -1,432 +1,3 @@
1
- #!/usr/bin/env node
2
- import { checkbox, confirm, input, select } from "@inquirer/prompts";
3
- import { Command } from "commander";
4
- import consola from "consola";
5
- import fs, { existsSync, readFileSync, readdirSync, renameSync, writeFileSync } from "node:fs";
6
- import { dirname, resolve } from "node:path";
7
- import process, { cwd } from "node:process";
8
- import { fileURLToPath } from "node:url";
9
- import colors from "ansi-colors";
10
- import nodePlop from "node-plop";
11
- import fonts from "cfonts";
12
- import { downloadTemplate } from "giget";
13
- import ora from "ora";
14
- import { coerce, compare } from "semver";
15
- import { ofetch } from "ofetch";
16
- import { rimrafSync } from "rimraf";
17
- import { readFile, writeFile } from "node:fs/promises";
18
- import { parseJSON, parseJSONC, stringifyJSON } from "confbox";
1
+ import { copyTemplate, generate, interaction, readPackageJSON, run } from "./helper-D73z-FZU.js";
19
2
 
20
- //#region package.json
21
- var name = "create-pubinfo";
22
- var type = "module";
23
- var version = "2.0.0";
24
- var description = "初始化项目框架";
25
- var author = "Werheng <werheng.zhang@gmail.com>";
26
- var license = "MIT";
27
- var bin = { "pubinfo": "./dist/index.js" };
28
- var files = ["dist", "templates"];
29
- var engines = { "node": "^20.19.0 || >=22.12.0" };
30
- var scripts = {
31
- "dev": "tsdown --watch src",
32
- "build": "tsdown",
33
- "cli": "node dist/index.js",
34
- "cli:clean": "rimraf ./my-app",
35
- "lint": "eslint . --cache --fix"
36
- };
37
- var dependencies = {
38
- "@inquirer/prompts": "catalog:node",
39
- "ansi-colors": "catalog:node",
40
- "cfonts": "catalog:node",
41
- "commander": "catalog:node",
42
- "confbox": "catalog:node",
43
- "consola": "catalog:browser",
44
- "giget": "catalog:node",
45
- "node-plop": "catalog:node",
46
- "ofetch": "catalog:http",
47
- "ora": "catalog:node",
48
- "rimraf": "catalog:node",
49
- "semver": "catalog:node"
50
- };
51
- var devDependencies = { "@types/node": "catalog:ts" };
52
- var package_default = {
53
- name,
54
- type,
55
- version,
56
- description,
57
- author,
58
- license,
59
- bin,
60
- files,
61
- engines,
62
- scripts,
63
- dependencies,
64
- devDependencies
65
- };
66
-
67
- //#endregion
68
- //#region src/utils.ts
69
- function validateInput(input$1) {
70
- if (/[<>:"/\\|?*\s]/.test(input$1)) return "错误提示: 该值不能包含空格或者非法字符 (例如, <>:\"/\\|?*).";
71
- return true;
72
- }
73
-
74
- //#endregion
75
- //#region src/core/init.ts
76
- async function init() {
77
- try {
78
- const answer = {};
79
- answer.dir = await input({
80
- message: "目录名称(dir)",
81
- default: "my-app",
82
- validate: validateInput
83
- });
84
- answer.key = await input({
85
- message: "项目标识(key)",
86
- default: answer.dir,
87
- validate: validateInput
88
- });
89
- if (existsSync(answer.dir)) {
90
- if (!await confirm({ message: `目录 ${colors.cyan(answer.dir)} 已存在,是否覆盖?` })) throw Error;
91
- }
92
- answer.templateName = await select({
93
- message: "选择模板类型",
94
- default: "pubinfo-app",
95
- choices: [{
96
- name: "Web应用(app)",
97
- value: "pubinfo-app"
98
- }, {
99
- name: "模块插件(module)",
100
- value: "pubinfo-module"
101
- }]
102
- });
103
- answer.openapi = await confirm({
104
- message: "运行时自动生成接口对接文件,若关闭可通过指令生成(openapi)",
105
- default: false
106
- });
107
- return answer;
108
- } catch {
109
- consola.fail("操作终止");
110
- process.exit(1);
111
- }
112
- }
113
-
114
- //#endregion
115
- //#region src/core/index.ts
116
- const __dirname = dirname(fileURLToPath(import.meta.url));
117
- async function generate(version$1) {
118
- const options = await init();
119
- const root = cwd();
120
- const plop = await nodePlop();
121
- const templateDir = resolve(__dirname, "../templates");
122
- const destDir = resolve(root, options.dir);
123
- plop.setGenerator("generate", { actions: () => {
124
- return [{
125
- type: "addMany",
126
- destination: destDir,
127
- base: resolve(templateDir, options.templateName),
128
- templateFiles: resolve(templateDir, options.templateName, "**/*.hbs"),
129
- globOptions: { dot: true }
130
- }, async function copyRawFiles() {
131
- const baseDir = resolve(templateDir, options.templateName);
132
- async function walk(dir) {
133
- const entries = await fs.promises.readdir(dir, { withFileTypes: true });
134
- for (const e of entries) {
135
- const srcPath = resolve(dir, e.name);
136
- const rel = srcPath.replace(`${baseDir}/`, "");
137
- if (e.isDirectory()) {
138
- await walk(srcPath);
139
- continue;
140
- }
141
- if (e.name.endsWith(".hbs")) continue;
142
- const targetPath$1 = resolve(destDir, rel);
143
- await fs.promises.mkdir(resolve(targetPath$1, ".."), { recursive: true });
144
- await fs.promises.copyFile(srcPath, targetPath$1);
145
- }
146
- }
147
- await walk(baseDir);
148
- return `Copied raw files from ${options.templateName}`;
149
- }];
150
- } });
151
- await plop.getGenerator("generate").runActions({
152
- ...options,
153
- version: version$1
154
- });
155
- const renameFiles = {
156
- _gitignore: ".gitignore",
157
- _npmrc: ".npmrc"
158
- };
159
- const targetPath = resolve(root, options.dir);
160
- for (const [oldName, newName] of Object.entries(renameFiles)) {
161
- const oldPath = resolve(targetPath, oldName);
162
- const newPath = resolve(targetPath, newName || oldName);
163
- if (existsSync(oldPath)) renameSync(oldPath, newPath);
164
- }
165
- consola.success(`${colors.green("项目模板初始化完成.")}`);
166
- }
167
-
168
- //#endregion
169
- //#region src/log.ts
170
- function bootstrop() {
171
- printLoGo("PUBINFO");
172
- function printLoGo(logo) {
173
- fonts.say(logo, {
174
- font: "simple3d",
175
- align: "left",
176
- background: "transparent",
177
- letterSpacing: 1,
178
- lineHeight: 1,
179
- space: true,
180
- maxLength: 0,
181
- spaceless: false,
182
- gradient: ["blue", "magenta"],
183
- independentGradient: false,
184
- transitionGradient: false,
185
- env: "node"
186
- });
187
- }
188
- }
189
-
190
- //#endregion
191
- //#region src/v1/constant.ts
192
- const REMOTE_URL = "http://106.53.74.49:20000/templates";
193
- const PKG_NAME = "monorepo-project-template";
194
- const VERSION_FILE = "version.json";
195
- const SETTING_FILE_PATH = "src/settings.default.ts";
196
- const OPENAPI_FILE_PATH = "openapi.config.ts";
197
- const APPS_DIR = "apps";
198
- const APPS = [{
199
- name: "用户权限系统(rbac)",
200
- value: "rbac"
201
- }];
202
- const METADATA_DIR = "configs/metadata";
203
- const META_FILENAME = "pubinfo.json";
204
-
205
- //#endregion
206
- //#region src/v1/download.ts
207
- const pubinfo = async (input$1, { auth }) => {
208
- const semver = coerce(input$1);
209
- return {
210
- name: "pubinfo",
211
- version: input$1,
212
- headers: { Authorization: `token ${auth}` },
213
- tar: `${REMOTE_URL}/${PKG_NAME}-${semver?.version}.tar.gz`
214
- };
215
- };
216
- async function download(options) {
217
- const finish = loading();
218
- try {
219
- await downloadTemplate(`pubinfo:${PKG_NAME}-${options.version}`, {
220
- providers: { pubinfo },
221
- force: true,
222
- forceClean: true,
223
- dir: options.dir
224
- });
225
- } catch (error) {
226
- if (error.message.includes(REMOTE_URL)) error.message = error.message.replace(REMOTE_URL, "[REMOTE_URL]");
227
- consola.error(`下载失败: ${error.message}`);
228
- process.exit(1);
229
- } finally {
230
- finish();
231
- }
232
- }
233
- function loading() {
234
- const startTime = Date.now();
235
- const spinner = ora({
236
- text: "下载中...",
237
- color: "green"
238
- });
239
- spinner.start();
240
- return () => {
241
- const { green, yellow } = colors;
242
- spinner.stop();
243
- consola.success(`${green("下载完成, 用时")} ${yellow(`${Date.now() - startTime}`)} ${green("ms.")}`);
244
- };
245
- }
246
-
247
- //#endregion
248
- //#region src/v1/fetch.ts
249
- async function fetchVersion() {
250
- const versions = await ofetch(`${REMOTE_URL}/${VERSION_FILE}`);
251
- versions.sort((v1, v2) => -compare(v1, v2));
252
- return {
253
- latest: versions[0],
254
- list: versions
255
- };
256
- }
257
- async function fetchData() {
258
- const spinner = ora({
259
- text: "检测网络连接...",
260
- color: "blue"
261
- });
262
- spinner.start();
263
- try {
264
- return { version: await fetchVersion() };
265
- } catch (error) {
266
- if (error.message.includes(REMOTE_URL)) error.message = error.message.replace(REMOTE_URL, "[REMOTE_URL]");
267
- consola.error(`网络连接异常: ${error.message}`);
268
- process.exit(1);
269
- } finally {
270
- spinner.stop();
271
- }
272
- }
273
-
274
- //#endregion
275
- //#region src/v1/utils.ts
276
- function assignValues(target, source) {
277
- for (const [key, value] of Object.entries(source)) target[key] = value;
278
- }
279
- async function readJSON(path) {
280
- const blob = await readFile(path, "utf-8");
281
- let parsed;
282
- try {
283
- parsed = parseJSON(blob);
284
- } catch {
285
- parsed = parseJSONC(blob);
286
- }
287
- return parsed;
288
- }
289
- async function writeJSON(path, json) {
290
- await writeFile(path, stringifyJSON(json));
291
- }
292
- /**
293
- * 重写文件内容
294
- */
295
- function rewriteFile(path, fn) {
296
- if (!existsSync(path)) {
297
- consola.error(`RewriteFile fail: ${path} does not exist`);
298
- return;
299
- }
300
- try {
301
- const content = readFileSync(path, { encoding: "utf-8" });
302
- writeFileSync(path, fn(content));
303
- } catch (error) {
304
- consola.error(`RewriteFile fail: ${error}`);
305
- }
306
- }
307
-
308
- //#endregion
309
- //#region src/v1/rewrite.ts
310
- async function rewrite(options) {
311
- const { dir } = options;
312
- const root = process.cwd();
313
- const projectDir = resolve(root, dir);
314
- writeMetaJSON(resolve(projectDir, METADATA_DIR), options);
315
- writeApps(resolve(projectDir, APPS_DIR), options);
316
- }
317
- /**
318
- * 写入项目整体配置
319
- */
320
- async function writeMetaJSON(metaDir, options) {
321
- const { key, version: version$1, apps, openapi } = options;
322
- const path = resolve(metaDir, META_FILENAME);
323
- try {
324
- const json = await readJSON(path);
325
- assignValues(json, {
326
- key,
327
- version: version$1,
328
- apps,
329
- openapi
330
- });
331
- await writeJSON(path, json);
332
- } catch (error) {
333
- consola.error(`初始化 metadata 失败: ${error}`);
334
- }
335
- }
336
- /**
337
- * 处理目录 `/apps` 下的各个应用
338
- */
339
- function writeApps(appsDir, options) {
340
- const { key, apps = [], openapi } = options;
341
- if (!existsSync(appsDir)) {
342
- consola.error(`初始化 apps 失败: ${appsDir} 不存在`);
343
- return;
344
- }
345
- try {
346
- readdirSync(appsDir).forEach((app) => {
347
- const appPath = resolve(appsDir, app);
348
- const settingPath = resolve(appPath, SETTING_FILE_PATH);
349
- const openapiPath = resolve(appPath, OPENAPI_FILE_PATH);
350
- if (!apps.includes(app)) {
351
- rimrafSync(appPath);
352
- return;
353
- }
354
- rewriteFile(settingPath, (content) => {
355
- return content.replace(/storagePrefix:\s*'([^']*)'/g, `storagePrefix: '${key}'`);
356
- });
357
- rewriteFile(openapiPath, (content) => {
358
- return content.replace(/enabled:[^,]+,/g, `enabled: ${openapi},`);
359
- });
360
- });
361
- } catch (error) {
362
- consola.error(`初始化 apps 失败: ${error}`);
363
- }
364
- }
365
-
366
- //#endregion
367
- //#region src/v1/index.ts
368
- async function generateV1() {
369
- const { version: version$1 } = await fetchData();
370
- const answer = {};
371
- answer.dir = await input({
372
- message: "目录名称(dir)",
373
- default: "my-app",
374
- validate: validateInput
375
- });
376
- answer.key = await input({
377
- message: "项目标识(key)",
378
- default: answer.dir,
379
- validate: validateInput
380
- });
381
- if (existsSync(answer.dir)) {
382
- if (!await confirm({ message: `目录 ${colors.cyan(answer.dir)} 已存在,是否覆盖?` })) throw Error;
383
- }
384
- answer.openapi = await confirm({
385
- message: "运行时自动生成接口对接文件,若关闭可通过指令生成(openapi)",
386
- default: false
387
- });
388
- const optionsV1 = JSON.parse(JSON.stringify(answer));
389
- optionsV1.version = await select({
390
- message: "框架版本号(version)",
391
- default: version$1.latest,
392
- choices: version$1.list
393
- });
394
- optionsV1.apps = await checkbox({
395
- message: "选择应用模块(apps)",
396
- choices: APPS,
397
- validate: (input$1) => {
398
- if (input$1.length === 0) return "请至少选择一个应用";
399
- return true;
400
- }
401
- });
402
- await download(optionsV1);
403
- await rewrite(optionsV1);
404
- }
405
-
406
- //#endregion
407
- //#region src/index.ts
408
- async function main() {
409
- const program = new Command();
410
- program.name(package_default.name).description(package_default.description).version(package_default.version);
411
- program.description("初始化框架").action(async () => {
412
- bootstrop();
413
- try {
414
- if (await select({
415
- message: "使用 v1/v2 版本?",
416
- default: "v2",
417
- choices: ["v1", "v2"]
418
- }) === "v1") {
419
- await generateV1();
420
- return;
421
- }
422
- await generate(package_default.version);
423
- } catch (error) {
424
- consola.error(error);
425
- }
426
- });
427
- program.parse();
428
- }
429
- main();
430
-
431
- //#endregion
432
- export { };
3
+ export { copyTemplate, generate, interaction, readPackageJSON, run };
package/package.json CHANGED
@@ -1,12 +1,21 @@
1
1
  {
2
2
  "name": "create-pubinfo",
3
3
  "type": "module",
4
- "version": "2.0.0",
4
+ "version": "2.0.2",
5
5
  "description": "初始化项目框架",
6
6
  "author": "Werheng <werheng.zhang@gmail.com>",
7
7
  "license": "MIT",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "main": "./dist/index.js",
15
+ "module": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
8
17
  "bin": {
9
- "pubinfo": "./dist/index.js"
18
+ "pubinfo": "./dist/cli.js"
10
19
  },
11
20
  "files": [
12
21
  "dist",
@@ -22,6 +31,7 @@
22
31
  "commander": "^14.0.0",
23
32
  "confbox": "^0.2.2",
24
33
  "consola": "^3.4.2",
34
+ "fs-extra": "^11.3.2",
25
35
  "giget": "^2.0.0",
26
36
  "node-plop": "0.32.1",
27
37
  "ofetch": "^1.4.1",
@@ -35,7 +45,7 @@
35
45
  "scripts": {
36
46
  "dev": "tsdown --watch src",
37
47
  "build": "tsdown",
38
- "cli": "node dist/index.js",
48
+ "cli": "node dist/cli.js",
39
49
  "cli:clean": "rimraf ./my-app",
40
50
  "lint": "eslint . --cache --fix"
41
51
  }