create-aomex 0.0.2 → 0.0.3

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/index.js CHANGED
@@ -1,66 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // src/input-package-manager.ts
4
- import inquirer from "inquirer";
5
- var packageManagers = ["pnpm", "npm", "yarn"];
6
- var inputPackageManager = async (argv2) => {
7
- for (const expected of packageManagers) {
8
- if (argv2[expected]) return expected;
9
- }
10
- const { manager } = await inquirer.prompt({
11
- name: "manager",
12
- message: "\u9009\u62E9\u5305\u7BA1\u7406\u5DE5\u5177",
13
- type: "list",
14
- choices: packageManagers,
15
- default: "pnpm",
16
- askAnswered: true
17
- });
18
- return manager;
19
- };
20
-
21
- // src/input-project-name.ts
22
- import inquirer2 from "inquirer";
23
- import kebabCase from "lodash.kebabcase";
24
- import { existsSync } from "node:fs";
25
- import path from "node:path";
26
- var inputProjectName = async (argv2) => {
27
- if (typeof argv2["name"] === "string") return argv2["name"];
28
- const result = await inquirer2.prompt({
29
- name: "projectName",
30
- type: "input",
31
- message: "\u9879\u76EE\u540D\u79F0",
32
- suffix: ":"
33
- });
34
- const projectName2 = kebabCase(result.projectName);
35
- const absolutePath = path.posix.resolve(projectName2);
36
- if (existsSync(absolutePath) && argv2["force"] !== true) {
37
- const { goon } = await inquirer2.prompt({
38
- name: "goon",
39
- type: "confirm",
40
- message: `\u76EE\u5F55 "${absolutePath}" \u5DF2\u7ECF\u5B58\u5728\uFF0C\u662F\u5426\u8986\u76D6\uFF1F`,
41
- suffix: ":"
42
- });
43
- if (!goon) {
44
- process.exit(0);
45
- }
46
- }
47
- return projectName2;
48
- };
49
-
50
3
  // src/index.ts
51
- import path3 from "node:path/posix";
4
+ import path2 from "node:path/posix";
52
5
  import yargsParser from "yargs-parser";
53
- import { existsSync as existsSync2 } from "node:fs";
6
+ import { existsSync } from "node:fs";
54
7
  import { cp, mkdir, rm } from "node:fs/promises";
55
8
  import { Listr } from "listr2";
56
- import { promisify, styleText } from "node:util";
57
- import childProcess from "node:child_process";
9
+ import { styleText } from "node:util";
10
+ import { execSync, spawn } from "node:child_process";
58
11
 
59
12
  // src/replace-variables.ts
60
13
  import { readFile, writeFile } from "node:fs/promises";
61
- import path2 from "node:path";
14
+ import path from "node:path";
62
15
  var replaceVariables = async (filename, variables) => {
63
- const packageJSONFile = path2.resolve(filename);
16
+ const packageJSONFile = path.resolve(filename);
64
17
  let packageContent = await readFile(packageJSONFile, "utf8");
65
18
  Object.entries(variables).forEach(([key, value]) => {
66
19
  packageContent = packageContent.replaceAll(`{{${key}}}`, value);
@@ -69,28 +22,49 @@ var replaceVariables = async (filename, variables) => {
69
22
  };
70
23
 
71
24
  // src/index.ts
25
+ import kebabCase from "lodash.kebabcase";
72
26
  var argv = yargsParser(process.argv.slice(2));
73
- var projectName = await inputProjectName(argv);
74
- var packageManager = await inputPackageManager(argv);
75
- var templateDir = path3.join(import.meta.dirname, "..", "templates");
76
- var targetDir = path3.resolve(projectName);
77
- var exec = promisify(childProcess.exec);
27
+ var templateDir = path2.join(import.meta.dirname, "..", "templates");
28
+ var projectName = kebabCase(argv["project"] || "my-aomex-project");
29
+ var targetDir = path2.resolve(projectName);
30
+ if (existsSync(targetDir)) {
31
+ console.error(styleText("red", `\u76EE\u5F55 "${targetDir}" \u5DF2\u5B58\u5728\uFF01`));
32
+ process.exit(1);
33
+ }
34
+ var packageManager = "npm";
35
+ for (const item of ["pnpm", "npm", "yarn"]) {
36
+ if (argv[item]) {
37
+ packageManager = item;
38
+ break;
39
+ }
40
+ }
41
+ var runShell = async (command) => {
42
+ await new Promise((resolve, reject) => {
43
+ const stream = spawn(command, {
44
+ cwd: process.cwd(),
45
+ env: process.env,
46
+ shell: true
47
+ });
48
+ stream.on("close", resolve);
49
+ stream.on("error", reject);
50
+ });
51
+ };
78
52
  var sleep = () => new Promise((resolve) => setTimeout(resolve, 500));
79
53
  var spinner = new Listr([]);
80
54
  spinner.add({
81
55
  title: "\u521B\u5EFA\u76EE\u5F55",
82
56
  task: async () => {
83
- if (existsSync2(targetDir)) {
57
+ if (existsSync(targetDir)) {
84
58
  await rm(targetDir, { recursive: true, force: true });
85
59
  }
86
60
  await mkdir(targetDir, { recursive: true });
87
- process.chdir(path3.resolve(projectName));
61
+ process.chdir(path2.resolve(projectName));
88
62
  }
89
63
  });
90
64
  spinner.add({
91
65
  title: "git\u521D\u59CB\u5316",
92
66
  task: async () => {
93
- await exec("git init");
67
+ await runShell("git init");
94
68
  await sleep();
95
69
  }
96
70
  });
@@ -106,12 +80,12 @@ spinner.add({
106
80
  spinner.add({
107
81
  title: "\u589E\u52A0volta\u914D\u7F6E",
108
82
  skip: async () => {
109
- return !/\d\.\d/.test((await exec("volta -v")).stdout);
83
+ return !/\d\.\d/.test(execSync("volta -v", { encoding: "utf8" }));
110
84
  },
111
85
  task: async () => {
112
- await exec("volta pin node");
86
+ await runShell("volta pin node");
113
87
  if (packageManager !== "npm") {
114
- await exec(`volta pin ${packageManager}`);
88
+ await runShell(`volta pin ${packageManager}`);
115
89
  }
116
90
  }
117
91
  });
@@ -119,53 +93,70 @@ spinner.add({
119
93
  title: "\u5B89\u88C5\u63D2\u4EF6",
120
94
  task: async (_, task) => {
121
95
  const packages = [
122
- { pkg: "@aomex/core" },
123
- { pkg: "@aomex/web" },
124
- { pkg: "@aomex/console" },
125
- { pkg: "@aomex/router" },
126
- { pkg: "@aomex/cors" },
127
- { pkg: "@aomex/etag" },
128
- { pkg: "@aomex/compress" },
129
- { pkg: "@aomex/http-logger" },
130
- { pkg: "@aomex/commander" },
131
- { pkg: "@aomex/openapi" },
132
- { pkg: "@prisma/client" },
133
- { pkg: "prisma", dev: true },
134
- { pkg: "typescript", dev: true },
135
- { pkg: "@types/node", dev: true },
136
- { pkg: "tsx", dev: true },
137
- { pkg: "husky", dev: true },
138
- { pkg: "tsc-alias", dev: true },
139
- { pkg: "prettier", dev: true },
140
- { pkg: "@commitlint/cli", dev: true },
141
- { pkg: "@commitlint/config-conventional", dev: true },
142
- { pkg: "eslint", dev: true },
143
- { pkg: "@typescript-eslint/eslint-plugin", dev: true },
144
- { pkg: "@typescript-eslint/parser", dev: true },
145
- { pkg: "eslint-plugin-check-file", dev: true }
96
+ {
97
+ label: "dependencies",
98
+ pkgs: [
99
+ "@aomex/core",
100
+ "@aomex/web",
101
+ "@aomex/router",
102
+ "@aomex/cors",
103
+ "@aomex/etag",
104
+ "@aomex/compress",
105
+ "@aomex/http-logger",
106
+ "@aomex/response-time",
107
+ "@aomex/console",
108
+ "@aomex/commander",
109
+ "@aomex/cron",
110
+ "@aomex/helmet",
111
+ "@aomex/openapi",
112
+ "@prisma/client",
113
+ // prisma不会直接导入代码,但是需要在生产环境中生成代码。
114
+ // 导致devDependencies被忽略的场景:
115
+ // 1. pnpm install --production
116
+ // 2. NODE_ENV='production'
117
+ "prisma"
118
+ ]
119
+ },
120
+ {
121
+ label: "dev dependencies",
122
+ pkgs: [
123
+ "typescript",
124
+ "tsx",
125
+ "tsc-alias",
126
+ "@types/node",
127
+ "husky",
128
+ "prettier",
129
+ "@commitlint/cli",
130
+ "@commitlint/config-conventional",
131
+ "eslint",
132
+ "@typescript-eslint/eslint-plugin",
133
+ "@typescript-eslint/parser",
134
+ "eslint-plugin-check-file"
135
+ ],
136
+ dev: true
137
+ }
146
138
  ];
147
139
  const action = packageManager === "npm" ? "install" : "add";
148
140
  const devSuffix = packageManager === "npm" ? "--save-dev" : "-D";
149
141
  for (let i = 0; i < packages.length; ++i) {
150
- const { pkg, dev } = packages[i];
151
- task.title = "\u5B89\u88C5\u63D2\u4EF6 " + styleText("gray", pkg);
152
- await exec(`${packageManager} ${action} ${pkg} ${dev ? devSuffix : ""}`, {
153
- cwd: process.cwd(),
154
- env: process.env
155
- });
142
+ const { pkgs, dev, label } = packages[i];
143
+ task.title = "\u5B89\u88C5\u63D2\u4EF6 " + styleText("gray", label);
144
+ await runShell(
145
+ `${packageManager} ${action} ${pkgs.join(" ")} ${dev ? devSuffix : ""}`
146
+ );
156
147
  }
157
148
  task.title = "\u5B89\u88C5\u63D2\u4EF6";
158
149
  }
159
150
  });
160
151
  spinner.add({
161
- title: "\u751F\u6210prisma/client\u6587\u4EF6",
152
+ title: "\u751F\u6210prisma\u5BA2\u6237\u7AEF",
162
153
  task: async () => {
163
- await exec("npx prisma generate");
154
+ await runShell("npx prisma generate");
164
155
  await sleep();
165
156
  }
166
157
  });
167
158
  await spinner.run();
168
159
  console.log(
169
- "\n\u9879\u76EE\u521B\u5EFA\u6210\u529F\uFF1A" + styleText(["blue", "underline"], process.cwd()) + "\n"
160
+ "\n\u9879\u76EE\u521B\u5EFA\u6210\u529F\uFF1A" + styleText(["blue", "underline"], process.cwd()) + "\n\u542F\u52A8\u9879\u76EE\u53EF\u6267\u884C\u5982\u4E0B\u6307\u4EE4\uFF1A\n\n" + styleText("green", `cd ${projectName} && ${packageManager} start`) + "\n"
170
161
  );
171
162
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/input-package-manager.ts","../src/input-project-name.ts","../src/index.ts","../src/replace-variables.ts"],"sourcesContent":["import inquirer from 'inquirer';\n\nconst packageManagers = <const>['pnpm', 'npm', 'yarn'];\n\nexport type PackageManager = (typeof packageManagers)[number];\n\nexport const inputPackageManager = async (\n argv: Record<string, any>,\n): Promise<PackageManager> => {\n for (const expected of packageManagers) {\n if (argv[expected]) return expected;\n }\n\n const { manager } = await inquirer.prompt<{\n manager: PackageManager;\n }>({\n name: 'manager',\n message: '选择包管理工具',\n type: 'list',\n choices: packageManagers,\n default: 'pnpm',\n askAnswered: true,\n });\n\n return manager;\n};\n","import inquirer from 'inquirer';\nimport kebabCase from 'lodash.kebabcase';\nimport { existsSync } from 'node:fs';\nimport path from 'node:path';\n\nexport const inputProjectName = async (\n argv: Record<string, any>,\n): Promise<string> => {\n if (typeof argv['name'] === 'string') return argv['name'];\n\n const result = await inquirer.prompt<{ projectName: string }>({\n name: 'projectName',\n type: 'input',\n message: '项目名称',\n suffix: ':',\n });\n\n const projectName = kebabCase(result.projectName);\n\n const absolutePath = path.posix.resolve(projectName);\n if (existsSync(absolutePath) && argv['force'] !== true) {\n const { goon } = await inquirer.prompt<{ goon: boolean }>({\n name: 'goon',\n type: 'confirm',\n message: `目录 \"${absolutePath}\" 已经存在,是否覆盖?`,\n suffix: ':',\n });\n\n if (!goon) {\n process.exit(0);\n }\n }\n\n return projectName;\n};\n","#!/usr/bin/env node\n\nimport { inputPackageManager } from './input-package-manager';\nimport { inputProjectName } from './input-project-name';\nimport path from 'node:path/posix';\nimport yargsParser from 'yargs-parser';\nimport { existsSync } from 'node:fs';\nimport { cp, mkdir, rm } from 'node:fs/promises';\nimport { Listr } from 'listr2';\nimport { promisify, styleText } from 'node:util';\nimport childProcess from 'node:child_process';\nimport { replaceVariables } from './replace-variables';\n\nconst argv = yargsParser(process.argv.slice(2));\nconst projectName = await inputProjectName(argv);\nconst packageManager = await inputPackageManager(argv);\nconst templateDir = path.join(import.meta.dirname, '..', 'templates');\nconst targetDir = path.resolve(projectName);\n\nconst exec = promisify(childProcess.exec);\nconst sleep = () => new Promise((resolve) => setTimeout(resolve, 500));\n\nconst spinner = new Listr([]);\n\nspinner.add({\n title: '创建目录',\n task: async () => {\n if (existsSync(targetDir)) {\n await rm(targetDir, { recursive: true, force: true });\n }\n await mkdir(targetDir, { recursive: true });\n process.chdir(path.resolve(projectName));\n },\n});\n\nspinner.add({\n title: 'git初始化',\n task: async () => {\n await exec('git init');\n await sleep();\n },\n});\n\nspinner.add({\n title: '复制模板文件',\n task: async () => {\n await cp(templateDir, targetDir, { recursive: true });\n await replaceVariables('package.json', { projectName, packageManager });\n await replaceVariables('README.md', { projectName, packageManager });\n await sleep();\n },\n});\n\nspinner.add({\n title: '增加volta配置',\n skip: async () => {\n return !/\\d\\.\\d/.test((await exec('volta -v')).stdout);\n },\n task: async () => {\n await exec('volta pin node');\n if (packageManager !== 'npm') {\n await exec(`volta pin ${packageManager}`);\n }\n },\n});\n\nspinner.add({\n title: '安装插件',\n task: async (_, task) => {\n const packages: { pkg: string; dev?: boolean }[] = [\n { pkg: '@aomex/core' },\n { pkg: '@aomex/web' },\n { pkg: '@aomex/console' },\n { pkg: '@aomex/router' },\n { pkg: '@aomex/cors' },\n { pkg: '@aomex/etag' },\n { pkg: '@aomex/compress' },\n { pkg: '@aomex/http-logger' },\n { pkg: '@aomex/commander' },\n { pkg: '@aomex/openapi' },\n { pkg: '@prisma/client' },\n { pkg: 'prisma', dev: true },\n { pkg: 'typescript', dev: true },\n { pkg: '@types/node', dev: true },\n { pkg: 'tsx', dev: true },\n { pkg: 'husky', dev: true },\n { pkg: 'tsc-alias', dev: true },\n { pkg: 'prettier', dev: true },\n { pkg: '@commitlint/cli', dev: true },\n { pkg: '@commitlint/config-conventional', dev: true },\n { pkg: 'eslint', dev: true },\n { pkg: '@typescript-eslint/eslint-plugin', dev: true },\n { pkg: '@typescript-eslint/parser', dev: true },\n { pkg: 'eslint-plugin-check-file', dev: true },\n ];\n const action = packageManager === 'npm' ? 'install' : 'add';\n const devSuffix = packageManager === 'npm' ? '--save-dev' : '-D';\n\n for (let i = 0; i < packages.length; ++i) {\n const { pkg, dev } = packages[i]!;\n task.title = '安装插件 ' + styleText('gray', pkg);\n await exec(`${packageManager} ${action} ${pkg} ${dev ? devSuffix : ''}`, {\n cwd: process.cwd(),\n env: process.env,\n });\n }\n task.title = '安装插件';\n },\n});\n\nspinner.add({\n title: '生成prisma/client文件',\n task: async () => {\n await exec('npx prisma generate');\n await sleep();\n },\n});\n\nawait spinner.run();\n\nconsole.log(\n '\\n项目创建成功:' + styleText(['blue', 'underline'], process.cwd()) + '\\n',\n);\n","import { readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\n\nexport const replaceVariables = async (\n filename: string,\n variables: Record<string, string>,\n) => {\n const packageJSONFile = path.resolve(filename);\n let packageContent = await readFile(packageJSONFile, 'utf8');\n Object.entries(variables).forEach(([key, value]) => {\n packageContent = packageContent.replaceAll(`{{${key}}}`, value);\n });\n await writeFile(packageJSONFile, packageContent);\n};\n"],"mappings":"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","names":["argv","inquirer","argv","projectName","path","existsSync","path","path","existsSync"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/replace-variables.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport path from 'node:path/posix';\nimport yargsParser from 'yargs-parser';\nimport { existsSync } from 'node:fs';\nimport { cp, mkdir, rm } from 'node:fs/promises';\nimport { Listr } from 'listr2';\nimport { styleText } from 'node:util';\nimport { execSync, spawn } from 'node:child_process';\nimport { replaceVariables } from './replace-variables';\nimport kebabCase from 'lodash.kebabcase';\n\nconst argv = yargsParser(process.argv.slice(2));\nconst templateDir = path.join(import.meta.dirname, '..', 'templates');\n\nconst projectName = kebabCase(argv['project'] || 'my-aomex-project');\nconst targetDir = path.resolve(projectName);\nif (existsSync(targetDir)) {\n console.error(styleText('red', `目录 \"${targetDir}\" 已存在!`));\n process.exit(1);\n}\n\nlet packageManager = 'npm';\nfor (const item of <const>['pnpm', 'npm', 'yarn']) {\n if (argv[item]) {\n packageManager = item;\n break;\n }\n}\n\nconst runShell = async (command: string) => {\n await new Promise((resolve, reject) => {\n const stream = spawn(command, {\n cwd: process.cwd(),\n env: process.env,\n shell: true,\n });\n stream.on('close', resolve);\n stream.on('error', reject);\n });\n};\nconst sleep = () => new Promise((resolve) => setTimeout(resolve, 500));\n\nconst spinner = new Listr([]);\n\nspinner.add({\n title: '创建目录',\n task: async () => {\n if (existsSync(targetDir)) {\n await rm(targetDir, { recursive: true, force: true });\n }\n await mkdir(targetDir, { recursive: true });\n process.chdir(path.resolve(projectName));\n },\n});\n\nspinner.add({\n title: 'git初始化',\n task: async () => {\n await runShell('git init');\n await sleep();\n },\n});\n\nspinner.add({\n title: '复制模板文件',\n task: async () => {\n await cp(templateDir, targetDir, { recursive: true });\n await replaceVariables('package.json', { projectName, packageManager });\n await replaceVariables('README.md', { projectName, packageManager });\n await sleep();\n },\n});\n\nspinner.add({\n title: '增加volta配置',\n skip: async () => {\n return !/\\d\\.\\d/.test(execSync('volta -v', { encoding: 'utf8' }));\n },\n task: async () => {\n await runShell('volta pin node');\n if (packageManager !== 'npm') {\n await runShell(`volta pin ${packageManager}`);\n }\n },\n});\n\nspinner.add({\n title: '安装插件',\n task: async (_, task) => {\n const packages: { label: string; pkgs: string[]; dev?: boolean }[] = [\n {\n label: 'dependencies',\n pkgs: [\n '@aomex/core',\n '@aomex/web',\n '@aomex/router',\n '@aomex/cors',\n '@aomex/etag',\n '@aomex/compress',\n '@aomex/http-logger',\n '@aomex/response-time',\n '@aomex/console',\n '@aomex/commander',\n '@aomex/cron',\n '@aomex/helmet',\n '@aomex/openapi',\n '@prisma/client',\n // prisma不会直接导入代码,但是需要在生产环境中生成代码。\n // 导致devDependencies被忽略的场景:\n // 1. pnpm install --production\n // 2. NODE_ENV='production'\n 'prisma',\n ],\n },\n {\n label: 'dev dependencies',\n pkgs: [\n 'typescript',\n 'tsx',\n 'tsc-alias',\n '@types/node',\n 'husky',\n 'prettier',\n '@commitlint/cli',\n '@commitlint/config-conventional',\n 'eslint',\n '@typescript-eslint/eslint-plugin',\n '@typescript-eslint/parser',\n 'eslint-plugin-check-file',\n ],\n dev: true,\n },\n ];\n const action = packageManager === 'npm' ? 'install' : 'add';\n const devSuffix = packageManager === 'npm' ? '--save-dev' : '-D';\n\n for (let i = 0; i < packages.length; ++i) {\n const { pkgs, dev, label } = packages[i]!;\n task.title = '安装插件 ' + styleText('gray', label);\n await runShell(\n `${packageManager} ${action} ${pkgs.join(' ')} ${dev ? devSuffix : ''}`,\n );\n }\n task.title = '安装插件';\n },\n});\n\nspinner.add({\n title: '生成prisma客户端',\n task: async () => {\n await runShell('npx prisma generate');\n await sleep();\n },\n});\n\nawait spinner.run();\n\nconsole.log(\n '\\n项目创建成功:' +\n styleText(['blue', 'underline'], process.cwd()) +\n '\\n' +\n '启动项目可执行如下指令:' +\n '\\n\\n' +\n styleText('green', `cd ${projectName} && ${packageManager} start`) +\n '\\n',\n);\n","import { readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\n\nexport const replaceVariables = async (\n filename: string,\n variables: Record<string, string>,\n) => {\n const packageJSONFile = path.resolve(filename);\n let packageContent = await readFile(packageJSONFile, 'utf8');\n Object.entries(variables).forEach(([key, value]) => {\n packageContent = packageContent.replaceAll(`{{${key}}}`, value);\n });\n await writeFile(packageJSONFile, packageContent);\n};\n"],"mappings":"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","names":["path","path"]}
package/package.json CHANGED
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "name": "create-aomex",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "repository": "git@github.com:aomex/create-aomex.git",
5
5
  "author": "geekact <fanwenhua1990@gmail.com>",
6
6
  "license": "MIT",
7
7
  "type": "module",
8
8
  "scripts": {
9
- "cli": "tsx src/index.ts",
9
+ "cli": "tsx src/index.ts --project=abc --pnpm",
10
10
  "release": "release-it --ci",
11
11
  "build": "tsup",
12
- "prepublishOnly": "pnpm build"
12
+ "prepublishOnly": "tsup"
13
13
  },
14
14
  "volta": {
15
15
  "node": "20.15.0",
16
16
  "pnpm": "9.4.0"
17
17
  },
18
18
  "engines": {
19
- "node": ">=20"
19
+ "node": ">=20.13.0"
20
20
  },
21
21
  "publishConfig": {
22
22
  "access": "public"
@@ -37,17 +37,19 @@
37
37
  "yargs-parser": "^21.1.1"
38
38
  },
39
39
  "devDependencies": {
40
- "@aomex/commander": "^1.0.4",
41
- "@aomex/compress": "^1.0.4",
42
- "@aomex/console": "^1.0.4",
43
- "@aomex/core": "^1.0.4",
44
- "@aomex/cors": "^1.0.4",
45
- "@aomex/etag": "^1.0.4",
46
- "@aomex/helmet": "^1.0.4",
47
- "@aomex/http-logger": "^1.0.4",
48
- "@aomex/openapi": "^1.0.4",
49
- "@aomex/router": "^1.0.4",
50
- "@aomex/web": "^1.0.4",
40
+ "@aomex/commander": "^1.0.5",
41
+ "@aomex/compress": "^1.0.5",
42
+ "@aomex/console": "^1.0.5",
43
+ "@aomex/core": "^1.0.5",
44
+ "@aomex/cors": "^1.0.5",
45
+ "@aomex/cron": "^1.3.0",
46
+ "@aomex/etag": "^1.0.5",
47
+ "@aomex/helmet": "^1.0.5",
48
+ "@aomex/http-logger": "^1.0.5",
49
+ "@aomex/openapi": "^1.0.5",
50
+ "@aomex/response-time": "^1.3.0",
51
+ "@aomex/router": "^1.0.5",
52
+ "@aomex/web": "^1.0.5",
51
53
  "@prisma/client": "^5.16.0",
52
54
  "@types/inquirer": "^9.0.7",
53
55
  "@types/lodash.kebabcase": "^4.1.9",
package/templates/.env CHANGED
@@ -1 +1 @@
1
- DATABASE_URL="mysql://USERNAME:PASSWORD@HOST:PORT/DB_NAME"
1
+ DATABASE_URL="mysql://root:123456@localhost:3306/demo_db"
@@ -15,12 +15,3 @@ model User {
15
15
  created_at DateTime @default(now()) @db.DateTime(0)
16
16
  updated_at DateTime @default(now()) @updatedAt @db.DateTime(0)
17
17
  }
18
-
19
- model Post {
20
- id Int @id @default(autoincrement())
21
- user_id Int
22
- title String
23
- content String @db.Text()
24
- created_at DateTime @default(now()) @db.DateTime(0)
25
- updated_at DateTime @default(now()) @updatedAt @db.DateTime(0)
26
- }
@@ -1,10 +1,36 @@
1
1
  import { ConsoleApp } from '@aomex/console';
2
- import { appChain } from './middleware/console.chain';
2
+ import { commanders } from '@aomex/commander';
3
+ import { openapi } from '@aomex/openapi';
4
+ import { cron } from '@aomex/cron';
3
5
 
4
6
  const app = new ConsoleApp({
5
7
  locale: 'zh_CN',
6
- mount: appChain,
8
+ mount: [
9
+ openapi({
10
+ routers: './src/routers',
11
+ docs: {
12
+ servers: [{ url: 'http://localhost:3000', description: 'Local' }],
13
+ },
14
+ }),
15
+ cron({
16
+ path: './src/commanders',
17
+ // store: RedisCache,
18
+ }),
19
+ commanders('./src/commanders'),
20
+ ],
21
+ });
22
+
23
+ app.on('error', (err) => {
24
+ // 上报错误日志
25
+ app.log(err);
7
26
  });
8
27
 
9
28
  await app.run();
10
29
  process.exit(0);
30
+
31
+ declare module '@aomex/console' {
32
+ namespace ConsoleApp {
33
+ type T = ConsoleApp.Infer<typeof app>;
34
+ interface Props extends T {}
35
+ }
36
+ }
@@ -1,12 +1,25 @@
1
1
  import { Commander } from '@aomex/commander';
2
- import { commanderChain } from '../middleware/console.chain';
2
+ import { options } from '@aomex/console';
3
+ import { rule } from '@aomex/core';
3
4
 
4
- export const commander = new Commander({
5
- mount: commanderChain,
6
- });
5
+ export const commander = new Commander();
7
6
 
7
+ // npx aomex hello
8
+ // npx aomex hello --user World
9
+ // npx aomex hello -u aomex.js
8
10
  commander.create('hello', {
9
- async action(_ctx) {
10
- console.log('Hello World');
11
+ mount: [
12
+ options(
13
+ {
14
+ user: rule.string().default('World'),
15
+ },
16
+ {
17
+ user: ['u'],
18
+ },
19
+ ),
20
+ ],
21
+ action: async (ctx) => {
22
+ const { user } = ctx.options;
23
+ console.log(`Hello ${user}`);
11
24
  },
12
25
  });
@@ -0,0 +1,43 @@
1
+ import { Commander } from '@aomex/commander';
2
+ import { options } from '@aomex/console';
3
+ import { rule } from '@aomex/core';
4
+ import { schedule } from '@aomex/cron';
5
+
6
+ export const commander = new Commander();
7
+
8
+ // npx aomex cron:start
9
+ commander.create('schedule', {
10
+ mount: [
11
+ schedule({
12
+ second: '*/5',
13
+ }),
14
+ schedule({
15
+ second: '*/8',
16
+ args: ['--user', 'aomex.js'],
17
+ }),
18
+ options({
19
+ user: rule.string().default('World'),
20
+ }),
21
+ ],
22
+ action: async (ctx) => {
23
+ const { user } = ctx.options;
24
+ console.log(`Hello ${user}`);
25
+ await new Promise((resolve) => {
26
+ setTimeout(resolve, 2_000);
27
+ });
28
+ },
29
+ });
30
+
31
+ commander.create('long:schedule', {
32
+ mount: [
33
+ schedule({
34
+ second: '*/10',
35
+ overlap: true,
36
+ }),
37
+ ],
38
+ action: async () => {
39
+ await new Promise((resolve) => {
40
+ setTimeout(resolve, 15_000);
41
+ });
42
+ },
43
+ });
@@ -0,0 +1,9 @@
1
+ import { Router } from '@aomex/router';
2
+
3
+ export const router = new Router();
4
+
5
+ router.get('/', {
6
+ action: (ctx) => {
7
+ ctx.send('hello world');
8
+ },
9
+ });
@@ -1,18 +1,45 @@
1
1
  import { Router } from '@aomex/router';
2
- import { routerChain } from '../middleware/web.chain';
2
+ import { services } from '../services';
3
+ import { body, params } from '@aomex/web';
4
+ import { rule } from '@aomex/core';
3
5
 
4
6
  export const router = new Router({
5
- mount: routerChain,
7
+ prefix: '/users',
6
8
  });
7
9
 
8
10
  router.get('/', {
9
- async action(ctx) {
10
- ctx.send('hello world');
11
+ action: async (ctx) => {
12
+ const users = await services.user.findAll();
13
+ ctx.send(200, users);
14
+ },
15
+ });
16
+
17
+ router.get('/:id', {
18
+ mount: [
19
+ params({
20
+ id: rule.int().min(1),
21
+ }),
22
+ ],
23
+ action: async (ctx) => {
24
+ const { id } = ctx.params;
25
+ const user = await services.user.findById(id);
26
+ if (!user) {
27
+ ctx.throw(404, 'user not found');
28
+ }
29
+ ctx.send(user);
11
30
  },
12
31
  });
13
32
 
14
33
  router.post('/', {
15
- async action(ctx) {
16
- ctx.send(201, { id: 1 });
34
+ mount: [
35
+ body({
36
+ name: rule.string(),
37
+ age: rule.number(),
38
+ }),
39
+ ],
40
+ action: async (ctx) => {
41
+ const { name, age } = ctx.body;
42
+ await services.user.createUser(name, age);
43
+ ctx.send(201);
17
44
  },
18
45
  });
@@ -1,7 +1,7 @@
1
1
  import { combineServices } from '@aomex/core';
2
2
  import { UserService } from './user.service';
3
3
 
4
- export const services = combineServices({
4
+ export const services = await combineServices({
5
5
  user: UserService,
6
6
  });
7
7
 
@@ -1,12 +1,28 @@
1
1
  import { Service } from '@aomex/core';
2
2
  import { db } from './db';
3
+ import { Prisma } from '@prisma/client';
3
4
 
4
5
  export class UserService extends Service {
5
6
  findAll() {
6
- return db.user.findAll();
7
+ return db.user.findMany({
8
+ orderBy: [
9
+ {
10
+ id: Prisma.SortOrder.desc,
11
+ },
12
+ ],
13
+ });
7
14
  }
8
15
 
9
16
  findById(userId: number) {
10
17
  return db.user.findUnique({ where: { id: userId } });
11
18
  }
19
+
20
+ async createUser(name: string, age: number) {
21
+ await db.user.create({
22
+ data: {
23
+ name,
24
+ age,
25
+ },
26
+ });
27
+ }
12
28
  }
@@ -1,11 +1,42 @@
1
1
  import { WebApp } from '@aomex/web';
2
- import { appChain } from './middleware/web.chain';
2
+ import { cors } from '@aomex/cors';
3
+ import { compress } from '@aomex/compress';
4
+ import { httpLogger } from '@aomex/http-logger';
5
+ import { etag } from '@aomex/etag';
6
+ import { routers } from '@aomex/router';
7
+ import { helmet } from '@aomex/helmet';
8
+ import { responseTime } from '@aomex/response-time';
3
9
 
4
10
  export const app = new WebApp({
5
11
  locale: 'zh_CN',
6
- mount: appChain,
12
+ mount: [
13
+ responseTime,
14
+ cors(),
15
+ compress(),
16
+ httpLogger(),
17
+ etag(),
18
+ helmet(),
19
+ routers('./src/routers'),
20
+ ],
7
21
  });
8
22
 
9
- app.listen(process.env['PORT'] || 3000, () => {
10
- console.log('服务已启动');
23
+ app.on('error', (err, ctx) => {
24
+ // 上报错误日志
25
+ app.log(err, ctx);
26
+ ctx.response.body = {
27
+ status: ctx.response.statusCode,
28
+ message: ctx.response.body,
29
+ };
11
30
  });
31
+
32
+ const port = process.env['PORT'] || 3000;
33
+ app.listen(port, () => {
34
+ console.log(`服务已启动,点击 http://localhost:${port} 访问`);
35
+ });
36
+
37
+ declare module '@aomex/web' {
38
+ namespace WebApp {
39
+ type T = WebApp.Infer<typeof app>;
40
+ interface Props extends T {}
41
+ }
42
+ }
@@ -22,9 +22,10 @@
22
22
  "allowJs": false,
23
23
  "checkJs": false,
24
24
  "declaration": false,
25
- "removeComments": false,
25
+ "removeComments": true,
26
26
  "rootDir": "./",
27
- "noEmit": true,
27
+ "outDir": "build",
28
+ "noEmit": false,
28
29
  "importHelpers": false,
29
30
  "downlevelIteration": true,
30
31
  "noImplicitAny": true,
@@ -1,9 +0,0 @@
1
- import { mdchain } from '@aomex/core';
2
- import { openapiGenerator } from './openapi.md';
3
- import { commanders } from '@aomex/commander';
4
-
5
- export const appChain = mdchain.console
6
- .mount(openapiGenerator)
7
- .mount(commanders('./src/commanders'));
8
-
9
- export const commanderChain = appChain.mount(null);
@@ -1,13 +0,0 @@
1
- import { openapi } from '@aomex/openapi';
2
-
3
- export const openapiGenerator = openapi({
4
- routers: ['./src/routers'],
5
- docs: {
6
- servers: [
7
- {
8
- url: 'http://localhost:3000',
9
- description: 'Local',
10
- },
11
- ],
12
- },
13
- });
@@ -1,17 +0,0 @@
1
- import { mdchain } from '@aomex/core';
2
- import { cors } from '@aomex/cors';
3
- import { compress } from '@aomex/compress';
4
- import { routers } from '@aomex/router';
5
- import { httpLogger } from '@aomex/http-logger';
6
- import { helmet } from '@aomex/helmet';
7
- import { etag } from '@aomex/etag';
8
-
9
- export const appChain = mdchain.web
10
- .mount(cors())
11
- .mount(compress())
12
- .mount(httpLogger())
13
- .mount(etag())
14
- .mount(helmet())
15
- .mount(routers('./src/routers'));
16
-
17
- export const routerChain = appChain;