milkio 0.0.10 → 0.0.11
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/.co.toml +0 -0
- package/api-test/index.ts +64 -0
- package/c.ts +39 -57
- package/defines/define-api-test.ts +3 -3
- package/defines/define-api.ts +3 -3
- package/defines/define-http-handler.ts +60 -69
- package/defines/define-middleware.ts +2 -2
- package/defines/define-use.ts +6 -6
- package/index.ts +23 -22
- package/kernel/context.ts +1 -2
- package/kernel/fail.ts +6 -6
- package/kernel/logger.ts +38 -38
- package/kernel/meta.ts +5 -5
- package/kernel/middleware.ts +16 -16
- package/kernel/milkio.ts +70 -95
- package/kernel/runtime.ts +2 -7
- package/kernel/validate.ts +5 -5
- package/package.json +4 -1
- package/scripts/gen-insignificant.ts +261 -0
- package/scripts/gen-significant.ts +176 -0
- package/{scripts → scripts-del}/build-cookbook.ts +119 -119
- package/scripts-del/build-dto.ts +65 -0
- package/{scripts → scripts-del}/generate/generate-app-partial.ts +31 -31
- package/{scripts → scripts-del}/generate/generate-app.ts +41 -41
- package/scripts-del/generate/generate-database.ts +22 -0
- package/scripts-del/generate-partial.ts +15 -0
- package/scripts-del/generate.ts +23 -0
- package/templates/api.ts +4 -4
- package/types.ts +29 -19
- package/utils/create-template.ts +5 -5
- package/utils/create-ulid.ts +3 -3
- package/utils/env-to-boolean.ts +5 -5
- package/utils/env-to-number.ts +2 -2
- package/utils/env-to-string.ts +2 -2
- package/utils/exec.ts +12 -12
- package/utils/handle-catch-error.ts +10 -10
- package/utils/remove-dir.ts +11 -11
- package/utils/tson.ts +2 -2
- package/defines/define-api-test-handler.ts +0 -71
- package/kernel/config.ts +0 -14
- package/scripts/build-dto.ts +0 -65
- package/scripts/generate/generate-database.ts +0 -22
- package/scripts/generate-database.ts +0 -23
- package/scripts/generate-partial.ts +0 -15
- package/scripts/generate.ts +0 -23
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/* eslint-disable no-console, @typescript-eslint/no-dynamic-delete */
|
|
2
|
+
|
|
3
|
+
import { $ } from "bun"
|
|
4
|
+
import { join } from "node:path"
|
|
5
|
+
import { cwd } from "node:process"
|
|
6
|
+
import { existsSync } from "node:fs"
|
|
7
|
+
import { writeFile, readFile, mkdir, copyFile } from "node:fs/promises"
|
|
8
|
+
import { MilkioConfig, TSON, type Cookbook } from ".."
|
|
9
|
+
|
|
10
|
+
export default async () => {
|
|
11
|
+
const schema = await import("../../../generated/api-schema")
|
|
12
|
+
const paths = Object.keys(schema.default.apiMethodsSchema)
|
|
13
|
+
|
|
14
|
+
console.log('')
|
|
15
|
+
console.time(`🧊 Cookbook Stage`)
|
|
16
|
+
|
|
17
|
+
const cookbook: Cookbook = {}
|
|
18
|
+
for (const path of paths) {
|
|
19
|
+
// const module = await import(/* @vite-ignore */ join(`../../../src/apps/${path}`));
|
|
20
|
+
const code = (await readFile(join(cwd(), `./src/apps/${path}.ts`))).toString()
|
|
21
|
+
const codeLines = code.split("\n")
|
|
22
|
+
let title
|
|
23
|
+
let desc
|
|
24
|
+
const descRaw = /\n\/\*\*\n[\s\S]+?\*\//.exec(code)?.[0] ?? ""
|
|
25
|
+
|
|
26
|
+
if (descRaw) {
|
|
27
|
+
const descRawLines = descRaw.split("\n")
|
|
28
|
+
if (descRawLines.at(0)?.trim() === "") descRawLines.shift()
|
|
29
|
+
if (descRawLines.at(-1)?.trim() === "") descRawLines.pop()
|
|
30
|
+
let first = true
|
|
31
|
+
for (let index = 0; index < descRawLines.length; index++) {
|
|
32
|
+
const descRawLine = descRawLines[index].replace(/^[/ ]+?[*]*/, "").replace(/[*]*\/$/, "")
|
|
33
|
+
|
|
34
|
+
if (!descRawLine) continue
|
|
35
|
+
if (first) {
|
|
36
|
+
title = descRawLine.replace(/#/g, "").trim()
|
|
37
|
+
// Originally the title was in the first line, desc is the rest of it, now desc contains complete markdown content.
|
|
38
|
+
// continue;
|
|
39
|
+
}
|
|
40
|
+
first = false
|
|
41
|
+
desc = (desc ?? "") + "\n" + descRawLine.trim()
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let apiParams = /action\([\s\S]+?\)/.exec(code)?.[0] ?? "" // The intention of the following code is to extract the parameter part of the action.
|
|
46
|
+
apiParams = /\([\s\S]*,/.exec(apiParams)?.[0] ?? ""
|
|
47
|
+
apiParams = apiParams.slice(0, -1)
|
|
48
|
+
apiParams = apiParams.slice(/[\s\S]+?:/.exec(apiParams)?.[0].length)
|
|
49
|
+
const apiParamsLines = apiParams.split("\n") // The intention of the following code is to remove extra spaces, which will make the code look more beautiful.
|
|
50
|
+
if (apiParamsLines.at(-1)?.trim() === "") apiParamsLines.pop()
|
|
51
|
+
if (apiParamsLines.at(-1)?.trim() === "") apiParamsLines.pop()
|
|
52
|
+
let spaceNumber = 0
|
|
53
|
+
for (const char of apiParamsLines.at(-1) ?? "") {
|
|
54
|
+
if (char === " ") spaceNumber++
|
|
55
|
+
else break
|
|
56
|
+
}
|
|
57
|
+
for (let index = 0; index < apiParamsLines.length; index++) {
|
|
58
|
+
const line = apiParamsLines[index]
|
|
59
|
+
let spaceNumberForThisLine = 0
|
|
60
|
+
for (const char of line) {
|
|
61
|
+
if (char === " ") spaceNumberForThisLine++
|
|
62
|
+
else break
|
|
63
|
+
}
|
|
64
|
+
if (spaceNumberForThisLine >= spaceNumber) {
|
|
65
|
+
apiParamsLines[index] = line.slice(spaceNumber)
|
|
66
|
+
} else {
|
|
67
|
+
apiParamsLines[index] = line.slice(spaceNumberForThisLine)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
apiParams = apiParamsLines.join("\n")
|
|
71
|
+
|
|
72
|
+
// Find the code for the API testing section.
|
|
73
|
+
const apiTestsCodeChars = []
|
|
74
|
+
let apiTestsStartIndex = undefined as undefined | number
|
|
75
|
+
let semicolonMatch = 0
|
|
76
|
+
let semicolonMax = 0
|
|
77
|
+
for (let index = 0; index < codeLines.length; index++) {
|
|
78
|
+
const codeLine = codeLines[index]
|
|
79
|
+
if (apiTestsStartIndex === undefined && !codeLine.includes("defineApiTest(")) continue
|
|
80
|
+
if (apiTestsStartIndex === undefined) apiTestsStartIndex = index
|
|
81
|
+
const codeChars = codeLine.split("")
|
|
82
|
+
for (const codeChar of codeChars) {
|
|
83
|
+
if (codeChar === "[") {
|
|
84
|
+
semicolonMatch++
|
|
85
|
+
semicolonMax++
|
|
86
|
+
}
|
|
87
|
+
if (semicolonMatch !== 0) apiTestsCodeChars.push(codeChar)
|
|
88
|
+
if (codeChar === "]") semicolonMatch--
|
|
89
|
+
}
|
|
90
|
+
if (semicolonMatch === 0 && semicolonMax >= 1) {
|
|
91
|
+
break
|
|
92
|
+
}
|
|
93
|
+
apiTestsCodeChars.push("\n")
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Find the code for each API test case.
|
|
97
|
+
const apiCaseCodes: Array<string> = []
|
|
98
|
+
let currentApiCaseCode = undefined as undefined | Array<string>
|
|
99
|
+
let apiTestCaseStartIndex = undefined as undefined | number
|
|
100
|
+
let apiTestCaseMatch = 0
|
|
101
|
+
for (let index = 0; index < apiTestsCodeChars.length; index++) {
|
|
102
|
+
const apiTestsCodeChar = apiTestsCodeChars[index]
|
|
103
|
+
if (apiTestCaseStartIndex === undefined && apiTestsCodeChar === "{") {
|
|
104
|
+
currentApiCaseCode = []
|
|
105
|
+
apiTestCaseStartIndex = index
|
|
106
|
+
}
|
|
107
|
+
if (apiTestsCodeChar === "{") {
|
|
108
|
+
apiTestCaseMatch++
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (apiTestCaseMatch !== 0) currentApiCaseCode!.push(apiTestsCodeChar)
|
|
112
|
+
|
|
113
|
+
if (apiTestsCodeChar === "}") {
|
|
114
|
+
apiTestCaseMatch--
|
|
115
|
+
if (apiTestCaseMatch === 0) {
|
|
116
|
+
apiCaseCodes.push(currentApiCaseCode!.join(""))
|
|
117
|
+
currentApiCaseCode = undefined
|
|
118
|
+
apiTestCaseStartIndex = undefined
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const apiCases: Array<{
|
|
124
|
+
name: string;
|
|
125
|
+
handler: string;
|
|
126
|
+
}> = []
|
|
127
|
+
|
|
128
|
+
for (let index = 0; index < apiCaseCodes.length; index++) {
|
|
129
|
+
const code = apiCaseCodes[index]
|
|
130
|
+
const name = /name:[\s\S]+?,/.exec(code)?.[0]?.slice(5, -1)?.trim().slice(1, -1) ?? ""
|
|
131
|
+
const handlerChars = /handler:[\s\S]*/.exec(code)?.[0]?.split("") ?? []
|
|
132
|
+
let handler = "" // Find the main code of the handler.
|
|
133
|
+
let handlerStartIndex = undefined as undefined | number
|
|
134
|
+
let handlerMatch = 0
|
|
135
|
+
for (let index = 0; index < handlerChars.length; index++) {
|
|
136
|
+
const handlerChar = handlerChars[index]
|
|
137
|
+
if (handlerStartIndex !== undefined && handlerChar === "{") handlerStartIndex = index
|
|
138
|
+
if (handlerChar === "{") handlerMatch++
|
|
139
|
+
if (handlerMatch !== 0) handler = handler + handlerChar
|
|
140
|
+
if (handlerChar === "}") handlerMatch--
|
|
141
|
+
if (handlerStartIndex !== undefined && handlerMatch === 0) break
|
|
142
|
+
}
|
|
143
|
+
handler = handler.slice(1, -1)
|
|
144
|
+
|
|
145
|
+
const handlerLines = handler.split("\n") // The intention of the following code is to remove extra spaces, which will make the code look more beautiful.
|
|
146
|
+
if (handlerLines.at(-1)?.trim() === "") handlerLines.pop()
|
|
147
|
+
if (handlerLines.at(-1)?.trim() === "") handlerLines.pop()
|
|
148
|
+
if (handlerLines.at(0)?.trim() === "") handlerLines.shift()
|
|
149
|
+
if (handlerLines.at(0)?.trim() === "") handlerLines.shift()
|
|
150
|
+
let spaceNumber = 0
|
|
151
|
+
for (const char of handlerLines.at(-1) ?? "") {
|
|
152
|
+
if (char === " ") spaceNumber++
|
|
153
|
+
else break
|
|
154
|
+
}
|
|
155
|
+
for (let index = 0; index < handlerLines.length; index++) {
|
|
156
|
+
const line = handlerLines[index]
|
|
157
|
+
let spaceNumberForThisLine = 0
|
|
158
|
+
for (const char of line) {
|
|
159
|
+
if (char === " ") spaceNumberForThisLine++
|
|
160
|
+
else break
|
|
161
|
+
}
|
|
162
|
+
if (spaceNumberForThisLine >= spaceNumber) {
|
|
163
|
+
handlerLines[index] = line.slice(spaceNumber)
|
|
164
|
+
} else {
|
|
165
|
+
handlerLines[index] = line.slice(spaceNumberForThisLine)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
handler = handlerLines.join("\n")
|
|
169
|
+
|
|
170
|
+
apiCases.push({
|
|
171
|
+
name,
|
|
172
|
+
handler
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// This value has been deprecated because TypeScript types can already replace it well
|
|
177
|
+
// let paramsSchema;
|
|
178
|
+
// try {
|
|
179
|
+
// const moduleGenerated = await import(/* @vite-ignore */ `../../../generated/products/apps/${path}`);
|
|
180
|
+
// paramsSchema = moduleGenerated.paramsSchema.schemas[0]?.properties?.data;
|
|
181
|
+
// } catch (error) {}
|
|
182
|
+
|
|
183
|
+
cookbook[path] = {
|
|
184
|
+
title,
|
|
185
|
+
desc,
|
|
186
|
+
params: apiParams,
|
|
187
|
+
cases: apiCases
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* -- indexes
|
|
193
|
+
*/
|
|
194
|
+
|
|
195
|
+
const indexes: Record<string, Array<string>> = {}
|
|
196
|
+
const folderIndexes: Record<string, Array<string>> = {}
|
|
197
|
+
indexes["(root)"] = []
|
|
198
|
+
folderIndexes["(root)"] = []
|
|
199
|
+
for (const path in cookbook) {
|
|
200
|
+
if (!path.includes("/")) indexes["(root)"].push(path)
|
|
201
|
+
}
|
|
202
|
+
for (const path in cookbook) {
|
|
203
|
+
const dirnames = path.split("/")
|
|
204
|
+
for (let index = 0; index < dirnames.length - 1; index++) {
|
|
205
|
+
const dirpath = dirnames.slice(0, index + 1).join("/")
|
|
206
|
+
if (!indexes[dirpath]) indexes[dirpath] = []
|
|
207
|
+
if (!folderIndexes[dirpath]) folderIndexes[dirpath] = []
|
|
208
|
+
if (index + 1 === dirnames.length - 1) {
|
|
209
|
+
indexes[dirpath].push(path)
|
|
210
|
+
} else {
|
|
211
|
+
const childDirpath = dirnames.slice(0, index + 2).join("/")
|
|
212
|
+
if (folderIndexes[dirpath].includes(childDirpath)) continue
|
|
213
|
+
folderIndexes[dirpath].push(childDirpath)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
for (const path in folderIndexes) {
|
|
218
|
+
if (path.includes("/") || path === "(root)") continue
|
|
219
|
+
folderIndexes["(root)"].push(path)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const readme = (await readFile(join(cwd(), "src", "apps", "README.md"))).toString()
|
|
223
|
+
Object.keys(indexes).forEach((key) => indexes[key].length === 0 && delete indexes[key])
|
|
224
|
+
const generatedAt = new Date()
|
|
225
|
+
|
|
226
|
+
await writeFile(
|
|
227
|
+
join(cwd(), `./generated/cookbook.json`),
|
|
228
|
+
TSON.stringify({
|
|
229
|
+
cookbook,
|
|
230
|
+
readme,
|
|
231
|
+
indexes,
|
|
232
|
+
folderIndexes,
|
|
233
|
+
generatedAt
|
|
234
|
+
})
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
console.timeEnd(`🧊 Cookbook Stage`)
|
|
238
|
+
console.log(``)
|
|
239
|
+
|
|
240
|
+
console.time(`🧊 DTO Stage`)
|
|
241
|
+
await $`bun run ./node_modules/typescript/bin/tsc --outDir "./packages/dto/project"`.quiet()
|
|
242
|
+
await Bun.build({
|
|
243
|
+
entrypoints: ["./packages/dto/index.ts"],
|
|
244
|
+
outdir: "./packages/dto/dist",
|
|
245
|
+
target: 'browser',
|
|
246
|
+
minify: true
|
|
247
|
+
})
|
|
248
|
+
console.timeEnd(`🧊 DTO Stage`)
|
|
249
|
+
console.log(``)
|
|
250
|
+
|
|
251
|
+
if (!existsSync(join(cwd(), "milkio.toml"))) return
|
|
252
|
+
const milkioConfig = (await import(join(cwd(), "milkio.toml"))).default as MilkioConfig
|
|
253
|
+
if (!milkioConfig?.generate?.significant) return
|
|
254
|
+
let i = 0
|
|
255
|
+
for (const command of milkioConfig.generate.significant) {
|
|
256
|
+
++i
|
|
257
|
+
console.time(`🧊 Insignificant Stage (LINE ${i})`)
|
|
258
|
+
await $`${{ raw: command }}`
|
|
259
|
+
console.timeEnd(`🧊 Insignificant Stage (LINE ${i})`)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
|
|
3
|
+
import ejs from "ejs"
|
|
4
|
+
import { join } from "node:path"
|
|
5
|
+
import { existsSync, mkdirSync } from "node:fs"
|
|
6
|
+
import { cwd, exit } from "node:process"
|
|
7
|
+
import { unlink, writeFile } from "node:fs/promises"
|
|
8
|
+
import { camel, hyphen } from "@poech/camel-hump-under"
|
|
9
|
+
import { $, Glob } from "bun"
|
|
10
|
+
import { MilkioConfig } from ".."
|
|
11
|
+
|
|
12
|
+
export default async () => {
|
|
13
|
+
// Delete the files generated in the past and regenerate them
|
|
14
|
+
try {
|
|
15
|
+
await unlink(join(cwd(), "generated", "api-schema.ts"))
|
|
16
|
+
} catch (error) { } // Maybe the file does not exist
|
|
17
|
+
|
|
18
|
+
// Make sure that the existing directories are all present
|
|
19
|
+
existsSync(join("generated")) || mkdirSync(join("generated"))
|
|
20
|
+
existsSync(join("generated", "raw")) || mkdirSync(join("generated", "raw"))
|
|
21
|
+
existsSync(join("generated", "raw", "apps")) || mkdirSync(join("generated", "raw", "apps"))
|
|
22
|
+
|
|
23
|
+
if (!existsSync(join("generated", "README.md"))) {
|
|
24
|
+
await writeFile(join("generated", "README.md"), "⚠️ All files in this directory are generated by milkio. Please do not modify the content, otherwise your modifications will be overwritten in the next generation.")
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const utils = {
|
|
28
|
+
camel: (str: string) => camel(str).replaceAll("-", "").replaceAll("_", ""),
|
|
29
|
+
hyphen: (str: string) => hyphen(str).replaceAll("_", "")
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Write a basic framework to ensure that there are no errors when reading later
|
|
33
|
+
const apiSchemaSkeleton = `
|
|
34
|
+
export default {
|
|
35
|
+
apiValidator: {},
|
|
36
|
+
apiMethodsSchema: {},
|
|
37
|
+
apiMethodsTypeSchema: {},
|
|
38
|
+
}
|
|
39
|
+
`
|
|
40
|
+
await writeFile(join(cwd(), "generated", "api-schema.ts"), ejs.render(apiSchemaSkeleton, { utils }))
|
|
41
|
+
|
|
42
|
+
// Generate api-schema.ts file through templates
|
|
43
|
+
const templateVars = {
|
|
44
|
+
utils,
|
|
45
|
+
apiPaths: [] as Array<string>,
|
|
46
|
+
apiTestPaths: [] as Array<string>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const glob = new Glob("**/*.ts")
|
|
50
|
+
const appFiles = await Array.fromAsync(glob.scan({ cwd: join(cwd(), "src", "apps") }))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
console.time(`🧊 File Stage`)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
for (const path of appFiles) {
|
|
57
|
+
if (!path.endsWith(".ts")) continue
|
|
58
|
+
const module = await import(/* @vite-ignore */ join(cwd(), "src", "apps", path))
|
|
59
|
+
|
|
60
|
+
if (module?.api?.isApi === true) {
|
|
61
|
+
// Exclude disallowed characters
|
|
62
|
+
if (path.includes("_")) {
|
|
63
|
+
console.error(`\n\nPath: ` + `"${path}"`)
|
|
64
|
+
console.error(`Do not use "_" in the path. If you want to add a separator between words, please use "-".\n`)
|
|
65
|
+
exit(1)
|
|
66
|
+
}
|
|
67
|
+
if (!/^[a-z0-9/-]+$/.test(path.slice(0, -3))) {
|
|
68
|
+
console.error(`\n\nPath: ` + `"${path}"`)
|
|
69
|
+
console.error(`The path can only contain lowercase letters, numbers, and "-".\n`)
|
|
70
|
+
exit(1)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
templateVars.apiPaths.push(path)
|
|
74
|
+
|
|
75
|
+
if (module?.test?.isApiTest === true) {
|
|
76
|
+
templateVars.apiTestPaths.push(path)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// typia
|
|
80
|
+
const filePath = join(cwd(), "generated", "raw", "apps", path)
|
|
81
|
+
const dirPath = join(cwd(), "generated", "raw", "apps", path).split("/").slice(0, -1).join("/")
|
|
82
|
+
if (!existsSync(dirPath)) {
|
|
83
|
+
mkdirSync(dirPath, { recursive: true })
|
|
84
|
+
}
|
|
85
|
+
let importPath = "../../../"
|
|
86
|
+
|
|
87
|
+
for (let i = 0; i < path.split("/").length - 1; i++) {
|
|
88
|
+
importPath = importPath + "../"
|
|
89
|
+
}
|
|
90
|
+
importPath = importPath + "src/apps"
|
|
91
|
+
const template = `
|
|
92
|
+
import typia from "typia";
|
|
93
|
+
import { _validate, type ExecuteResultSuccess } from "milkio";
|
|
94
|
+
import { type TSONEncode } from "@southern-aurora/tson";
|
|
95
|
+
import type * as <%= utils.camel(path.slice(0, -3).replaceAll('/', '$')) %> from '${importPath}/<%= path.slice(0, -3) %>';
|
|
96
|
+
|
|
97
|
+
type ParamsT = Parameters<typeof <%= utils.camel(path.replaceAll('/', '$').slice(0, -${3})) %>['api']['action']>[0];
|
|
98
|
+
export const params = async (params: any) => typia.misc.validatePrune<ParamsT>(params);
|
|
99
|
+
type ResultsT = Awaited<ReturnType<typeof <%= utils.camel(path.replaceAll('/', '$').slice(0, -${3})) %>['api']['action']>>;
|
|
100
|
+
export const results = async (results: any) => { _validate(typia.validate<TSONEncode<ExecuteResultSuccess<ResultsT>>>(results)); return typia.json.stringify<TSONEncode<ExecuteResultSuccess<ResultsT>>>(results); };
|
|
101
|
+
|
|
102
|
+
`.trim()
|
|
103
|
+
// export const paramsSchema = typia.json.application<[{ data: ParamsT }], "swagger">();
|
|
104
|
+
|
|
105
|
+
await writeFile(filePath, ejs.render(template, { ...templateVars, path }))
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
await writeFile(
|
|
110
|
+
join(cwd(), "generated", "api-schema.ts"),
|
|
111
|
+
ejs.render(
|
|
112
|
+
`
|
|
113
|
+
/**
|
|
114
|
+
* ⚠️ This file is generated and modifications will be overwritten
|
|
115
|
+
*/
|
|
116
|
+
|
|
117
|
+
// api
|
|
118
|
+
<% for (const path of ${"apiPaths"}) { %>import type * as <%= utils.camel(path.slice(0, -3).replaceAll('/', '$')) %> from '${"../src/apps"}/<%= path.slice(0, -3) %>'
|
|
119
|
+
<% } %>
|
|
120
|
+
import _apiValidator from './products/api-validator.ts'
|
|
121
|
+
|
|
122
|
+
export default {
|
|
123
|
+
apiValidator: _apiValidator,
|
|
124
|
+
${"apiMethodsSchema"}: {
|
|
125
|
+
<% for (const path of apiPaths) { %>'<%= utils.hyphen(path.slice(0, -${3})) %>': () => ({ module: import('../src/apps/<%= path.slice(0, -${3}) %>') }),
|
|
126
|
+
<% } %>
|
|
127
|
+
},
|
|
128
|
+
${"apiMethodsTypeSchema"}: {
|
|
129
|
+
<% for (const path of apiPaths) { %>'<%= utils.hyphen(path.slice(0, -${3})) %>': undefined as unknown as typeof <%= utils.camel(path.slice(0, -${3}).replaceAll('/', '$')) %>,
|
|
130
|
+
<% } %>
|
|
131
|
+
},
|
|
132
|
+
${"apiTestsSchema"}: {
|
|
133
|
+
<% for (const path of apiTestPaths) { %>'<%= utils.hyphen(path.slice(0, -${3})) %>': () => ({ module: import('../src/apps/<%= path.slice(0, -${3}) %>') }),
|
|
134
|
+
<% } %>
|
|
135
|
+
},
|
|
136
|
+
}
|
|
137
|
+
`.trim(),
|
|
138
|
+
templateVars
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
// api
|
|
143
|
+
const apiValidatorTemplate = `/**
|
|
144
|
+
* ⚠️This file is generated and modifications will be overwritten
|
|
145
|
+
*/
|
|
146
|
+
|
|
147
|
+
export default {
|
|
148
|
+
generatedAt: ${new Date().getTime()},
|
|
149
|
+
${"validate"}: {
|
|
150
|
+
<% for (const path of apiPaths) { %>'<%= utils.hyphen(path.slice(0, -${3})) %>': () => import('./apps/<%= utils.hyphen(path) %>'),
|
|
151
|
+
<% } %>
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
`.trim()
|
|
155
|
+
await writeFile(join(cwd(), "generated", "raw", "api-validator.ts"), ejs.render(apiValidatorTemplate, templateVars))
|
|
156
|
+
|
|
157
|
+
console.timeEnd(`🧊 File Stage`)
|
|
158
|
+
console.log(``)
|
|
159
|
+
|
|
160
|
+
// typia
|
|
161
|
+
console.time(`🧊 Typia Stage`)
|
|
162
|
+
await $`bun run ./node_modules/typia/lib/executable/typia.js generate --input generated/raw --output generated/products --project tsconfig.json`
|
|
163
|
+
console.timeEnd(`🧊 Typia Stage`)
|
|
164
|
+
console.log(``)
|
|
165
|
+
|
|
166
|
+
if (!existsSync(join(cwd(), "milkio.toml"))) return
|
|
167
|
+
const milkioConfig = (await import(join(cwd(), "milkio.toml"))).default as MilkioConfig
|
|
168
|
+
if (!milkioConfig?.generate?.significant) return
|
|
169
|
+
let i = 0
|
|
170
|
+
for (const command of milkioConfig.generate.significant) {
|
|
171
|
+
++i
|
|
172
|
+
console.time(`🧊 Significant Stage (LINE ${i})`)
|
|
173
|
+
await $`${{ raw: command }}`
|
|
174
|
+
console.timeEnd(`🧊 Significant Stage (LINE ${i})`)
|
|
175
|
+
}
|
|
176
|
+
}
|