@jayfong/x-server 1.8.0

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.
Files changed (68) hide show
  1. package/CHANGELOG.md +154 -0
  2. package/README.md +51 -0
  3. package/lib/cli/api_generator.d.ts +44 -0
  4. package/lib/cli/api_generator.js +339 -0
  5. package/lib/cli/build_util.d.ts +9 -0
  6. package/lib/cli/build_util.js +141 -0
  7. package/lib/cli/cli.d.ts +2 -0
  8. package/lib/cli/cli.js +216 -0
  9. package/lib/cli/deploy_util.d.ts +10 -0
  10. package/lib/cli/deploy_util.js +51 -0
  11. package/lib/cli/env_util.d.ts +29 -0
  12. package/lib/cli/env_util.js +139 -0
  13. package/lib/cli/register.d.ts +0 -0
  14. package/lib/cli/register.js +5 -0
  15. package/lib/cli/template_util.d.ts +3 -0
  16. package/lib/cli/template_util.js +18 -0
  17. package/lib/cli/templates/handlers.ts +3 -0
  18. package/lib/cli/templates/hooks.ts +2 -0
  19. package/lib/cli/templates/models.ts +22 -0
  20. package/lib/cli/templates/routes.ts +26 -0
  21. package/lib/cli/templates/tasks.ts +2 -0
  22. package/lib/core/define_bus.d.ts +38 -0
  23. package/lib/core/define_bus.js +15 -0
  24. package/lib/core/define_handler.d.ts +16 -0
  25. package/lib/core/define_handler.js +41 -0
  26. package/lib/core/define_hook.d.ts +2 -0
  27. package/lib/core/define_hook.js +8 -0
  28. package/lib/core/define_server.d.ts +3 -0
  29. package/lib/core/define_server.js +10 -0
  30. package/lib/core/define_task.d.ts +2 -0
  31. package/lib/core/define_task.js +27 -0
  32. package/lib/core/handler.d.ts +10 -0
  33. package/lib/core/handler.js +76 -0
  34. package/lib/core/http_error.d.ts +2 -0
  35. package/lib/core/http_error.js +8 -0
  36. package/lib/core/http_method.d.ts +3 -0
  37. package/lib/core/http_method.js +10 -0
  38. package/lib/core/server.d.ts +16 -0
  39. package/lib/core/server.js +137 -0
  40. package/lib/core/types.d.ts +123 -0
  41. package/lib/core/types.js +2 -0
  42. package/lib/index.d.ts +22 -0
  43. package/lib/index.js +41 -0
  44. package/lib/plugins/base.d.ts +4 -0
  45. package/lib/plugins/base.js +2 -0
  46. package/lib/plugins/cors.d.ts +21 -0
  47. package/lib/plugins/cors.js +39 -0
  48. package/lib/plugins/file_parser.d.ts +13 -0
  49. package/lib/plugins/file_parser.js +19 -0
  50. package/lib/plugins/ws_parser.d.ts +13 -0
  51. package/lib/plugins/ws_parser.js +19 -0
  52. package/lib/plugins/xml_parser.d.ts +19 -0
  53. package/lib/plugins/xml_parser.js +48 -0
  54. package/lib/services/base.d.ts +4 -0
  55. package/lib/services/base.js +2 -0
  56. package/lib/services/cache.d.ts +119 -0
  57. package/lib/services/cache.js +196 -0
  58. package/lib/services/captcha.d.ts +33 -0
  59. package/lib/services/captcha.js +34 -0
  60. package/lib/services/dispose.d.ts +17 -0
  61. package/lib/services/dispose.js +24 -0
  62. package/lib/services/jwt.d.ts +21 -0
  63. package/lib/services/jwt.js +50 -0
  64. package/lib/services/redis.d.ts +10 -0
  65. package/lib/services/redis.js +14 -0
  66. package/lib/x.d.ts +5 -0
  67. package/lib/x.js +11 -0
  68. package/package.json +86 -0
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.BuildUtil = void 0;
30
+ const esbuild = __importStar(require("esbuild"));
31
+ const compressing_1 = __importDefault(require("compressing"));
32
+ const execa_1 = __importDefault(require("execa"));
33
+ const fs_extra_1 = __importDefault(require("fs-extra"));
34
+ const node_path_1 = __importDefault(require("node:path"));
35
+ const vtils_1 = require("vtils");
36
+ class BuildUtil {
37
+ static async build(options) {
38
+ const mainFile = node_path_1.default.join(options.cwd, 'src/main.ts');
39
+ const pkgFile = node_path_1.default.join(options.cwd, 'package.json');
40
+ const pkgContent = await fs_extra_1.default.readJson(pkgFile);
41
+ const distDir = node_path_1.default.join(options.cwd, 'dist');
42
+ const tempDir = node_path_1.default.join(options.cwd, 'temp');
43
+ const distMainFile = node_path_1.default.join(distDir, 'main.js');
44
+ const distPkgFile = node_path_1.default.join(distDir, 'package.json');
45
+ const distPm2File = node_path_1.default.join(distDir, 'pm2.config.js');
46
+ const distBundleFile = node_path_1.default.join(tempDir, `app.${pkgContent.version}.tgz`);
47
+ await fs_extra_1.default.ensureDir(distDir);
48
+ await fs_extra_1.default.ensureDir(tempDir);
49
+ // 处理 external
50
+ const external = (0, vtils_1.uniq)(options.external || []);
51
+ const externalWithVersions = await Promise.all(external.map(async (item) => {
52
+ const itemPkgFile = require.resolve(`${item}/package.json`);
53
+ const itemPkgContent = await fs_extra_1.default.readJson(itemPkgFile);
54
+ return {
55
+ name: item,
56
+ version: itemPkgContent.version,
57
+ };
58
+ }));
59
+ // 构建
60
+ await fs_extra_1.default.emptyDir(distDir);
61
+ await esbuild.build({
62
+ entryPoints: [mainFile],
63
+ bundle: true,
64
+ outfile: distMainFile,
65
+ platform: 'node',
66
+ target: `node${process.version.slice(1)}`,
67
+ external: external,
68
+ minify: true,
69
+ format: 'cjs',
70
+ sourcemap: false,
71
+ treeShaking: true,
72
+ banner: {
73
+ js: `${`;${options.inlineEnvs
74
+ ?.map(env => `process.env[${JSON.stringify(env.key)}]=${JSON.stringify(env.value)}`)
75
+ .join(';')}` || ''};`,
76
+ },
77
+ plugins: [
78
+ {
79
+ name: 'extract-assets',
80
+ setup(build) {
81
+ build.onLoad({ filter: /\/svg-captcha\/lib\/option-manager\.js$/ }, async (args) => {
82
+ let js = await fs_extra_1.default.readFile(args.path, 'utf-8');
83
+ js = js.replace('../fonts/Comismsh.ttf', './assets/svg-captcha/fonts/Comismsh.ttf');
84
+ await fs_extra_1.default.copy(node_path_1.default.dirname(require.resolve('svg-captcha/fonts/LICENSE.md')), node_path_1.default.join(distDir, 'assets/svg-captcha/fonts'));
85
+ return {
86
+ contents: js,
87
+ loader: 'js',
88
+ };
89
+ });
90
+ build.onLoad({ filter: /\/bull\/lib\/commands\/index\.js$/ }, async (args) => {
91
+ let js = await fs_extra_1.default.readFile(args.path, 'utf-8');
92
+ js = js.replace('loadScripts(__dirname)', 'loadScripts("./assets/bull/commands")');
93
+ await fs_extra_1.default.copy(node_path_1.default.dirname(require.resolve('bull/lib/commands/index.js')), node_path_1.default.join(distDir, 'assets/bull/commands'));
94
+ return {
95
+ contents: js,
96
+ loader: 'js',
97
+ };
98
+ });
99
+ },
100
+ },
101
+ ],
102
+ });
103
+ // 兼容 prisma
104
+ const prismaSchemaFile = node_path_1.default.join(options.cwd, 'src/db/schema.prisma');
105
+ if (await fs_extra_1.default.pathExists(prismaSchemaFile)) {
106
+ const distPrismaSchemaFile = node_path_1.default.join(distDir, 'schema.prisma');
107
+ await fs_extra_1.default.copyFile(prismaSchemaFile, distPrismaSchemaFile);
108
+ await fs_extra_1.default.copyFile(require.resolve('@prisma/engines/libquery_engine-rhel-openssl-1.0.x.so.node'), node_path_1.default.join(distDir, 'libquery_engine-rhel-openssl-1.0.x.so.node'));
109
+ }
110
+ // 写入 pkg
111
+ const deps = externalWithVersions.reduce((res, item) => {
112
+ res[item.name] = item.version;
113
+ return res;
114
+ }, {});
115
+ const distPkgContent = {
116
+ name: pkgContent.name,
117
+ version: pkgContent.version,
118
+ dependencies: deps,
119
+ };
120
+ await fs_extra_1.default.writeJSON(distPkgFile, distPkgContent);
121
+ // 写入 pm2 配置
122
+ await fs_extra_1.default.writeFile(distPm2File, (0, vtils_1.dedent) `
123
+ module.exports = {
124
+ apps: [
125
+ {
126
+ name: ${JSON.stringify(pkgContent.name)},
127
+ script: './main.js',
128
+ },
129
+ ],
130
+ }
131
+ `);
132
+ // 安装 external 依赖
133
+ await (0, execa_1.default)('tyn', {
134
+ cwd: distDir,
135
+ stdio: 'inherit',
136
+ });
137
+ // 打包为 tgz
138
+ await compressing_1.default.tgz.compressDir(distDir, distBundleFile);
139
+ }
140
+ }
141
+ exports.BuildUtil = BuildUtil;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/lib/cli/cli.js ADDED
@@ -0,0 +1,216 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const chokidar_1 = __importDefault(require("chokidar"));
8
+ const execa_1 = __importDefault(require("execa"));
9
+ const fs_extra_1 = __importDefault(require("fs-extra"));
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const yargs_1 = __importDefault(require("yargs"));
12
+ const api_generator_1 = require("./api_generator");
13
+ const build_util_1 = require("./build_util");
14
+ const vtils_1 = require("vtils");
15
+ const deploy_util_1 = require("./deploy_util");
16
+ const env_util_1 = require("./env_util");
17
+ const vscode_generate_index_standalone_1 = require("vscode-generate-index-standalone");
18
+ const template_util_1 = require("./template_util");
19
+ yargs_1.default
20
+ .command('dev', '开始开发', _ => _.positional('index', {
21
+ alias: 'i',
22
+ describe: '额外需要生成的索引文件',
23
+ type: 'string',
24
+ array: true,
25
+ }).positional('npc', {
26
+ describe: 'npc 地址及密钥,格式:地址@密钥',
27
+ type: 'string',
28
+ }), async (argv) => {
29
+ process.env.NODE_ENV = 'development';
30
+ const watcher = chokidar_1.default.watch(['src', '.env'], {
31
+ cwd: process.cwd(),
32
+ });
33
+ let lastRun;
34
+ const run = () => {
35
+ lastRun = (0, execa_1.default)('node', ['-r', require.resolve('./register'), 'src/main.ts'], {
36
+ cwd: process.cwd(),
37
+ stdio: 'inherit',
38
+ });
39
+ };
40
+ if (argv.npc) {
41
+ const [server, vkey] = argv.npc.split('@');
42
+ (0, execa_1.default)('docker', [
43
+ 'run',
44
+ '--rm',
45
+ 'ffdfgdfg/npc:v0.26.9',
46
+ `-server=${server}`,
47
+ `-vkey=${vkey}`,
48
+ '-type=tcp',
49
+ ], {
50
+ cwd: process.cwd(),
51
+ stdio: 'inherit',
52
+ });
53
+ }
54
+ await template_util_1.TemplateUtil.init(process.cwd());
55
+ watcher.on('all', (0, vtils_1.debounce)(async () => {
56
+ await env_util_1.EnvUtil.importFile({
57
+ cwd: process.cwd(),
58
+ file: '.env',
59
+ });
60
+ env_util_1.EnvUtil.outputTypes({
61
+ cwd: process.cwd(),
62
+ file: '.env',
63
+ outFile: 'env.d.ts',
64
+ });
65
+ (0, vscode_generate_index_standalone_1.generateManyIndex)({
66
+ cwd: process.cwd(),
67
+ patterns: [
68
+ 'src/**/index.ts',
69
+ 'node_modules/.x/*.ts',
70
+ ...(argv.index || []),
71
+ ],
72
+ replaceFile: true,
73
+ onSuccess: filePath => {
74
+ console.log(`✔️ 索引文件已更新: ${filePath}`);
75
+ },
76
+ });
77
+ if (lastRun) {
78
+ console.log('==== 重启服务中 ====');
79
+ lastRun.kill();
80
+ setTimeout(run, 0);
81
+ }
82
+ else {
83
+ console.log('==== 启动服务中 ====');
84
+ run();
85
+ }
86
+ }, 6));
87
+ })
88
+ .command('build', '构建', _ => _.positional('external', {
89
+ alias: 'e',
90
+ describe: '不应该被打的包',
91
+ type: 'string',
92
+ array: true,
93
+ }), async (argv) => {
94
+ process.env.NODE_ENV = 'production';
95
+ const envs = await env_util_1.EnvUtil.parseFile({
96
+ cwd: process.cwd(),
97
+ file: '.env',
98
+ });
99
+ envs.unshift({
100
+ key: 'NODE_ENV',
101
+ value: 'production',
102
+ comment: '',
103
+ });
104
+ await template_util_1.TemplateUtil.init(process.cwd());
105
+ await (0, vscode_generate_index_standalone_1.generateManyIndex)({
106
+ cwd: process.cwd(),
107
+ patterns: ['src/.x/*.ts'],
108
+ replaceFile: true,
109
+ onSuccess: filePath => {
110
+ console.log(`✔️ 索引文件已更新: ${filePath}`);
111
+ },
112
+ });
113
+ await build_util_1.BuildUtil.build({
114
+ cwd: process.cwd(),
115
+ external: argv.external,
116
+ inlineEnvs: envs,
117
+ });
118
+ console.log('构建成功');
119
+ })
120
+ .command('api', '生成 API', async () => {
121
+ await new api_generator_1.ApiGenerator().generate();
122
+ })
123
+ .command('prisma', 'prisma 代理,主要为了注入环境变量', _ => _.positional('production', {
124
+ alias: 'p',
125
+ describe: '是否生产模式',
126
+ type: 'boolean',
127
+ default: false,
128
+ }), async (argv) => {
129
+ process.env.NODE_ENV = argv.production ? 'production' : 'development';
130
+ // https://www.prisma.io/docs/reference/api-reference/environment-variables-reference#prisma_cli_binary_targets
131
+ // https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#binarytargets-options
132
+ process.env.PRISMA_CLI_BINARY_TARGETS = 'darwin,rhel-openssl-1.0.x';
133
+ await env_util_1.EnvUtil.importFile({
134
+ cwd: process.cwd(),
135
+ file: '.env',
136
+ });
137
+ process.env.DATABASE_URL =
138
+ process.env.DATABASE_ACTION_URL || process.env.DATABASE_URL;
139
+ await (0, execa_1.default)('tnpx', [
140
+ 'prisma',
141
+ ...argv._.slice(1),
142
+ '--schema',
143
+ 'src/db/schema.prisma',
144
+ ], {
145
+ cwd: process.cwd(),
146
+ stdio: 'inherit',
147
+ });
148
+ if (!argv.production) {
149
+ const modelsDir = node_path_1.default.join(process.cwd(), 'src/models');
150
+ const baseModelFile = node_path_1.default.join(modelsDir, '_.ts');
151
+ await (0, vscode_generate_index_standalone_1.generateIndex)({
152
+ filePath: baseModelFile,
153
+ replaceFile: true,
154
+ });
155
+ const baseModelContent = await fs_extra_1.default.readFile(baseModelFile, 'utf-8');
156
+ const modelNames = [
157
+ ...baseModelContent.matchAll(/makeBaseModel\('(\w+?)'\)/g),
158
+ ].map(match => (0, vtils_1.snakeCase)(match[1]));
159
+ await Promise.all(modelNames.map(async (modelName) => {
160
+ const modelFile = node_path_1.default.join(modelsDir, `${modelName}.ts`);
161
+ if (!(await fs_extra_1.default.pathExists(modelFile))) {
162
+ const ModelName = (0, vtils_1.pascalCase)(modelName);
163
+ await fs_extra_1.default.writeFile(modelFile, (0, vtils_1.dedent) `
164
+ import { ${ModelName}BaseModel } from './_'
165
+
166
+ export class ${ModelName}Model extends ${ModelName}BaseModel {}
167
+
168
+ export const ${(0, vtils_1.camelCase)(modelName)}Model = new ${ModelName}Model()
169
+ `);
170
+ }
171
+ }));
172
+ }
173
+ })
174
+ .command('run [script]', '执行脚本', _ => _.positional('script', {
175
+ describe: '脚本',
176
+ type: 'string',
177
+ }).positional('production', {
178
+ alias: 'p',
179
+ describe: '是否生产模式',
180
+ type: 'boolean',
181
+ default: false,
182
+ }), async (argv) => {
183
+ process.env.NODE_ENV = argv.production ? 'production' : 'development';
184
+ await env_util_1.EnvUtil.importFile({
185
+ cwd: process.cwd(),
186
+ file: '.env',
187
+ });
188
+ process.env.DATABASE_URL =
189
+ process.env.DATABASE_ACTION_URL || process.env.DATABASE_URL;
190
+ if (!argv.script) {
191
+ await (0, execa_1.default)('tnpx', ['select-run'], {
192
+ cwd: process.cwd(),
193
+ stdio: 'inherit',
194
+ });
195
+ }
196
+ else {
197
+ await (0, execa_1.default)('node', ['-r', require.resolve('./register'), argv.script], {
198
+ cwd: process.cwd(),
199
+ stdio: 'inherit',
200
+ });
201
+ }
202
+ })
203
+ .command('deploy', '部署', async () => {
204
+ await env_util_1.EnvUtil.importFile({
205
+ cwd: process.cwd(),
206
+ file: '.env.deploy',
207
+ });
208
+ await deploy_util_1.DeployUtil.deploy({
209
+ cwd: process.cwd(),
210
+ host: process.env.HOST,
211
+ user: process.env.USER,
212
+ key: process.env.KEY,
213
+ dir: process.env.DIR,
214
+ });
215
+ })
216
+ .parse();
@@ -0,0 +1,10 @@
1
+ export interface DeployOptions {
2
+ cwd: string;
3
+ host: string;
4
+ user: string;
5
+ key: string;
6
+ dir: string;
7
+ }
8
+ export declare class DeployUtil {
9
+ static deploy(options: DeployOptions): Promise<void>;
10
+ }
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DeployUtil = void 0;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const vtils_1 = require("vtils");
10
+ const node_ssh_1 = require("node-ssh");
11
+ class DeployUtil {
12
+ static async deploy(options) {
13
+ const pkgContent = await fs_extra_1.default.readJson(path_1.default.join(options.cwd, 'package.json'));
14
+ const appFile = path_1.default.join(options.cwd, `temp/app.${pkgContent.version}.tgz`);
15
+ if (!(await fs_extra_1.default.pathExists(appFile))) {
16
+ throw new Error('请先构建');
17
+ }
18
+ const ssh = new node_ssh_1.NodeSSH();
19
+ await ssh.connect({
20
+ host: options.host,
21
+ username: options.user,
22
+ privateKey: options.key,
23
+ });
24
+ const appName = pkgContent.name;
25
+ const appDir = `./${appName}`;
26
+ await ssh.execCommand((0, vtils_1.dedent) `
27
+ set -ex
28
+ mkdir -p app_files/${appName}
29
+ rm -rf ${appDir}
30
+ mkdir ${appDir}
31
+ `, {
32
+ cwd: options.dir,
33
+ onStdout: buf => console.log(buf.toString()),
34
+ onStderr: buf => console.log(buf.toString()),
35
+ });
36
+ const remoteAppFile = `${options.dir}/app_files/${appName}/${path_1.default.basename(appFile)}`;
37
+ await ssh.putFile(appFile, remoteAppFile);
38
+ await ssh.execCommand((0, vtils_1.dedent) `
39
+ set -ex
40
+ tar -zxvf ${remoteAppFile} -C ${appDir}
41
+ cd ${appDir}/dist
42
+ pm2 startOrReload pm2.config.js
43
+ `, {
44
+ cwd: options.cwd,
45
+ onStdout: buf => console.log(buf.toString()),
46
+ onStderr: buf => console.log(buf.toString()),
47
+ });
48
+ ssh.dispose();
49
+ }
50
+ }
51
+ exports.DeployUtil = DeployUtil;
@@ -0,0 +1,29 @@
1
+ /// <reference types="node" />
2
+ export interface ParsedEnv {
3
+ comment: string;
4
+ key: string;
5
+ value: any;
6
+ }
7
+ export declare class EnvUtil {
8
+ static NEWLINE: string;
9
+ static RE_INI_KEY_VAL: RegExp;
10
+ static RE_NEWLINES: RegExp;
11
+ static NEWLINES_MATCH: RegExp;
12
+ static COMMENT_MATCH: RegExp;
13
+ static normalizeValue(value: string): any;
14
+ static parseContent(src: string | Buffer): ParsedEnv[];
15
+ static parseFile(options: {
16
+ cwd: string;
17
+ file: string;
18
+ }): Promise<ParsedEnv[]>;
19
+ static importFile(options: {
20
+ cwd: string;
21
+ file: string;
22
+ }): Promise<void>;
23
+ static makeTypes(envs: ParsedEnv[]): string;
24
+ static outputTypes(options: {
25
+ cwd: string;
26
+ file: string;
27
+ outFile: string;
28
+ }): Promise<void>;
29
+ }
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.EnvUtil = void 0;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const vtils_1 = require("vtils");
10
+ class EnvUtil {
11
+ static normalizeValue(value) {
12
+ if (value.includes(' || ')) {
13
+ const [devValue, prodValue] = value.split(/\s+\|\|\s+/);
14
+ return (0, vtils_1.devOrProd)(() => EnvUtil.normalizeValue(devValue), () => EnvUtil.normalizeValue(prodValue));
15
+ }
16
+ return value === 'true'
17
+ ? true
18
+ : value === 'false'
19
+ ? false
20
+ : (value.startsWith('[') && value.endsWith(']')) ||
21
+ (value.startsWith('{') && value.endsWith('}'))
22
+ ? JSON.parse(value)
23
+ : (0, vtils_1.isNumeric)(value)
24
+ ? Number(value)
25
+ : value;
26
+ }
27
+ static parseContent(src) {
28
+ const envs = [];
29
+ // https://github.com/andreialecu/dotenv/blob/feat-multiline/lib/main.js
30
+ const multilineLineBreaks = true;
31
+ // convert Buffers before splitting into lines and processing
32
+ const lines = src.toString().split(EnvUtil.NEWLINES_MATCH);
33
+ let lastComment = '';
34
+ for (let idx = 0; idx < lines.length; idx++) {
35
+ let line = lines[idx];
36
+ // matching "KEY' and 'VAL' in 'KEY=VAL'
37
+ const keyValueArr = line.match(EnvUtil.RE_INI_KEY_VAL);
38
+ // matched?
39
+ if (keyValueArr != null) {
40
+ const key = keyValueArr[1];
41
+ // default undefined or missing values to empty string
42
+ let val = keyValueArr[2] || '';
43
+ let end = val.length - 1;
44
+ const isDoubleQuoted = val[0] === '"' && val[end] === '"';
45
+ const isSingleQuoted = val[0] === "'" && val[end] === "'";
46
+ const isMultilineDoubleQuoted = val[0] === '"' && (val.length === 1 || val[end] !== '"');
47
+ const isMultilineSingleQuoted = val[0] === "'" && (val.length === 1 || val[end] !== "'");
48
+ // if parsing line breaks and the value starts with a quote
49
+ if (multilineLineBreaks &&
50
+ (isMultilineDoubleQuoted || isMultilineSingleQuoted)) {
51
+ const quoteChar = isMultilineDoubleQuoted ? '"' : "'";
52
+ val = val.substring(1);
53
+ while (idx++ < lines.length - 1) {
54
+ line = lines[idx];
55
+ end = line.length - 1;
56
+ if (line[end] === quoteChar) {
57
+ val += EnvUtil.NEWLINE + line.substring(0, end);
58
+ break;
59
+ }
60
+ val += EnvUtil.NEWLINE + line;
61
+ }
62
+ val = (0, vtils_1.dedent)(val);
63
+ // if single or double quoted, remove quotes
64
+ }
65
+ else if (isSingleQuoted || isDoubleQuoted) {
66
+ val = val.substring(1, end);
67
+ // if double quoted, expand newlines
68
+ if (isDoubleQuoted) {
69
+ val = val.replace(EnvUtil.RE_NEWLINES, EnvUtil.NEWLINE);
70
+ }
71
+ val = (0, vtils_1.dedent)(val);
72
+ }
73
+ else {
74
+ // remove surrounding whitespace
75
+ val = (0, vtils_1.dedent)(val).trim();
76
+ }
77
+ envs.push({
78
+ key: key,
79
+ value: EnvUtil.normalizeValue(val),
80
+ comment: lastComment,
81
+ });
82
+ lastComment = '';
83
+ }
84
+ else {
85
+ const commentArr = line.match(EnvUtil.COMMENT_MATCH);
86
+ if (commentArr) {
87
+ lastComment = commentArr[1];
88
+ }
89
+ }
90
+ }
91
+ return envs;
92
+ }
93
+ static async parseFile(options) {
94
+ const envFile = node_path_1.default.join(options.cwd, options.file);
95
+ if (await fs_extra_1.default.pathExists(envFile)) {
96
+ const envContent = await fs_extra_1.default.readFile(envFile, 'utf-8');
97
+ const envs = EnvUtil.parseContent(envContent);
98
+ return envs;
99
+ }
100
+ return [];
101
+ }
102
+ static async importFile(options) {
103
+ for (const env of await EnvUtil.parseFile(options)) {
104
+ process.env[env.key] = env.value;
105
+ }
106
+ }
107
+ static makeTypes(envs) {
108
+ return (0, vtils_1.dedent) `
109
+ declare namespace NodeJS {
110
+ interface ProcessEnv {
111
+ /** 当前环境 */
112
+ NODE_ENV: 'development' | 'production';
113
+ ${envs
114
+ .map(env => (0, vtils_1.dedent) `
115
+ ${env.comment ? `/** ${env.comment} */` : ''}
116
+ ${env.key}: ${
117
+ // TODO: 优化类型推断
118
+ Array.isArray(env.value)
119
+ ? `Array<${typeof env.value[0]}>`
120
+ : typeof env.value};
121
+ `)
122
+ .join('\n')}
123
+ }
124
+ }
125
+ `;
126
+ }
127
+ static async outputTypes(options) {
128
+ const envs = await EnvUtil.parseFile(options);
129
+ const outFile = node_path_1.default.join(options.cwd, options.outFile);
130
+ const types = EnvUtil.makeTypes(envs);
131
+ await fs_extra_1.default.outputFile(outFile, types);
132
+ }
133
+ }
134
+ exports.EnvUtil = EnvUtil;
135
+ EnvUtil.NEWLINE = '\n';
136
+ EnvUtil.RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*(.*)?\s*$/;
137
+ EnvUtil.RE_NEWLINES = /\\n/g;
138
+ EnvUtil.NEWLINES_MATCH = /\r\n|\n|\r/;
139
+ EnvUtil.COMMENT_MATCH = /^#\s*(.+?)\s*$/;
File without changes
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ require('esbuild-runner').install({
3
+ type: 'transform',
4
+ });
5
+ require('tsconfig-paths/register');
@@ -0,0 +1,3 @@
1
+ export declare class TemplateUtil {
2
+ static init(cwd: string): Promise<void>;
3
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.TemplateUtil = void 0;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ class TemplateUtil {
10
+ static async init(cwd) {
11
+ const fromDir = path_1.default.join(__dirname, 'templates');
12
+ const toDir = path_1.default.join(cwd, 'node_modules/.x');
13
+ await fs_extra_1.default.copy(fromDir, toDir, {
14
+ overwrite: true,
15
+ });
16
+ }
17
+ }
18
+ exports.TemplateUtil = TemplateUtil;
@@ -0,0 +1,3 @@
1
+ // @index('../../src/handlers/**/*.ts', (f, _) => `export * as __${_.pascal(f.path.replace('handlers', ''))}__ from '${f.path}'`)
2
+
3
+ // @endindex
@@ -0,0 +1,2 @@
1
+ // @index('../../src/hooks/**/*.ts', (f, _) => `import '${f.path}'`)
2
+ // @endindex
@@ -0,0 +1,22 @@
1
+ import { x, HttpError } from '@jayfong/x-server'
2
+ import { PrismaClient } from '@prisma/client'
3
+
4
+ const prisma = new PrismaClient({
5
+ rejectOnNotFound: err => new HttpError.NotFound(err.message),
6
+ })
7
+
8
+ x.dispose.add(() => prisma.$disconnect())
9
+
10
+ type ModelName =
11
+ // @index('../.prisma/client/index.d.ts', /(?<=const ModelName:).+?(?=\})/s, /(\S+?):/g, (m, _) => `| '${_.camel(m[1])}'`)
12
+ ''
13
+ // @endindex
14
+
15
+ function makeBaseModel<TModelName extends ModelName>(name: TModelName) {
16
+ return class BaseModel {
17
+ public query = prisma[name]
18
+ }
19
+ }
20
+
21
+ // @index('../.prisma/client/index.d.ts', /(?<=const ModelName:).+?(?=\})/s, /(\S+?):/g, (m, _) => `export const ${_.pascal(m[1])}BaseModel = makeBaseModel('${_.camel(m[1])}')`)
22
+ // @endindex