@jayfong/x-server 2.12.7 → 2.12.15
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/lib/_cjs/cli/api_generator.js +20 -54
- package/lib/_cjs/cli/build_util.js +16 -45
- package/lib/_cjs/cli/cli.js +6 -27
- package/lib/_cjs/cli/deploy_util.js +0 -12
- package/lib/_cjs/cli/env_util.js +16 -49
- package/lib/_cjs/cli/template_util.js +26 -29
- package/lib/_cjs/core/define_bus.js +9 -6
- package/lib/_cjs/core/define_cron.js +0 -2
- package/lib/_cjs/core/define_handler.js +10 -12
- package/lib/_cjs/core/define_hook.js +0 -2
- package/lib/_cjs/core/define_server.js +0 -2
- package/lib/_cjs/core/define_task.js +7 -13
- package/lib/_cjs/core/get_handler_url.js +1 -2
- package/lib/_cjs/core/handler.js +8 -29
- package/lib/_cjs/core/http_error.js +0 -3
- package/lib/_cjs/core/http_header.js +8 -12
- package/lib/_cjs/core/server.js +8 -41
- package/lib/_cjs/core/types.js +0 -7
- package/lib/_cjs/index.js +0 -72
- package/lib/_cjs/plugins/cors.js +0 -7
- package/lib/_cjs/plugins/file_parser.js +2 -9
- package/lib/_cjs/plugins/form_body_parser.js +0 -6
- package/lib/_cjs/plugins/ws_parser.js +0 -6
- package/lib/_cjs/plugins/xml_parser.js +0 -12
- package/lib/_cjs/services/cache.js +18 -58
- package/lib/_cjs/services/captcha.js +0 -14
- package/lib/_cjs/services/dingtalk.js +0 -14
- package/lib/_cjs/services/dispose.js +0 -9
- package/lib/_cjs/services/emoji.js +2 -5
- package/lib/_cjs/services/jwt.js +0 -21
- package/lib/_cjs/services/log.js +0 -17
- package/lib/_cjs/services/mail.js +0 -9
- package/lib/_cjs/services/pay.js +9 -40
- package/lib/_cjs/services/rate_limit.js +0 -12
- package/lib/_cjs/services/redis.js +0 -4
- package/lib/_cjs/services/request.js +14 -299
- package/lib/_cjs/services/sensitive_words.js +13 -19
- package/lib/_cjs/x.js +0 -11
- package/lib/cli/api_generator.js +20 -42
- package/lib/cli/build_util.js +16 -19
- package/lib/cli/cli.js +6 -12
- package/lib/cli/deploy_util.js +0 -4
- package/lib/cli/env_util.js +16 -37
- package/lib/cli/template_util.d.ts +1 -0
- package/lib/cli/template_util.js +26 -14
- package/lib/core/define_bus.js +7 -5
- package/lib/core/define_handler.js +10 -9
- package/lib/core/define_task.js +7 -6
- package/lib/core/get_handler_url.js +2 -1
- package/lib/core/handler.js +9 -22
- package/lib/core/http_header.js +6 -9
- package/lib/core/server.js +8 -31
- package/lib/core/types.js +0 -7
- package/lib/index.js +4 -3
- package/lib/plugins/cors.js +0 -3
- package/lib/plugins/file_parser.js +2 -5
- package/lib/plugins/form_body_parser.js +0 -3
- package/lib/plugins/ws_parser.js +0 -3
- package/lib/plugins/xml_parser.js +0 -10
- package/lib/services/cache.js +18 -52
- package/lib/services/captcha.js +0 -7
- package/lib/services/dingtalk.js +0 -7
- package/lib/services/dispose.js +0 -5
- package/lib/services/emoji.js +2 -3
- package/lib/services/jwt.js +0 -13
- package/lib/services/log.js +0 -8
- package/lib/services/mail.js +0 -3
- package/lib/services/pay.js +9 -30
- package/lib/services/rate_limit.js +0 -8
- package/lib/services/redis.js +0 -1
- package/lib/services/request.d.ts +3 -130
- package/lib/services/request.js +5 -274
- package/lib/services/sensitive_words.js +13 -15
- package/package.json +4 -28
- package/README.md +0 -51
- package/lib/_cjs/cli/register.js +0 -8
- package/lib/_cjs/client_helper.js +0 -26
- package/lib/_cjs/types/_formstream.js +0 -1
- package/lib/cli/register.d.ts +0 -1
- package/lib/cli/register.js +0 -5
- package/lib/client_helper.d.ts +0 -1
- package/lib/client_helper.js +0 -16
- package/lib/types/_formstream.d.ts +0 -49
- package/lib/types/_formstream.js +0 -0
package/lib/cli/api_generator.js
CHANGED
|
@@ -9,17 +9,15 @@ export class ApiGenerator {
|
|
|
9
9
|
constructor() {
|
|
10
10
|
this.debug = createDebug('api');
|
|
11
11
|
this.cwd = process.cwd();
|
|
12
|
+
// cwd = '/Users/admin/Documents/jfWorks/x-server-test'
|
|
12
13
|
this.project = void 0;
|
|
13
14
|
}
|
|
14
|
-
|
|
15
15
|
getTypeBySymbol(symbol) {
|
|
16
16
|
return symbol.getTypeAtLocation(symbol.getDeclarations()[0] || symbol.getValueDeclarationOrThrow());
|
|
17
17
|
}
|
|
18
|
-
|
|
19
18
|
getComment(declaration) {
|
|
20
19
|
var _declaration$getLeadi;
|
|
21
|
-
|
|
22
|
-
const text = ((declaration == null ? void 0 : (_declaration$getLeadi = declaration.getLeadingCommentRanges()[0]) == null ? void 0 : _declaration$getLeadi.getText()) || '').trim();
|
|
20
|
+
const text = ((declaration == null || (_declaration$getLeadi = declaration.getLeadingCommentRanges()[0]) == null ? void 0 : _declaration$getLeadi.getText()) || '').trim();
|
|
23
21
|
const comment = parseComment.parse(text)[0];
|
|
24
22
|
const description = (comment == null ? void 0 : comment.description) || '';
|
|
25
23
|
const tags = new Map();
|
|
@@ -32,16 +30,12 @@ export class ApiGenerator {
|
|
|
32
30
|
tags: tags
|
|
33
31
|
};
|
|
34
32
|
}
|
|
35
|
-
|
|
36
33
|
getCommentBySymbol(symbol) {
|
|
37
34
|
var _this$getComment;
|
|
38
|
-
|
|
39
35
|
return ((_this$getComment = this.getComment(symbol.getDeclarations()[0])) == null ? void 0 : _this$getComment.description) || '';
|
|
40
36
|
}
|
|
41
|
-
|
|
42
37
|
typeToApiData(type, _symbol) {
|
|
43
38
|
var _type$getSymbol, _type$getSymbol2, _type$getSymbol3;
|
|
44
|
-
|
|
45
39
|
// ws
|
|
46
40
|
if (((_type$getSymbol = type.getSymbol()) == null ? void 0 : _type$getSymbol.getName()) === 'SocketStream') {
|
|
47
41
|
return {
|
|
@@ -52,12 +46,11 @@ export class ApiGenerator {
|
|
|
52
46
|
children: [],
|
|
53
47
|
enum: []
|
|
54
48
|
};
|
|
55
|
-
}
|
|
56
|
-
|
|
49
|
+
}
|
|
57
50
|
|
|
51
|
+
// XFile
|
|
58
52
|
if (((_type$getSymbol2 = type.getSymbol()) == null ? void 0 : _type$getSymbol2.getName()) === 'MultipartFile') {
|
|
59
53
|
const symbol = _symbol || type.getSymbol();
|
|
60
|
-
|
|
61
54
|
return {
|
|
62
55
|
name: 'file',
|
|
63
56
|
desc: symbol && this.getCommentBySymbol(symbol) || '',
|
|
@@ -67,25 +60,22 @@ export class ApiGenerator {
|
|
|
67
60
|
enum: []
|
|
68
61
|
};
|
|
69
62
|
}
|
|
70
|
-
|
|
71
63
|
let isRequired = true;
|
|
72
64
|
let isUnion = type.isUnion();
|
|
73
65
|
const unionTypes = isUnion ? type.getUnionTypes().filter(item => !item.isBooleanLiteral() && !item.isNull() && !item.isUndefined()) : [];
|
|
74
66
|
isUnion = !!unionTypes.length;
|
|
75
|
-
|
|
76
67
|
if (isUnion) {
|
|
77
68
|
if (unionTypes.length === 1 && !unionTypes[0].isLiteral()) {
|
|
78
69
|
isUnion = false;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
70
|
+
}
|
|
71
|
+
// 兼容 prisma 生成的类型用 null 表示可选
|
|
72
|
+
isRequired = unionTypes.length === type.getUnionTypes().length;
|
|
73
|
+
// 必须用 getBaseTypeOfLiteralType 获取枚举字面量的原始类型
|
|
84
74
|
type = unionTypes[0].getBaseTypeOfLiteralType();
|
|
85
75
|
}
|
|
86
|
-
|
|
87
76
|
const isEnum = type.isEnum();
|
|
88
|
-
const enumData = isEnum ?
|
|
77
|
+
const enumData = isEnum ?
|
|
78
|
+
// @ts-ignore
|
|
89
79
|
type.compilerType.types.reduce((res, item) => {
|
|
90
80
|
res[item.getSymbol().getName()] = item.value;
|
|
91
81
|
return res;
|
|
@@ -98,11 +88,10 @@ export class ApiGenerator {
|
|
|
98
88
|
const isString = isEnum ? typeof enumValues[0] === 'string' : type.isString() || ['Date'].includes(((_type$getSymbol3 = type.getSymbol()) == null ? void 0 : _type$getSymbol3.getName()) || '');
|
|
99
89
|
const isNumber = isEnum ? typeof enumValues[0] === 'number' : type.isNumber();
|
|
100
90
|
const isBoolean = type.isBoolean() || type.isUnion() && type.getUnionTypes().some(item => item.isBooleanLiteral()) && type.getUnionTypes().every(item => item.isBooleanLiteral() || item.isNull() || item.isUndefined());
|
|
101
|
-
const isObject = !isArray && !isString && !isNumber && !isBoolean && type.isObject() ||
|
|
91
|
+
const isObject = !isArray && !isString && !isNumber && !isBoolean && type.isObject() ||
|
|
92
|
+
// 将交集类型视为对象
|
|
102
93
|
isIntersection;
|
|
103
|
-
|
|
104
94
|
const symbol = _symbol || type.getSymbol();
|
|
105
|
-
|
|
106
95
|
const apiName = (symbol == null ? void 0 : symbol.getName()) || '__type';
|
|
107
96
|
const apiDesc = [symbol && this.getCommentBySymbol(symbol), isEnum && `枚举:${enumKeys.map((key, index) => `${key}->${enumValues[index]}`).join('; ')}`].filter(Boolean).join('\n');
|
|
108
97
|
const apiEnum = isUnion ? unionTypes.map(t => t.getLiteralValue()) : isEnum ? enumValues : [];
|
|
@@ -113,20 +102,21 @@ export class ApiGenerator {
|
|
|
113
102
|
const compilerFactory = context.compilerFactory;
|
|
114
103
|
const rawChecker = type.compilerType.checker;
|
|
115
104
|
let symbols = [];
|
|
116
|
-
|
|
117
105
|
if (intersectionTypes) {
|
|
118
106
|
// https://github.com/microsoft/TypeScript/issues/38184
|
|
119
|
-
symbols = rawChecker.getAllPossiblePropertiesOfTypes(intersectionTypes.map(item => item.compilerType))
|
|
107
|
+
symbols = rawChecker.getAllPossiblePropertiesOfTypes(intersectionTypes.map(item => item.compilerType))
|
|
108
|
+
// https://github.com/dsherret/ts-morph/blob/a7072fcf6f9babb784b40f0326c80dea4563a4aa/packages/ts-morph/src/compiler/types/Type.ts#L296
|
|
120
109
|
.map(symbol => compilerFactory.getSymbol(symbol));
|
|
121
110
|
} else {
|
|
122
111
|
// symbols = type.getApparentProperties()
|
|
123
112
|
// https://github.com/microsoft/TypeScript/issues/38184
|
|
124
|
-
symbols = rawChecker.getAllPossiblePropertiesOfTypes([type.compilerType])
|
|
113
|
+
symbols = rawChecker.getAllPossiblePropertiesOfTypes([type.compilerType])
|
|
114
|
+
// https://github.com/dsherret/ts-morph/blob/a7072fcf6f9babb784b40f0326c80dea4563a4aa/packages/ts-morph/src/compiler/types/Type.ts#L296
|
|
125
115
|
.map(symbol => compilerFactory.getSymbol(symbol));
|
|
126
116
|
}
|
|
127
|
-
|
|
128
117
|
return symbols.map(symbol => {
|
|
129
|
-
return this.typeToApiData(!symbol.compilerSymbol.declarations ?
|
|
118
|
+
return this.typeToApiData(!symbol.compilerSymbol.declarations ?
|
|
119
|
+
// 对于复杂对象,没有定义的,通过 type 直接获取(在前面通过 getText 预处理得到)
|
|
130
120
|
compilerFactory.getType(symbol.compilerSymbol.type) : this.getTypeBySymbol(symbol), symbol);
|
|
131
121
|
});
|
|
132
122
|
}) : [];
|
|
@@ -139,10 +129,8 @@ export class ApiGenerator {
|
|
|
139
129
|
children: apiChildren
|
|
140
130
|
};
|
|
141
131
|
}
|
|
142
|
-
|
|
143
132
|
apiDataToJsonSchema(apiData, jsonSchema = {}) {
|
|
144
133
|
jsonSchema.description = apiData.desc;
|
|
145
|
-
|
|
146
134
|
if (apiData.type === 'object') {
|
|
147
135
|
jsonSchema.type = 'object';
|
|
148
136
|
jsonSchema.properties = apiData.children.reduce((res, item) => {
|
|
@@ -156,18 +144,14 @@ export class ApiGenerator {
|
|
|
156
144
|
jsonSchema.items = apiData.children.map(item => this.apiDataToJsonSchema(item))[0];
|
|
157
145
|
} else {
|
|
158
146
|
jsonSchema.type = apiData.type;
|
|
159
|
-
|
|
160
147
|
if (apiData.enum.length) {
|
|
161
148
|
jsonSchema.enum = apiData.enum;
|
|
162
149
|
}
|
|
163
150
|
}
|
|
164
|
-
|
|
165
151
|
return jsonSchema;
|
|
166
152
|
}
|
|
167
|
-
|
|
168
153
|
genYApiData(handles) {
|
|
169
154
|
const data = {};
|
|
170
|
-
|
|
171
155
|
for (const handle of handles) {
|
|
172
156
|
data[handle.category] = data[handle.category] || [];
|
|
173
157
|
data[handle.category].push(handle.handlerMethod === 'GET' ? {
|
|
@@ -221,14 +205,12 @@ export class ApiGenerator {
|
|
|
221
205
|
res_body: JSON.stringify(handle.responseDataJsonSchema)
|
|
222
206
|
});
|
|
223
207
|
}
|
|
224
|
-
|
|
225
208
|
return Object.keys(data).map(cat => ({
|
|
226
209
|
name: cat,
|
|
227
210
|
desc: cat,
|
|
228
211
|
list: data[cat]
|
|
229
212
|
}));
|
|
230
213
|
}
|
|
231
|
-
|
|
232
214
|
async generate() {
|
|
233
215
|
this.debug('启动项目...');
|
|
234
216
|
this.project = new ts.Project({
|
|
@@ -240,11 +222,9 @@ export class ApiGenerator {
|
|
|
240
222
|
const handlerGroup = sourceFile.getExportSymbols();
|
|
241
223
|
const handles = [];
|
|
242
224
|
this.debug('生成API文档...');
|
|
243
|
-
|
|
244
225
|
for (const handlerList of handlerGroup) {
|
|
245
226
|
const handlerListSourceFile = this.getTypeBySymbol(handlerList).getProperties()[0].getDeclarations()[0].getSourceFile();
|
|
246
227
|
const basePath = `/${handlerListSourceFile.getFilePath().replace('.ts', '').split('/src/handlers/')[1].replace(/(^|\/)index$/, '/').split('/').map(v => snakeCase(v)).join('/')}/`.replace(/\/{2,}/g, '/');
|
|
247
|
-
|
|
248
228
|
for (const handler of handlerListSourceFile.getVariableStatements().filter(item => item.isExported())) {
|
|
249
229
|
// 重要:这一步必须,先调一遍 getText 获取看到的对象,后续对于复杂定义才不会报错
|
|
250
230
|
handler.getDeclarations().forEach(exp => {
|
|
@@ -255,12 +235,10 @@ export class ApiGenerator {
|
|
|
255
235
|
this.debug('生成接口: %s ...', handlerPath);
|
|
256
236
|
const [requestType, responseType, methodType] = handlerExp.getType().getTypeArguments();
|
|
257
237
|
const handlerComment = this.getComment(handlerExp.getParent().getParent());
|
|
258
|
-
|
|
259
238
|
if (handlerComment.tags.has('private')) {
|
|
260
239
|
this.debug('跳过生成接口: %s', `${handlerPath}`);
|
|
261
240
|
continue;
|
|
262
241
|
}
|
|
263
|
-
|
|
264
242
|
const handlerCategory = pascalCase(basePath) || 'Index';
|
|
265
243
|
const handlerName = handlerComment.description || handlerPath;
|
|
266
244
|
const handlerMethod = methodType.getLiteralValueOrThrow();
|
|
@@ -282,7 +260,6 @@ export class ApiGenerator {
|
|
|
282
260
|
});
|
|
283
261
|
}
|
|
284
262
|
}
|
|
285
|
-
|
|
286
263
|
this.debug('写入文件...');
|
|
287
264
|
await Promise.all([fs.outputJSON(path.join(this.cwd, 'temp/api.json'), handles, {
|
|
288
265
|
spaces: 2
|
|
@@ -290,5 +267,6 @@ export class ApiGenerator {
|
|
|
290
267
|
spaces: 2
|
|
291
268
|
})]);
|
|
292
269
|
}
|
|
270
|
+
}
|
|
293
271
|
|
|
294
|
-
|
|
272
|
+
// new ApiGenerator().generate()
|
package/lib/cli/build_util.js
CHANGED
|
@@ -8,7 +8,6 @@ import { dedent, uniq } from 'vtils';
|
|
|
8
8
|
export class BuildUtil {
|
|
9
9
|
static async build(options) {
|
|
10
10
|
var _options$minify, _options$inlineEnvs;
|
|
11
|
-
|
|
12
11
|
const mainFile = path.join(options.cwd, 'src/main.ts');
|
|
13
12
|
const pkgFile = path.join(options.cwd, 'package.json');
|
|
14
13
|
const pkgContent = await fs.readJson(pkgFile);
|
|
@@ -20,8 +19,9 @@ export class BuildUtil {
|
|
|
20
19
|
const distNoInstallFile = path.join(distDir, 'no_install.lock');
|
|
21
20
|
const distBundleFile = path.join(tempDir, `app.${pkgContent.version}.tgz`);
|
|
22
21
|
await fs.ensureDir(distDir);
|
|
23
|
-
await fs.ensureDir(tempDir);
|
|
22
|
+
await fs.ensureDir(tempDir);
|
|
24
23
|
|
|
24
|
+
// 处理 external
|
|
25
25
|
const external = uniq(options.external || []);
|
|
26
26
|
const externalWithVersions = (await Promise.all(external.map(async item => {
|
|
27
27
|
try {
|
|
@@ -39,17 +39,17 @@ export class BuildUtil {
|
|
|
39
39
|
version: ''
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
|
-
}))).filter(item => !!item.version);
|
|
42
|
+
}))).filter(item => !!item.version);
|
|
43
43
|
|
|
44
|
+
// fix: hexoid 构建问题
|
|
44
45
|
try {
|
|
45
46
|
const hexoidPackageJsonFile = require.resolve('hexoid/package.json');
|
|
46
|
-
|
|
47
47
|
const pkg = await fs.readJson(hexoidPackageJsonFile);
|
|
48
48
|
delete pkg.module;
|
|
49
49
|
await fs.writeJson(hexoidPackageJsonFile, pkg);
|
|
50
|
-
} catch {}
|
|
51
|
-
|
|
50
|
+
} catch {}
|
|
52
51
|
|
|
52
|
+
// 构建
|
|
53
53
|
await fs.emptyDir(distDir);
|
|
54
54
|
await esbuild.build({
|
|
55
55
|
entryPoints: [mainFile],
|
|
@@ -75,7 +75,6 @@ export class BuildUtil {
|
|
|
75
75
|
},
|
|
76
76
|
plugins: [{
|
|
77
77
|
name: 'extract-assets',
|
|
78
|
-
|
|
79
78
|
setup(build) {
|
|
80
79
|
build.onLoad({
|
|
81
80
|
filter: /\/svg-captcha\/lib\/option-manager\.js$/
|
|
@@ -114,30 +113,29 @@ export class BuildUtil {
|
|
|
114
113
|
};
|
|
115
114
|
});
|
|
116
115
|
}
|
|
117
|
-
|
|
118
116
|
}]
|
|
119
|
-
});
|
|
117
|
+
});
|
|
120
118
|
|
|
119
|
+
// 兼容 prisma
|
|
121
120
|
const prismaSchemaFile = path.join(options.cwd, 'src/db/schema.prisma');
|
|
122
|
-
|
|
123
121
|
if (await fs.pathExists(prismaSchemaFile)) {
|
|
124
122
|
// 复制 schema.prisma
|
|
125
123
|
const distPrismaSchemaFile = path.join(distDir, path.basename(prismaSchemaFile));
|
|
126
|
-
await fs.copyFile(prismaSchemaFile, distPrismaSchemaFile);
|
|
124
|
+
await fs.copyFile(prismaSchemaFile, distPrismaSchemaFile);
|
|
127
125
|
|
|
126
|
+
// 复制查询引擎
|
|
128
127
|
const libqueryEngineFiles = await globby('libquery_engine-*', {
|
|
129
128
|
cwd: path.join(options.cwd, 'node_modules/.prisma/client'),
|
|
130
129
|
ignore: ['libquery_engine-{darwin,windows}*'],
|
|
131
130
|
onlyFiles: true,
|
|
132
131
|
absolute: true
|
|
133
132
|
});
|
|
134
|
-
|
|
135
133
|
for (const libqueryEngineFile of libqueryEngineFiles) {
|
|
136
134
|
await fs.copyFile(libqueryEngineFile, path.join(distDir, path.basename(libqueryEngineFile)));
|
|
137
135
|
}
|
|
138
|
-
}
|
|
139
|
-
|
|
136
|
+
}
|
|
140
137
|
|
|
138
|
+
// 写入 pkg
|
|
141
139
|
const deps = externalWithVersions.reduce((res, item) => {
|
|
142
140
|
res[item.name] = item.version;
|
|
143
141
|
return res;
|
|
@@ -147,8 +145,9 @@ export class BuildUtil {
|
|
|
147
145
|
version: pkgContent.version,
|
|
148
146
|
dependencies: deps
|
|
149
147
|
};
|
|
150
|
-
await fs.writeJSON(distPkgFile, distPkgContent);
|
|
148
|
+
await fs.writeJSON(distPkgFile, distPkgContent);
|
|
151
149
|
|
|
150
|
+
// 写入 pm2 配置
|
|
152
151
|
await fs.writeFile(distPm2File, dedent`
|
|
153
152
|
module.exports = {
|
|
154
153
|
apps: [
|
|
@@ -160,7 +159,6 @@ export class BuildUtil {
|
|
|
160
159
|
],
|
|
161
160
|
}
|
|
162
161
|
`);
|
|
163
|
-
|
|
164
162
|
if (externalWithVersions.length) {
|
|
165
163
|
if (options.noInstall) {
|
|
166
164
|
await fs.writeFile(distNoInstallFile, '');
|
|
@@ -177,10 +175,9 @@ export class BuildUtil {
|
|
|
177
175
|
}
|
|
178
176
|
});
|
|
179
177
|
}
|
|
180
|
-
}
|
|
181
|
-
|
|
178
|
+
}
|
|
182
179
|
|
|
180
|
+
// 打包为 tgz
|
|
183
181
|
await compressing.tgz.compressDir(distDir, distBundleFile);
|
|
184
182
|
}
|
|
185
|
-
|
|
186
183
|
}
|
package/lib/cli/cli.js
CHANGED
|
@@ -20,14 +20,12 @@ yargs.command('dev', '开始开发', _ => _.positional('index', {
|
|
|
20
20
|
}), async argv => {
|
|
21
21
|
process.env.NODE_ENV = 'development';
|
|
22
22
|
let lastRun;
|
|
23
|
-
|
|
24
23
|
const run = () => {
|
|
25
|
-
lastRun = execa('
|
|
24
|
+
lastRun = execa('tsx', ['src/main.ts'], {
|
|
26
25
|
cwd: process.cwd(),
|
|
27
26
|
stdio: 'inherit'
|
|
28
27
|
});
|
|
29
28
|
};
|
|
30
|
-
|
|
31
29
|
if (argv.npc) {
|
|
32
30
|
const [server, vkey] = argv.npc.split('@');
|
|
33
31
|
execa('docker', ['run', '--rm', 'ffdfgdfg/npc:v0.26.9', `-server=${server}`, `-vkey=${vkey}`, '-type=tcp'], {
|
|
@@ -35,10 +33,10 @@ yargs.command('dev', '开始开发', _ => _.positional('index', {
|
|
|
35
33
|
stdio: 'inherit'
|
|
36
34
|
});
|
|
37
35
|
}
|
|
38
|
-
|
|
39
36
|
await TemplateUtil.init(process.cwd());
|
|
40
37
|
const watcher = chokidar.watch(['src', '.env', '.env.*'], {
|
|
41
|
-
cwd: process.cwd()
|
|
38
|
+
cwd: process.cwd(),
|
|
39
|
+
ignored: '**/*.test.*'
|
|
42
40
|
});
|
|
43
41
|
watcher.on('all', debounce(async () => {
|
|
44
42
|
await EnvUtil.importFile({
|
|
@@ -58,7 +56,6 @@ yargs.command('dev', '开始开发', _ => _.positional('index', {
|
|
|
58
56
|
console.log(`✔️ 索引文件已更新: ${filePath}`);
|
|
59
57
|
}
|
|
60
58
|
});
|
|
61
|
-
|
|
62
59
|
if (lastRun) {
|
|
63
60
|
console.log('==== 重启服务中 ====');
|
|
64
61
|
lastRun.kill();
|
|
@@ -124,15 +121,14 @@ yargs.command('dev', '开始开发', _ => _.positional('index', {
|
|
|
124
121
|
await EnvUtil.importFile({
|
|
125
122
|
cwd: process.cwd(),
|
|
126
123
|
file: '.env'
|
|
127
|
-
});
|
|
124
|
+
});
|
|
125
|
+
// 之所以能成功是因为 Prisma 用的 dotenv 默认不会覆盖已经设置的环境变量
|
|
128
126
|
// https://github.com/motdotla/dotenv#override
|
|
129
|
-
|
|
130
127
|
process.env.DATABASE_URL = process.env.DATABASE_ACTION_URL || process.env.DATABASE_URL;
|
|
131
128
|
await execa('tnpx', ['prisma', ...argv._.slice(1), '--schema', 'src/db/schema.prisma'], {
|
|
132
129
|
cwd: process.cwd(),
|
|
133
130
|
stdio: 'inherit'
|
|
134
131
|
});
|
|
135
|
-
|
|
136
132
|
if (!argv.production) {
|
|
137
133
|
await TemplateUtil.initModels(process.cwd());
|
|
138
134
|
}
|
|
@@ -151,14 +147,13 @@ yargs.command('dev', '开始开发', _ => _.positional('index', {
|
|
|
151
147
|
file: '.env'
|
|
152
148
|
});
|
|
153
149
|
process.env.DATABASE_URL = process.env.DATABASE_ACTION_URL || process.env.DATABASE_URL;
|
|
154
|
-
|
|
155
150
|
if (!argv.script) {
|
|
156
151
|
await execa('tnpx', ['select-run'], {
|
|
157
152
|
cwd: process.cwd(),
|
|
158
153
|
stdio: 'inherit'
|
|
159
154
|
});
|
|
160
155
|
} else {
|
|
161
|
-
await execa('
|
|
156
|
+
await execa('tsx', [argv.script], {
|
|
162
157
|
cwd: process.cwd(),
|
|
163
158
|
stdio: 'inherit'
|
|
164
159
|
});
|
|
@@ -172,7 +167,6 @@ yargs.command('dev', '开始开发', _ => _.positional('index', {
|
|
|
172
167
|
cwd: process.cwd(),
|
|
173
168
|
file: '.env.deploy'
|
|
174
169
|
});
|
|
175
|
-
|
|
176
170
|
if (argv.type === 'env') {
|
|
177
171
|
const appEnv = await EnvUtil.parseFileAsMap({
|
|
178
172
|
cwd: process.cwd(),
|
package/lib/cli/deploy_util.js
CHANGED
|
@@ -6,11 +6,9 @@ export class DeployUtil {
|
|
|
6
6
|
static async deploy(options) {
|
|
7
7
|
const pkgContent = await fs.readJson(path.join(options.cwd, 'package.json'));
|
|
8
8
|
const appFile = path.join(options.cwd, `temp/app.${pkgContent.version}.tgz`);
|
|
9
|
-
|
|
10
9
|
if (!(await fs.pathExists(appFile))) {
|
|
11
10
|
throw new Error('请先构建');
|
|
12
11
|
}
|
|
13
|
-
|
|
14
12
|
const ssh = new NodeSSH();
|
|
15
13
|
await ssh.connect({
|
|
16
14
|
host: options.host,
|
|
@@ -44,7 +42,6 @@ export class DeployUtil {
|
|
|
44
42
|
});
|
|
45
43
|
ssh.dispose();
|
|
46
44
|
}
|
|
47
|
-
|
|
48
45
|
static async deployEnv(options) {
|
|
49
46
|
const ssh = new NodeSSH();
|
|
50
47
|
await ssh.connect({
|
|
@@ -68,5 +65,4 @@ export class DeployUtil {
|
|
|
68
65
|
});
|
|
69
66
|
ssh.dispose();
|
|
70
67
|
}
|
|
71
|
-
|
|
72
68
|
}
|
package/lib/cli/env_util.js
CHANGED
|
@@ -9,27 +9,21 @@ export class EnvUtil {
|
|
|
9
9
|
const [devValue, prodValue] = value.split(/\s+\|\|\s+/);
|
|
10
10
|
return devOrProd(() => EnvUtil.normalizeValue(devValue), () => EnvUtil.normalizeValue(prodValue));
|
|
11
11
|
}
|
|
12
|
-
|
|
13
12
|
return value === 'true' ? true : value === 'false' ? false : value.startsWith('[') && value.endsWith(']') || value.startsWith('{') && value.endsWith('}') ? JSON.parse(value) : isNumeric(value) ? Number(value) : value;
|
|
14
13
|
} else if (value && typeof value === 'object') {
|
|
15
14
|
if (value.dev != null && value.prod != null) {
|
|
16
15
|
return devOrProd(() => value.dev, () => value.prod);
|
|
17
16
|
}
|
|
18
|
-
|
|
19
17
|
return value;
|
|
20
18
|
}
|
|
21
|
-
|
|
22
19
|
return value;
|
|
23
20
|
}
|
|
24
|
-
|
|
25
21
|
static parseContent(src, isYaml = false) {
|
|
26
22
|
const envs = [];
|
|
27
|
-
|
|
28
23
|
if (isYaml) {
|
|
29
24
|
const envObj = yamlParse(src);
|
|
30
25
|
Object.keys(envObj).forEach(key => {
|
|
31
26
|
var _src$match;
|
|
32
|
-
|
|
33
27
|
envs.push({
|
|
34
28
|
key: key,
|
|
35
29
|
value: this.normalizeValue(envObj[key]),
|
|
@@ -38,56 +32,54 @@ export class EnvUtil {
|
|
|
38
32
|
});
|
|
39
33
|
} else {
|
|
40
34
|
// https://github.com/andreialecu/dotenv/blob/feat-multiline/lib/main.js
|
|
41
|
-
const multilineLineBreaks = true;
|
|
35
|
+
const multilineLineBreaks = true;
|
|
42
36
|
|
|
37
|
+
// convert Buffers before splitting into lines and processing
|
|
43
38
|
const lines = src.split(EnvUtil.NEWLINES_MATCH);
|
|
44
39
|
let lastComment = '';
|
|
45
|
-
|
|
46
40
|
for (let idx = 0; idx < lines.length; idx++) {
|
|
47
|
-
let line = lines[idx];
|
|
48
|
-
|
|
49
|
-
const keyValueArr = line.match(EnvUtil.RE_INI_KEY_VAL); // matched?
|
|
41
|
+
let line = lines[idx];
|
|
50
42
|
|
|
43
|
+
// matching "KEY' and 'VAL' in 'KEY=VAL'
|
|
44
|
+
const keyValueArr = line.match(EnvUtil.RE_INI_KEY_VAL);
|
|
45
|
+
// matched?
|
|
51
46
|
if (keyValueArr != null) {
|
|
52
|
-
const key = keyValueArr[1];
|
|
53
|
-
|
|
47
|
+
const key = keyValueArr[1];
|
|
48
|
+
// default undefined or missing values to empty string
|
|
54
49
|
let val = keyValueArr[2] || '';
|
|
55
50
|
let end = val.length - 1;
|
|
56
51
|
const isDoubleQuoted = val[0] === '"' && val[end] === '"';
|
|
57
52
|
const isSingleQuoted = val[0] === "'" && val[end] === "'";
|
|
58
53
|
const isMultilineDoubleQuoted = val[0] === '"' && (val.length === 1 || val[end] !== '"');
|
|
59
|
-
const isMultilineSingleQuoted = val[0] === "'" && (val.length === 1 || val[end] !== "'");
|
|
54
|
+
const isMultilineSingleQuoted = val[0] === "'" && (val.length === 1 || val[end] !== "'");
|
|
60
55
|
|
|
56
|
+
// if parsing line breaks and the value starts with a quote
|
|
61
57
|
if (multilineLineBreaks && (isMultilineDoubleQuoted || isMultilineSingleQuoted)) {
|
|
62
58
|
const quoteChar = isMultilineDoubleQuoted ? '"' : "'";
|
|
63
59
|
val = val.substring(1);
|
|
64
|
-
|
|
65
60
|
while (idx++ < lines.length - 1) {
|
|
66
61
|
line = lines[idx];
|
|
67
62
|
end = line.length - 1;
|
|
68
|
-
|
|
69
63
|
if (line[end] === quoteChar) {
|
|
70
64
|
val += EnvUtil.NEWLINE + line.substring(0, end);
|
|
71
65
|
break;
|
|
72
66
|
}
|
|
73
|
-
|
|
74
67
|
val += EnvUtil.NEWLINE + line;
|
|
75
68
|
}
|
|
76
|
-
|
|
77
|
-
|
|
69
|
+
val = dedent(val);
|
|
70
|
+
// if single or double quoted, remove quotes
|
|
78
71
|
} else if (isSingleQuoted || isDoubleQuoted) {
|
|
79
|
-
val = val.substring(1, end);
|
|
72
|
+
val = val.substring(1, end);
|
|
80
73
|
|
|
74
|
+
// if double quoted, expand newlines
|
|
81
75
|
if (isDoubleQuoted) {
|
|
82
76
|
val = val.replace(EnvUtil.RE_NEWLINES, EnvUtil.NEWLINE);
|
|
83
77
|
}
|
|
84
|
-
|
|
85
78
|
val = dedent(val);
|
|
86
79
|
} else {
|
|
87
80
|
// remove surrounding whitespace
|
|
88
81
|
val = dedent(val).trim();
|
|
89
82
|
}
|
|
90
|
-
|
|
91
83
|
envs.push({
|
|
92
84
|
key: key,
|
|
93
85
|
value: EnvUtil.normalizeValue(val),
|
|
@@ -96,58 +88,47 @@ export class EnvUtil {
|
|
|
96
88
|
lastComment = '';
|
|
97
89
|
} else {
|
|
98
90
|
const commentArr = line.match(EnvUtil.COMMENT_MATCH);
|
|
99
|
-
|
|
100
91
|
if (commentArr) {
|
|
101
92
|
lastComment = commentArr[1];
|
|
102
93
|
}
|
|
103
94
|
}
|
|
104
95
|
}
|
|
105
96
|
}
|
|
106
|
-
|
|
107
97
|
return envs;
|
|
108
98
|
}
|
|
109
|
-
|
|
110
99
|
static async parseFile(options) {
|
|
111
100
|
const envFile = path.join(options.cwd, options.file);
|
|
112
101
|
const envYmlFile = path.join(options.cwd, `${options.file}.yml`);
|
|
113
102
|
const envYamlFile = path.join(options.cwd, `${options.file}.yaml`);
|
|
114
103
|
const file = (await fs.pathExists(envYmlFile)) ? envYmlFile : (await fs.pathExists(envYamlFile)) ? envYamlFile : (await fs.pathExists(envFile)) ? envFile : '';
|
|
115
|
-
|
|
116
104
|
if (file) {
|
|
117
105
|
const envContent = await fs.readFile(file, 'utf-8');
|
|
118
106
|
const envs = EnvUtil.parseContent(envContent, /\.ya?ml$/i.test(file));
|
|
119
107
|
return envs;
|
|
120
108
|
}
|
|
121
|
-
|
|
122
109
|
return [];
|
|
123
110
|
}
|
|
124
|
-
|
|
125
111
|
static async parseFileAsMap(options) {
|
|
126
112
|
const envs = await EnvUtil.parseFile(options);
|
|
127
113
|
const envsObj = {};
|
|
128
|
-
|
|
129
114
|
for (const env of envs) {
|
|
130
115
|
envsObj[env.key] = env.value;
|
|
131
116
|
}
|
|
132
|
-
|
|
133
117
|
return envsObj;
|
|
134
118
|
}
|
|
135
|
-
|
|
136
119
|
static async importFile(options) {
|
|
137
120
|
const envs = await EnvUtil.parseFile(options);
|
|
138
121
|
const envsObj = {};
|
|
139
|
-
|
|
140
122
|
for (const env of envs) {
|
|
141
123
|
envsObj[env.key] = env.value;
|
|
142
124
|
process.env[env.key] = env.value;
|
|
143
125
|
}
|
|
144
|
-
|
|
145
|
-
|
|
126
|
+
process.env.X_SERVER_ENVS = JSON.stringify({
|
|
127
|
+
// @ts-ignore
|
|
146
128
|
...JSON.parse(process.env.X_SERVER_ENVS || '{}'),
|
|
147
129
|
...envsObj
|
|
148
130
|
});
|
|
149
131
|
}
|
|
150
|
-
|
|
151
132
|
static makeTypes(envs) {
|
|
152
133
|
return dedent`
|
|
153
134
|
declare namespace NodeJS {
|
|
@@ -168,14 +149,12 @@ export class EnvUtil {
|
|
|
168
149
|
}
|
|
169
150
|
`;
|
|
170
151
|
}
|
|
171
|
-
|
|
172
152
|
static async outputTypes(options) {
|
|
173
153
|
const envs = await EnvUtil.parseFile(options);
|
|
174
154
|
const outFile = path.join(options.cwd, options.outFile);
|
|
175
155
|
const types = EnvUtil.makeTypes(envs);
|
|
176
156
|
await fs.outputFile(outFile, types);
|
|
177
157
|
}
|
|
178
|
-
|
|
179
158
|
}
|
|
180
159
|
EnvUtil.NEWLINE = '\n';
|
|
181
160
|
EnvUtil.RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*(.*)?\s*$/;
|