galbe 0.2.0 → 0.4.1
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/.github/workflows/build_test.yml +2 -4
- package/.github/workflows/release.yml +2 -2
- package/bin/cli.ts +8 -104
- package/bin/commands/build.ts +95 -0
- package/bin/commands/dev.ts +53 -0
- package/bin/commands/generate/client.ts +192 -0
- package/bin/commands/generate/code/openapi.parser.ts +479 -0
- package/bin/commands/generate/code.ts +75 -0
- package/bin/commands/generate/index.ts +12 -0
- package/bin/commands/generate/spec.ts +94 -0
- package/bin/res/cli.template.js +120 -0
- package/bin/res/client.template.ts +161 -0
- package/bin/util.ts +164 -0
- package/bun.lockb +0 -0
- package/docs/getting-started.md +8 -0
- package/docs/hooks.md +4 -4
- package/docs/plugins.md +3 -9
- package/docs/routes.md +3 -3
- package/docs/schemas.md +19 -2
- package/package.json +10 -8
- package/scripts/postinstall.ts +1 -3
- package/src/extras/spec/openapi.serializer.ts +264 -0
- package/src/extras.ts +1 -0
- package/src/index.ts +122 -80
- package/src/parser.ts +29 -8
- package/src/router.ts +27 -28
- package/src/routes.ts +142 -40
- package/src/schema.ts +79 -8
- package/src/server.ts +55 -35
- package/src/types.ts +109 -53
- package/src/util.ts +85 -3
- package/src/validator.ts +27 -5
- package/test/parser.test.ts +34 -0
- package/test/requests.test.ts +5 -12
- package/test/resources/test.route.comment.ts +20 -0
- package/test/responses.test.ts +386 -13
- package/test/routeFiles.test.ts +44 -27
- package/test/router.test.ts +67 -42
- package/scripts/build.ts +0 -14
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
name:
|
|
1
|
+
name: Install & Test
|
|
2
2
|
on:
|
|
3
3
|
push:
|
|
4
4
|
pull_request:
|
|
5
5
|
jobs:
|
|
6
6
|
build:
|
|
7
|
-
name:
|
|
7
|
+
name: Install & Test
|
|
8
8
|
runs-on: ubuntu-latest
|
|
9
9
|
steps:
|
|
10
10
|
- uses: actions/checkout@v4
|
|
@@ -13,7 +13,5 @@ jobs:
|
|
|
13
13
|
bun-version: latest
|
|
14
14
|
- name: Install
|
|
15
15
|
run: bun install
|
|
16
|
-
- name: Build
|
|
17
|
-
run: bun run build
|
|
18
16
|
- name: Test
|
|
19
17
|
run: bun test
|
|
@@ -30,9 +30,9 @@ jobs:
|
|
|
30
30
|
run: |
|
|
31
31
|
git config user.email "${GITHUB_ACTOR}@users.noreply.github.com"
|
|
32
32
|
git config user.name "${GITHUB_ACTOR}"
|
|
33
|
-
- name: Install dependencies
|
|
33
|
+
- name: Install dependencies
|
|
34
34
|
run: |
|
|
35
|
-
bun install
|
|
35
|
+
bun install
|
|
36
36
|
- name: Release
|
|
37
37
|
run: |
|
|
38
38
|
npm config set //registry.npmjs.org/:_authToken $NPM_TOKEN
|
package/bin/cli.ts
CHANGED
|
@@ -1,113 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
import { $, Glob } from 'bun'
|
|
4
|
-
import type { RouteMeta } from '../src/routes'
|
|
5
3
|
import { program } from 'commander'
|
|
6
|
-
import { relative, resolve } from 'path'
|
|
7
|
-
import { mkdir, readdir, lstat, rm } from 'fs/promises'
|
|
8
|
-
import { DEFAULT_ROUTE_PATTERN, metaAnalysis } from '../src/routes'
|
|
9
|
-
import { randomUUID } from 'crypto'
|
|
10
|
-
import { Galbe } from '../src'
|
|
11
4
|
|
|
12
|
-
|
|
13
|
-
const BUILD_ID = randomUUID()
|
|
5
|
+
import { pckg } from './util'
|
|
14
6
|
|
|
15
|
-
|
|
7
|
+
import dev from './commands/dev'
|
|
8
|
+
import build from './commands/build'
|
|
9
|
+
import generate from './commands/generate'
|
|
16
10
|
|
|
17
|
-
|
|
18
|
-
routes = routes === true ? DEFAULT_ROUTE_PATTERN : routes
|
|
19
|
-
if (!routes) return []
|
|
20
|
-
let files: { path: string; meta: RouteMeta }[] = []
|
|
21
|
-
if (typeof routes === 'string') {
|
|
22
|
-
for await (const path of new Glob(routes).scan({ cwd: ROOT, absolute: true, onlyFiles: false })) {
|
|
23
|
-
const isDir = (await lstat(path)).isDirectory()
|
|
24
|
-
if (isDir) {
|
|
25
|
-
files = files.concat(
|
|
26
|
-
await Promise.all(
|
|
27
|
-
(
|
|
28
|
-
await readdir(path)
|
|
29
|
-
).map(async f => ({
|
|
30
|
-
path: f,
|
|
31
|
-
meta: await metaAnalysis(path)
|
|
32
|
-
}))
|
|
33
|
-
)
|
|
34
|
-
)
|
|
35
|
-
} else files.push({ path, meta: await metaAnalysis(path) })
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
if (Array.isArray(routes)) for (const r of routes) files = files.concat(await parseRoutes(r))
|
|
39
|
-
return files
|
|
40
|
-
}
|
|
11
|
+
program.name('galbe').description(pckg.description).version(pckg.version)
|
|
41
12
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
await Bun.write(
|
|
46
|
-
resolve(buildPath, 'index.ts'),
|
|
47
|
-
`import galbe from '${relative(buildPath, indexPath)}';
|
|
48
|
-
${routes.map((r, idx) => `import _${idx} from '${relative(buildPath, r.path)}'`).join(';\n')}
|
|
49
|
-
${routes
|
|
50
|
-
.map(
|
|
51
|
-
(r, idx) => `galbe.routesMetadata = {...${JSON.stringify(r.meta)}}
|
|
52
|
-
_${idx}(galbe)`
|
|
53
|
-
)
|
|
54
|
-
.join(';\n')}
|
|
55
|
-
galbe.listen();
|
|
56
|
-
`
|
|
57
|
-
)
|
|
58
|
-
return resolve(buildPath, 'index.ts')
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
program.name('galbe').description('CLI to execute galbe utilities').version('0.1.0')
|
|
62
|
-
|
|
63
|
-
program
|
|
64
|
-
.command('dev')
|
|
65
|
-
.description('Start a dev server running your Galbe application')
|
|
66
|
-
.argument('<string>', 'filename')
|
|
67
|
-
.option('-p, --port <number>', 'port number', '')
|
|
68
|
-
.option('-w, --watch', 'watch file changes', 'true')
|
|
69
|
-
.action(async (fileName, props) => {
|
|
70
|
-
const { port, watch } = props
|
|
71
|
-
const devRoot = resolve(ROOT, '.galbe', 'dev')
|
|
72
|
-
await mkdir(devRoot, { recursive: true })
|
|
73
|
-
await Bun.write(
|
|
74
|
-
resolve(devRoot, 'index.ts'),
|
|
75
|
-
`import galbe from '${relative(devRoot, fileName)}';galbe.listen(${port});`
|
|
76
|
-
)
|
|
77
|
-
process.on('SIGINT', async () => {
|
|
78
|
-
await rm(resolve(ROOT, '.galbe', 'dev'), { recursive: true })
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
await $`BUN_ENV=development bun run ${watch ? '--watch' : ''} ${resolve(devRoot, 'index.ts')}`.cwd(ROOT)
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
program
|
|
85
|
-
.command('build')
|
|
86
|
-
.description('undle your Galbe application')
|
|
87
|
-
.argument('<string>', 'filename')
|
|
88
|
-
.option('-o, --out <string>', 'output file/directory', '')
|
|
89
|
-
.option('-c, --compile', 'create a standalone executable', false)
|
|
90
|
-
.action(async (fileName, props) => {
|
|
91
|
-
const { out, compile } = props
|
|
92
|
-
const g: Galbe = (await import(resolve(ROOT, fileName))).default
|
|
93
|
-
const routes = await parseRoutes(g?.config?.routes)
|
|
94
|
-
const buildIndex = await createBuildIndex(fileName, routes)
|
|
95
|
-
|
|
96
|
-
const cmds = [
|
|
97
|
-
'bun',
|
|
98
|
-
'build',
|
|
99
|
-
buildIndex,
|
|
100
|
-
'--target',
|
|
101
|
-
'bun',
|
|
102
|
-
...(compile ? ['--compile', '--outfile', out ? out : 'app'] : ['--outdir', out ? out : 'dist'])
|
|
103
|
-
].filter(c => c)
|
|
104
|
-
Bun.spawn(cmds, {
|
|
105
|
-
cwd: ROOT,
|
|
106
|
-
stdout: 'inherit',
|
|
107
|
-
async onExit() {
|
|
108
|
-
await rm(resolve(ROOT, '.galbe', 'build', BUILD_ID), { recursive: true })
|
|
109
|
-
}
|
|
110
|
-
})
|
|
111
|
-
})
|
|
13
|
+
dev(program.command('dev'))
|
|
14
|
+
build(program.command('build'))
|
|
15
|
+
generate(program.command('generate'))
|
|
112
16
|
|
|
113
17
|
program.parse()
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { $ } from 'bun'
|
|
2
|
+
|
|
3
|
+
import { Command, Option } from 'commander'
|
|
4
|
+
import { resolve, relative, dirname } from 'path'
|
|
5
|
+
import { tmpdir } from 'os'
|
|
6
|
+
import { mkdir, rm } from 'fs/promises'
|
|
7
|
+
|
|
8
|
+
import { CWD, fmtVal, silentExec } from '../util'
|
|
9
|
+
import { Galbe } from '../../src'
|
|
10
|
+
import { defineRoutes } from '../../src/routes'
|
|
11
|
+
import { BuildConfig } from 'bun'
|
|
12
|
+
|
|
13
|
+
const createBuildIndex = async (indexPath: string, g: Galbe) => {
|
|
14
|
+
const buildId = crypto.randomUUID()
|
|
15
|
+
const buildPath = resolve(tmpdir(), buildId)
|
|
16
|
+
const routes = new Set<string>()
|
|
17
|
+
let errors: any[] = []
|
|
18
|
+
await defineRoutes({ routes: g?.config?.routes }, g, ({ type, error, filepath }) => {
|
|
19
|
+
if (!filepath) return
|
|
20
|
+
routes.add(filepath)
|
|
21
|
+
if (type === 'error') errors.push(error)
|
|
22
|
+
})
|
|
23
|
+
if (errors.length) throw errors
|
|
24
|
+
await mkdir(buildPath, { recursive: true })
|
|
25
|
+
await Bun.write(
|
|
26
|
+
resolve(buildPath, 'index.ts'),
|
|
27
|
+
`import galbe from '${relative(buildPath, indexPath)}';
|
|
28
|
+
${[...routes].map((r, idx) => `import _${idx} from '${relative(buildPath, r)}'`).join(';\n')}
|
|
29
|
+
galbe.meta = ${JSON.stringify(g.meta)};
|
|
30
|
+
${[...routes].map((_, idx) => `_${idx}(galbe)`).join(';\n')}
|
|
31
|
+
galbe.listen();
|
|
32
|
+
`
|
|
33
|
+
)
|
|
34
|
+
return resolve(buildPath, 'index.ts')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default (cmd: Command) => {
|
|
38
|
+
cmd
|
|
39
|
+
.description('bundle your \x1b[1;30m\x1b[36mGalbe\x1b[0m application')
|
|
40
|
+
.argument('<index>', 'index file')
|
|
41
|
+
.addOption(new Option('-o, --out <dir>', 'output directory').default('dist/app', fmtVal('dist/app')))
|
|
42
|
+
.addOption(new Option('-C, --compile', 'create a standalone executable').default(false, fmtVal(false)))
|
|
43
|
+
.option('-c, --config <file>', 'bun js or ts config file')
|
|
44
|
+
.action(async (index, props) => {
|
|
45
|
+
const { out, compile, config } = props
|
|
46
|
+
|
|
47
|
+
const bunfig = config ? (await import(resolve(CWD, config)))?.default || {} : {}
|
|
48
|
+
|
|
49
|
+
let error = null
|
|
50
|
+
process.stdout.write('📦 \x1b[1;30mBuilding \x1b[36mGalbe\x1b[0m\x1b[1;30m app\x1b[0m')
|
|
51
|
+
let g: Galbe = await silentExec(async () => {
|
|
52
|
+
try {
|
|
53
|
+
const g = (await import(resolve(CWD, index))).default
|
|
54
|
+
return g
|
|
55
|
+
} catch (err) {
|
|
56
|
+
error = err
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
if (error) {
|
|
60
|
+
console.log(`\nerror: galbe instance import failed`)
|
|
61
|
+
console.log(error)
|
|
62
|
+
return process.exit(1)
|
|
63
|
+
}
|
|
64
|
+
let buildIndex: string = ''
|
|
65
|
+
try {
|
|
66
|
+
buildIndex = await createBuildIndex(index, g)
|
|
67
|
+
} catch (errors) {
|
|
68
|
+
console.log(`\nerror: build errors`)
|
|
69
|
+
for (let error of errors) console.log(error)
|
|
70
|
+
return process.exit(1)
|
|
71
|
+
}
|
|
72
|
+
if (!buildIndex) {
|
|
73
|
+
console.log(`\nerror: could not create build index`)
|
|
74
|
+
return process.exit(1)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const buildConfig: BuildConfig = {
|
|
78
|
+
publicPath: `${resolve(CWD, out)}/`,
|
|
79
|
+
...Object.fromEntries(Object.entries(bunfig).filter(([k, v]) => v)),
|
|
80
|
+
entrypoints: [buildIndex],
|
|
81
|
+
outdir: resolve(CWD, out),
|
|
82
|
+
target: 'bun'
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let bo = await Bun.build(buildConfig)
|
|
86
|
+
if (bo.success) process.stdout.write(' : \x1b[1;30m\x1b[32mdone\x1b[0m\n')
|
|
87
|
+
else {
|
|
88
|
+
console.log(`\nerror: build errors`)
|
|
89
|
+
console.log(...bo.logs)
|
|
90
|
+
}
|
|
91
|
+
if (compile) await $`bun build --compile ${resolve(CWD, out, 'index.js')} --outfile ${resolve(CWD, out, 'app')}`
|
|
92
|
+
|
|
93
|
+
await rm(dirname(buildIndex), { recursive: true })
|
|
94
|
+
})
|
|
95
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { $ } from 'bun'
|
|
2
|
+
import { Command, Option } from 'commander'
|
|
3
|
+
import { resolve } from 'path'
|
|
4
|
+
|
|
5
|
+
import { CWD, fmtInterval, fmtVal, instanciateRoutes, watchDir } from '../util'
|
|
6
|
+
import { Galbe } from '../../src'
|
|
7
|
+
|
|
8
|
+
const defaultPort = 3000
|
|
9
|
+
|
|
10
|
+
export default (cmd: Command) => {
|
|
11
|
+
cmd
|
|
12
|
+
.description('start a dev server running your \x1b[1;30m\x1b[36mGalbe\x1b[0m application')
|
|
13
|
+
.argument('<index>', 'index file')
|
|
14
|
+
.addOption(
|
|
15
|
+
new Option('-p, --port <number>', `port number ${fmtInterval(1, 65535)}`)
|
|
16
|
+
.argParser(v => {
|
|
17
|
+
if (parseInt(v) >= 1 && parseInt(v) <= 65535) return v
|
|
18
|
+
console.log(`error: port range must be between ${fmtInterval(1, 65535)}`)
|
|
19
|
+
process.exit(1)
|
|
20
|
+
})
|
|
21
|
+
.default(null, fmtVal(defaultPort))
|
|
22
|
+
)
|
|
23
|
+
.addOption(new Option('-w, --watch', 'watch file changes').default(false, fmtVal(false)))
|
|
24
|
+
.addOption(new Option('-nc, --noclear', "don't clear on file changes").default(false, fmtVal(false)))
|
|
25
|
+
.action(async (index, props) => {
|
|
26
|
+
const { port, watch, noclear } = props
|
|
27
|
+
const clear = !noclear
|
|
28
|
+
const indexPath = resolve(CWD, index)
|
|
29
|
+
let g: Galbe
|
|
30
|
+
|
|
31
|
+
Bun.env.BUN_ENV = 'development'
|
|
32
|
+
|
|
33
|
+
if (watch) {
|
|
34
|
+
await watchDir(
|
|
35
|
+
CWD,
|
|
36
|
+
async () => {
|
|
37
|
+
g.stop()
|
|
38
|
+
if (clear) await $`clear`
|
|
39
|
+
Loader.registry.clear()
|
|
40
|
+
g = (await import(indexPath)).default
|
|
41
|
+
await instanciateRoutes(g)
|
|
42
|
+
await g.listen(port)
|
|
43
|
+
},
|
|
44
|
+
{ ignore: /node_modules/ }
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (watch && clear) await $`clear`
|
|
49
|
+
g = (await import(indexPath)).default
|
|
50
|
+
await instanciateRoutes(g)
|
|
51
|
+
await g.listen(port)
|
|
52
|
+
})
|
|
53
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { $ } from 'bun'
|
|
2
|
+
import { devNull } from 'os'
|
|
3
|
+
import { Script, createContext } from 'vm'
|
|
4
|
+
import { Command, Option } from 'commander'
|
|
5
|
+
import { resolve, extname } from 'path'
|
|
6
|
+
import { rm } from 'fs/promises'
|
|
7
|
+
import { transformSync } from '@swc/core'
|
|
8
|
+
import { CWD, fmtList, instanciateRoutes, silentExec } from '../../util'
|
|
9
|
+
import { $T, Galbe, GalbeCLICommand, Method, Route } from '../../../src'
|
|
10
|
+
import { walkRoutes } from '../../../src/util'
|
|
11
|
+
import { schemaToTypeStr, Optional, STSchema } from '../../../src/schema'
|
|
12
|
+
|
|
13
|
+
const clientTargets = ['ts', 'js', 'cli']
|
|
14
|
+
|
|
15
|
+
export default (cmd: Command) => {
|
|
16
|
+
cmd
|
|
17
|
+
.description('generate a \x1b[1;30m\x1b[36mGalbe\x1b[0m client')
|
|
18
|
+
.argument('<index>', 'index file')
|
|
19
|
+
.addOption(
|
|
20
|
+
new Option('-o, --out <file>', 'output file').default(
|
|
21
|
+
null,
|
|
22
|
+
fmtList(['dist/client.ts', 'dist/client.js', 'dist/cli'])
|
|
23
|
+
)
|
|
24
|
+
)
|
|
25
|
+
.addOption(
|
|
26
|
+
new Option('-t, --target <target>', `build target ${fmtList(clientTargets)}`).argParser(v => {
|
|
27
|
+
if (clientTargets.includes(v)) return v
|
|
28
|
+
console.log(`error: target must be one of ${fmtList(clientTargets)}`)
|
|
29
|
+
process.exit(1)
|
|
30
|
+
})
|
|
31
|
+
)
|
|
32
|
+
.action(async (index, props) => {
|
|
33
|
+
let { target, out } = props
|
|
34
|
+
if (!target) target = clientTargets.includes(extname(index)?.slice(1)) ? extname(index)?.slice(1) : 'ts'
|
|
35
|
+
if (!out) out = { ts: 'dist/client.ts', js: 'dist/client.js', cli: 'dist/cli' }[target]
|
|
36
|
+
let pckg: any = {}
|
|
37
|
+
try {
|
|
38
|
+
pckg = await Bun.file(resolve(CWD, 'package.json')).json()
|
|
39
|
+
} catch (e) {}
|
|
40
|
+
|
|
41
|
+
let error = null
|
|
42
|
+
process.stdout.write('💻 \x1b[1;30mBuilding \x1b[36mGalbe\x1b[0m\x1b[1;30m client\x1b[0m')
|
|
43
|
+
let g: Galbe = await silentExec(async () => {
|
|
44
|
+
try {
|
|
45
|
+
const g = (await import(resolve(CWD, index))).default
|
|
46
|
+
await instanciateRoutes(g)
|
|
47
|
+
await g.init()
|
|
48
|
+
return g
|
|
49
|
+
} catch (err) {
|
|
50
|
+
error = err
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
if (error) {
|
|
54
|
+
console.log(`\nerror: galbe instance import failed`)
|
|
55
|
+
console.log(error)
|
|
56
|
+
return process.exit(1)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const routes: Record<Method, Route[]> = {
|
|
60
|
+
get: [],
|
|
61
|
+
post: [],
|
|
62
|
+
put: [],
|
|
63
|
+
patch: [],
|
|
64
|
+
delete: [],
|
|
65
|
+
options: [],
|
|
66
|
+
head: []
|
|
67
|
+
}
|
|
68
|
+
let commands: GalbeCLICommand[] = []
|
|
69
|
+
const metaRoutes = g.meta?.reduce(
|
|
70
|
+
(routes, c) => ({ ...routes, ...c.routes }),
|
|
71
|
+
{} as Record<string, Record<string, Record<string, any>>>
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
walkRoutes(g.router.routes, r => {
|
|
75
|
+
let meta = metaRoutes?.[r.path]?.[r.method]
|
|
76
|
+
let route = {
|
|
77
|
+
...r,
|
|
78
|
+
...(meta?.operationId ? { alias: meta?.operationId } : {}),
|
|
79
|
+
pathT: r.path.replaceAll(/:([^\/]+)/g, '${$1}'),
|
|
80
|
+
params:
|
|
81
|
+
Object.fromEntries(
|
|
82
|
+
[...r.path.matchAll(/:([^\/]+)/g)]?.map(m => [
|
|
83
|
+
m?.[1],
|
|
84
|
+
{
|
|
85
|
+
...(r.schema?.params?.[m?.[1]] ? { type: schemaToTypeStr(r.schema.params[m[1]]) } : {})
|
|
86
|
+
}
|
|
87
|
+
])
|
|
88
|
+
) || {},
|
|
89
|
+
schemas: {
|
|
90
|
+
...(r.schema.headers ? { headers: schemaToTypeStr($T.object(r.schema.headers)) } : {}),
|
|
91
|
+
...(r.schema.query ? { query: schemaToTypeStr($T.object(r.schema.query)) } : {}),
|
|
92
|
+
...(r.schema.body ? { body: schemaToTypeStr(r.schema.body) } : {}),
|
|
93
|
+
...(r.schema.response
|
|
94
|
+
? {
|
|
95
|
+
response: Object.fromEntries(
|
|
96
|
+
Object.entries(r.schema.response).map(([k, v]) => [k === 'default' ? 200 : k, schemaToTypeStr(v)])
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
: {})
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
routes[r.method.toLocaleLowerCase()].push(route)
|
|
103
|
+
if (target === 'cli' && meta?.operationId)
|
|
104
|
+
commands.push({
|
|
105
|
+
name: meta.operationId,
|
|
106
|
+
description: meta.head,
|
|
107
|
+
route,
|
|
108
|
+
arguments:
|
|
109
|
+
Object.entries((r.schema?.params || {}) as Record<string, STSchema>)?.map(([k, p]) => {
|
|
110
|
+
let type = schemaToTypeStr({ ...p, [Optional]: false })
|
|
111
|
+
return {
|
|
112
|
+
name: k,
|
|
113
|
+
type: type === 'boolean' ? '' : `<${type}>`,
|
|
114
|
+
description: p?.description || ''
|
|
115
|
+
}
|
|
116
|
+
}) || [],
|
|
117
|
+
options:
|
|
118
|
+
Object.entries((r.schema?.query || {}) as Record<string, STSchema>)?.map(([k, o]) => {
|
|
119
|
+
let type = schemaToTypeStr({ ...o, [Optional]: false })
|
|
120
|
+
return {
|
|
121
|
+
name: k,
|
|
122
|
+
short: k[0],
|
|
123
|
+
type: type === 'boolean' ? '' : `<${type}>`,
|
|
124
|
+
description: o?.description || '',
|
|
125
|
+
default: o.default
|
|
126
|
+
}
|
|
127
|
+
}) || []
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
if (target === 'js' || target === 'ts') {
|
|
132
|
+
const file = await Bun.file(resolve(import.meta.dir, '..', '..', 'res', 'client.template.ts')).text()
|
|
133
|
+
let filled = file.replaceAll(/\/\*\%([\s\S]*?)\%\*\//g, (_match, p) => {
|
|
134
|
+
let idt = p.match(/^\n*([ \t]*)/, p)?.[1] || ''
|
|
135
|
+
const script = new Script(p)
|
|
136
|
+
const sandbox = {
|
|
137
|
+
console,
|
|
138
|
+
version: pckg?.version || '0.1.0',
|
|
139
|
+
routes
|
|
140
|
+
}
|
|
141
|
+
createContext(sandbox)
|
|
142
|
+
let res = script.runInNewContext(sandbox)
|
|
143
|
+
if (typeof res === 'string') res = res.split('\n')
|
|
144
|
+
if (Array.isArray(res)) return res.map((s, i) => (i === 0 ? s : `${idt}${s}`)).join('\n')
|
|
145
|
+
return res ?? ''
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
if (target === 'js') {
|
|
149
|
+
filled = transformSync(filled, {
|
|
150
|
+
jsc: {
|
|
151
|
+
parser: {
|
|
152
|
+
syntax: 'typescript'
|
|
153
|
+
},
|
|
154
|
+
preserveAllComments: true,
|
|
155
|
+
target: 'esnext'
|
|
156
|
+
}
|
|
157
|
+
}).code
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
await Bun.write(out, filled)
|
|
161
|
+
} else if (target === 'cli') {
|
|
162
|
+
const file = await Bun.file(resolve(import.meta.dir, '..', '..', 'res', 'cli.template.js')).text()
|
|
163
|
+
|
|
164
|
+
// Plugin CLI hook
|
|
165
|
+
if (commands) for (let p of g.plugins) if (p.cli) await p.cli(commands)
|
|
166
|
+
|
|
167
|
+
let filled = file.replaceAll(/\/\*\%([\s\S]*?)\%\*\//g, (_match, p) => {
|
|
168
|
+
let idt = p.match(/^\n*([ \t]*)/, p)?.[1] || ''
|
|
169
|
+
const script = new Script(p)
|
|
170
|
+
const sandbox = {
|
|
171
|
+
console,
|
|
172
|
+
name: pckg?.name || 'Galbe app CLI',
|
|
173
|
+
description: pckg?.description || '',
|
|
174
|
+
version: pckg?.version || '0.1.0',
|
|
175
|
+
commands
|
|
176
|
+
}
|
|
177
|
+
createContext(sandbox)
|
|
178
|
+
let res = script.runInNewContext(sandbox)
|
|
179
|
+
if (typeof res === 'string') res = res.split('\n')
|
|
180
|
+
if (Array.isArray(res)) return res.map((s, i) => (i === 0 ? s : `${idt}${s}`)).join('\n')
|
|
181
|
+
return res ?? ''
|
|
182
|
+
})
|
|
183
|
+
const buildId = crypto.randomUUID()
|
|
184
|
+
const buildPath = resolve(CWD, '.galbe', 'client', `${buildId}.js`)
|
|
185
|
+
await Bun.write(buildPath, filled)
|
|
186
|
+
await $`bun build --compile ${buildPath} --outfile ${resolve(CWD, out)} > ${devNull} && printf "\u200B"`
|
|
187
|
+
await rm(resolve(CWD, '.galbe'), { recursive: true })
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
process.stdout.write(' : \x1b[1;30m\x1b[32mdone\x1b[0m\n')
|
|
191
|
+
})
|
|
192
|
+
}
|