@pikku/cli 0.7.0 → 0.7.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/CHANGELOG.md +9 -0
- package/bin/pikku-all.ts +203 -0
- package/bin/pikku-channels-map.ts +55 -0
- package/bin/pikku-channels.ts +63 -0
- package/bin/pikku-fetch.ts +55 -0
- package/bin/pikku-function-types.ts +84 -0
- package/bin/pikku-functions.ts +35 -0
- package/bin/pikku-http-map.ts +55 -0
- package/bin/pikku-http-routes.ts +63 -0
- package/bin/pikku-nextjs.test.ts +279 -0
- package/bin/pikku-nextjs.ts +152 -0
- package/bin/pikku-openapi.ts +74 -0
- package/bin/pikku-rpc.ts +22 -0
- package/bin/pikku-scheduler.ts +64 -0
- package/bin/pikku-schemas.ts +56 -0
- package/bin/pikku-websocket.ts +58 -0
- package/bin/pikku.ts +26 -0
- package/dist/bin/pikku-all.js +3 -0
- package/dist/bin/pikku-functions.d.ts +0 -2
- package/dist/bin/pikku-functions.js +2 -17
- package/dist/bin/pikku-rpc.d.ts +3 -0
- package/dist/bin/pikku-rpc.js +8 -0
- package/dist/bin/pikku.js +0 -0
- package/dist/src/pikku-cli-config.d.ts +1 -0
- package/dist/src/pikku-cli-config.js +3 -0
- package/dist/src/schema-generator.js +1 -2
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +3 -3
- package/src/inspector-glob.ts +28 -0
- package/src/openapi-spec-generator.ts +227 -0
- package/src/pikku-cli-config.ts +240 -0
- package/src/schema-generator.ts +136 -0
- package/{dist/src/events/http/serialize-fetch-wrapper.js → src/serialize-fetch-wrapper.ts} +4 -4
- package/src/serialize-import-map.ts +34 -0
- package/{dist/src/nextjs/serialize-nextjs-backend-wrapper.js → src/serialize-nextjs-backend-wrapper.ts} +11 -4
- package/{dist/src/nextjs/serialize-nextjs-http-wrapper.js → src/serialize-nextjs-http-wrapper.ts} +7 -4
- package/{dist/src/core/serialize-pikku-types.js → src/serialize-pikku-types.ts} +46 -35
- package/src/serialize-scheduler-meta.ts +18 -0
- package/src/serialize-typed-channel-map.ts +138 -0
- package/src/serialize-typed-function-map.ts +151 -0
- package/src/serialize-typed-http-map.ts +151 -0
- package/{dist/src/channels/serialize-websocket-wrapper.js → src/serialize-websocket-wrapper.ts} +4 -4
- package/src/utils.ts +284 -0
- package/tsconfig.json +21 -0
- package/dist/bin/pikku-http.d.ts +0 -5
- package/dist/bin/pikku-http.js +0 -27
- package/dist/bin/pikku-routes-map.d.ts +0 -5
- package/dist/bin/pikku-routes-map.js +0 -23
- package/dist/src/channels/serialize-channels.d.ts +0 -3
- package/dist/src/channels/serialize-channels.js +0 -19
- package/dist/src/channels/serialize-typed-channel-map.d.ts +0 -3
- package/dist/src/channels/serialize-typed-channel-map.js +0 -93
- package/dist/src/channels/serialize-websocket-wrapper.d.ts +0 -1
- package/dist/src/core/serialize-import-map.d.ts +0 -2
- package/dist/src/core/serialize-import-map.js +0 -24
- package/dist/src/core/serialize-pikku-types.d.ts +0 -4
- package/dist/src/events/channels/serialize-channels.d.ts +0 -3
- package/dist/src/events/channels/serialize-channels.js +0 -19
- package/dist/src/events/channels/serialize-typed-channel-map.d.ts +0 -3
- package/dist/src/events/channels/serialize-typed-channel-map.js +0 -90
- package/dist/src/events/channels/serialize-websocket-wrapper.d.ts +0 -1
- package/dist/src/events/channels/serialize-websocket-wrapper.js +0 -61
- package/dist/src/events/http/serialize-fetch-wrapper.d.ts +0 -1
- package/dist/src/events/http/serialize-route-imports.d.ts +0 -1
- package/dist/src/events/http/serialize-route-imports.js +0 -13
- package/dist/src/events/http/serialize-route-meta.d.ts +0 -2
- package/dist/src/events/http/serialize-route-meta.js +0 -6
- package/dist/src/events/http/serialize-typed-route-map.d.ts +0 -4
- package/dist/src/events/http/serialize-typed-route-map.js +0 -107
- package/dist/src/events/scheduler/serialize-schedulers.d.ts +0 -3
- package/dist/src/events/scheduler/serialize-schedulers.js +0 -23
- package/dist/src/http/serialize-fetch-wrapper.d.ts +0 -1
- package/dist/src/http/serialize-fetch-wrapper.js +0 -67
- package/dist/src/http/serialize-route-imports.d.ts +0 -1
- package/dist/src/http/serialize-route-imports.js +0 -13
- package/dist/src/http/serialize-route-meta.d.ts +0 -2
- package/dist/src/http/serialize-route-meta.js +0 -6
- package/dist/src/http/serialize-typed-route-map.d.ts +0 -4
- package/dist/src/http/serialize-typed-route-map.js +0 -107
- package/dist/src/nextjs/serialize-nextjs-backend-wrapper.d.ts +0 -1
- package/dist/src/nextjs/serialize-nextjs-http-wrapper.d.ts +0 -1
- package/dist/src/openapi/openapi-spec-generator.d.ts +0 -79
- package/dist/src/openapi/openapi-spec-generator.js +0 -136
- package/dist/src/scheduler/serialize-schedulers.d.ts +0 -3
- package/dist/src/scheduler/serialize-schedulers.js +0 -23
- package/dist/src/schema/schema-generator.d.ts +0 -5
- package/dist/src/schema/schema-generator.js +0 -89
- package/dist/src/serialize-typed-route-map.d.ts +0 -4
- package/dist/src/serialize-typed-route-map.js +0 -107
package/src/utils.ts
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { relative, dirname } from 'path'
|
|
2
|
+
import { PathToNameAndType, InspectorState } from '@pikku/inspector'
|
|
3
|
+
import { mkdir, writeFile } from 'fs/promises'
|
|
4
|
+
import chalk from 'chalk'
|
|
5
|
+
import { fileURLToPath } from 'url'
|
|
6
|
+
import { readFileSync } from 'fs'
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
9
|
+
|
|
10
|
+
export const logPrimary = (message: string) => {
|
|
11
|
+
console.log(chalk.green(message))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const logSuccess = (message: string) => {
|
|
15
|
+
console.log(chalk.green(message))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const logInfo = (message: string) => {
|
|
19
|
+
console.log(chalk.blue(message))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const getFileImportRelativePath = (
|
|
23
|
+
from: string,
|
|
24
|
+
to: string,
|
|
25
|
+
packageMappings: Record<string, string>
|
|
26
|
+
): string => {
|
|
27
|
+
let filePath = relative(dirname(from), to)
|
|
28
|
+
if (!/^\.+\//.test(filePath)) {
|
|
29
|
+
filePath = `./${filePath}`
|
|
30
|
+
}
|
|
31
|
+
// let usesPackageName = false
|
|
32
|
+
for (const [path, packageName] of Object.entries(packageMappings)) {
|
|
33
|
+
if (filePath.includes(path)) {
|
|
34
|
+
// usesPackageName = true
|
|
35
|
+
filePath = filePath.replace(new RegExp(`.*${path}`), packageName)
|
|
36
|
+
break
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// if (usesPackageName) {
|
|
40
|
+
// return filePath.replace('.ts', '')
|
|
41
|
+
// }
|
|
42
|
+
return filePath.replace('.ts', '.js')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface Meta {
|
|
46
|
+
file: string
|
|
47
|
+
variable: string
|
|
48
|
+
type: string
|
|
49
|
+
typePath: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type FilesAndMethods = {
|
|
53
|
+
userSessionType: Meta
|
|
54
|
+
sessionServicesType: Meta
|
|
55
|
+
singletonServicesType: Meta
|
|
56
|
+
pikkuConfigFactory: Meta
|
|
57
|
+
singletonServicesFactory: Meta
|
|
58
|
+
sessionServicesFactory: Meta
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface PikkuCLIOptions {
|
|
62
|
+
watch?: boolean
|
|
63
|
+
config?: string
|
|
64
|
+
configFileType?: string
|
|
65
|
+
userSessionType?: string
|
|
66
|
+
singletonServicesFactoryType?: string
|
|
67
|
+
sessionServicesFactoryType?: string
|
|
68
|
+
tags?: string[]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const getMetaTypes = (
|
|
72
|
+
type: string,
|
|
73
|
+
errors: Map<string, PathToNameAndType>,
|
|
74
|
+
map: PathToNameAndType,
|
|
75
|
+
desiredType?: string
|
|
76
|
+
) => {
|
|
77
|
+
if (desiredType) {
|
|
78
|
+
const entries = Object.entries(map)
|
|
79
|
+
for (const [file, meta] of entries) {
|
|
80
|
+
for (const { type, variable, typePath } of meta) {
|
|
81
|
+
if (type === desiredType) {
|
|
82
|
+
return { file, variable, type, typePath }
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
errors.set(`No ${desiredType} found that extends ${type}`, map)
|
|
87
|
+
return undefined
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const totalValues = Object.values(map).flat()
|
|
91
|
+
if (totalValues.length === 0) {
|
|
92
|
+
errors.set(`No ${type} found`, map)
|
|
93
|
+
} else if (totalValues.length > 1) {
|
|
94
|
+
errors.set(`More than one ${type} found`, map)
|
|
95
|
+
} else {
|
|
96
|
+
const entry = Object.entries(map)[0]
|
|
97
|
+
if (entry) {
|
|
98
|
+
const [file, [{ type, variable, typePath }]] = entry
|
|
99
|
+
return { file, type, variable, typePath }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return undefined
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export const getPikkuFilesAndMethods = async (
|
|
107
|
+
{
|
|
108
|
+
singletonServicesTypeImportMap,
|
|
109
|
+
sessionServicesTypeImportMap,
|
|
110
|
+
userSessionTypeImportMap,
|
|
111
|
+
sessionServicesFactories,
|
|
112
|
+
singletonServicesFactories,
|
|
113
|
+
configFactories,
|
|
114
|
+
}: InspectorState,
|
|
115
|
+
packageMappings: Record<string, string>,
|
|
116
|
+
outputFile: string,
|
|
117
|
+
{
|
|
118
|
+
configFileType,
|
|
119
|
+
singletonServicesFactoryType,
|
|
120
|
+
sessionServicesFactoryType,
|
|
121
|
+
}: PikkuCLIOptions,
|
|
122
|
+
requires: Partial<{
|
|
123
|
+
config: boolean
|
|
124
|
+
sessionServiceType: boolean
|
|
125
|
+
singletonServicesType: boolean
|
|
126
|
+
userSessionType: boolean
|
|
127
|
+
singletonServicesFactory: boolean
|
|
128
|
+
sessionServicesFactory: boolean
|
|
129
|
+
}> = {
|
|
130
|
+
config: false,
|
|
131
|
+
singletonServicesType: false,
|
|
132
|
+
sessionServiceType: false,
|
|
133
|
+
userSessionType: false,
|
|
134
|
+
singletonServicesFactory: false,
|
|
135
|
+
sessionServicesFactory: false,
|
|
136
|
+
}
|
|
137
|
+
): Promise<FilesAndMethods> => {
|
|
138
|
+
let errors = new Map<string, PathToNameAndType>()
|
|
139
|
+
|
|
140
|
+
const result: Partial<FilesAndMethods> = {
|
|
141
|
+
userSessionType: getMetaTypes(
|
|
142
|
+
'CoreUserSession',
|
|
143
|
+
requires.userSessionType ? errors : new Map(),
|
|
144
|
+
userSessionTypeImportMap,
|
|
145
|
+
configFileType
|
|
146
|
+
),
|
|
147
|
+
singletonServicesType: getMetaTypes(
|
|
148
|
+
'CoreSingletonServices',
|
|
149
|
+
requires.singletonServicesType ? errors : new Map(),
|
|
150
|
+
singletonServicesTypeImportMap
|
|
151
|
+
),
|
|
152
|
+
sessionServicesType: getMetaTypes(
|
|
153
|
+
'CoreServices',
|
|
154
|
+
requires.sessionServiceType ? errors : new Map(),
|
|
155
|
+
sessionServicesTypeImportMap
|
|
156
|
+
),
|
|
157
|
+
pikkuConfigFactory: getMetaTypes(
|
|
158
|
+
'CoreConfig',
|
|
159
|
+
requires.config ? errors : new Map(),
|
|
160
|
+
configFactories,
|
|
161
|
+
configFileType
|
|
162
|
+
),
|
|
163
|
+
singletonServicesFactory: getMetaTypes(
|
|
164
|
+
'CreateSingletonServices',
|
|
165
|
+
requires.singletonServicesFactory ? errors : new Map(),
|
|
166
|
+
singletonServicesFactories,
|
|
167
|
+
singletonServicesFactoryType
|
|
168
|
+
),
|
|
169
|
+
sessionServicesFactory: getMetaTypes(
|
|
170
|
+
'CreateSessionServices',
|
|
171
|
+
requires.sessionServicesFactory ? errors : new Map(),
|
|
172
|
+
sessionServicesFactories,
|
|
173
|
+
sessionServicesFactoryType
|
|
174
|
+
),
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (errors.size > 0) {
|
|
178
|
+
const result: string[] = ['Found errors:']
|
|
179
|
+
errors.forEach((filesAndMethods, message) => {
|
|
180
|
+
result.push(`- ${message}`)
|
|
181
|
+
for (const [file, methods] of Object.entries(filesAndMethods)) {
|
|
182
|
+
result.push(
|
|
183
|
+
`\t* file: ${getFileImportRelativePath(outputFile, file, packageMappings)}`
|
|
184
|
+
)
|
|
185
|
+
result.push(
|
|
186
|
+
`\t* methods: ${methods.map(({ variable, type }) => `${variable}: ${type}`).join(', ')}`
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
console.error(result.join('\n'))
|
|
192
|
+
process.exit(1)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return result as FilesAndMethods
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export const writeFileInDir = async (
|
|
199
|
+
path: string,
|
|
200
|
+
content: string,
|
|
201
|
+
ignoreModifyComment: boolean = false
|
|
202
|
+
) => {
|
|
203
|
+
if (content.includes('server-only')) {
|
|
204
|
+
content = content.replace(
|
|
205
|
+
"'server-only'",
|
|
206
|
+
`'server-only'\n\n${ignoreModifyComment ? '' : DO_NOT_MODIFY_COMMENT}`
|
|
207
|
+
)
|
|
208
|
+
} else {
|
|
209
|
+
content = `${ignoreModifyComment ? '' : DO_NOT_MODIFY_COMMENT}${content}`
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
await mkdir(dirname(path), { recursive: true })
|
|
213
|
+
await writeFile(path, content, 'utf-8')
|
|
214
|
+
logSuccess(`✓ File written to ${path}`)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export const logCommandInfoAndTime = async (
|
|
218
|
+
commandStart: string,
|
|
219
|
+
commandEnd: string,
|
|
220
|
+
[skipCondition, skipMessage = 'none found']: [boolean] | [boolean, string],
|
|
221
|
+
callback: (...args: any[]) => Promise<unknown>
|
|
222
|
+
): Promise<boolean> => {
|
|
223
|
+
if (skipCondition === true) {
|
|
224
|
+
logInfo(
|
|
225
|
+
`• Skipping ${commandStart.charAt(0).toLocaleLowerCase()}${commandStart.slice(1)} since ${skipMessage}.`
|
|
226
|
+
)
|
|
227
|
+
return false
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const start = Date.now()
|
|
231
|
+
chalk.blue(`• ${commandStart}...`)
|
|
232
|
+
await callback()
|
|
233
|
+
|
|
234
|
+
logSuccess(`✓ ${commandEnd} in ${Date.now() - start}ms.`)
|
|
235
|
+
return true
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const logo = `
|
|
239
|
+
______ _ _ _
|
|
240
|
+
(_____ (_) | | |
|
|
241
|
+
_____) )| | _| | _ _ _
|
|
242
|
+
| ____/ | |_/ ) |_/ ) | | |
|
|
243
|
+
| | | | _ (| _ (| |_| |
|
|
244
|
+
|_| |_|_| \_)_| \_)____/
|
|
245
|
+
`
|
|
246
|
+
|
|
247
|
+
export const logPikkuLogo = () => {
|
|
248
|
+
logPrimary(logo)
|
|
249
|
+
|
|
250
|
+
const packageJson = JSON.parse(
|
|
251
|
+
readFileSync(`${dirname(__filename)}/../../package.json`, 'utf-8')
|
|
252
|
+
)
|
|
253
|
+
logPrimary(`⚙️ Welcome to the Pikku CLI (v${packageJson.version})\n`)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// TODO: add version back in once the ESM dust settles
|
|
257
|
+
export const DO_NOT_MODIFY_COMMENT = `/**
|
|
258
|
+
* This file was generated by the @pikku/cli
|
|
259
|
+
*/
|
|
260
|
+
`
|
|
261
|
+
|
|
262
|
+
export const serializeFileImports = (
|
|
263
|
+
importType: string,
|
|
264
|
+
outputPath: string,
|
|
265
|
+
files: Set<string>,
|
|
266
|
+
packageMappings: Record<string, string> = {}
|
|
267
|
+
) => {
|
|
268
|
+
const serializedOutput: string[] = [
|
|
269
|
+
`/* The files with an ${importType} function call */`,
|
|
270
|
+
]
|
|
271
|
+
|
|
272
|
+
Array.from(files)
|
|
273
|
+
.sort()
|
|
274
|
+
.forEach((path) => {
|
|
275
|
+
const filePath = getFileImportRelativePath(
|
|
276
|
+
outputPath,
|
|
277
|
+
path,
|
|
278
|
+
packageMappings
|
|
279
|
+
)
|
|
280
|
+
serializedOutput.push(`import '${filePath}'`)
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
return serializedOutput.join('\n')
|
|
284
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"rootDir": ".",
|
|
5
|
+
"module": "Node16",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"target": "esnext",
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"resolveJsonModule": true
|
|
10
|
+
},
|
|
11
|
+
"include": ["bin/**/*.ts", "src/**/*.ts"],
|
|
12
|
+
"exclude": ["**/*.test.ts", "node_modules", "bin/dist"],
|
|
13
|
+
"references": [
|
|
14
|
+
{
|
|
15
|
+
"path": "../core/tsconfig.json"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "../inspector/tsconfig.json"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
package/dist/bin/pikku-http.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { PikkuCLIConfig } from '../src/pikku-cli-config.js';
|
|
3
|
-
import { InspectorState } from '@pikku/inspector';
|
|
4
|
-
export declare const pikkuHTTP: (cliConfig: PikkuCLIConfig, visitState: InspectorState) => Promise<boolean>;
|
|
5
|
-
export declare const routes: (program: Command) => void;
|
package/dist/bin/pikku-http.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { getPikkuCLIConfig } from '../src/pikku-cli-config.js';
|
|
2
|
-
import { logCommandInfoAndTime, logPikkuLogo, serializeFileImports, writeFileInDir, } from '../src/utils.js';
|
|
3
|
-
import { inspectorGlob } from '../src/inspector-glob.js';
|
|
4
|
-
export const pikkuHTTP = async (cliConfig, visitState) => {
|
|
5
|
-
return await logCommandInfoAndTime('Finding HTTP routes', 'Found HTTP routes', [visitState.http.files.size === 0], async () => {
|
|
6
|
-
const { routesFile, packageMappings } = cliConfig;
|
|
7
|
-
const { http } = visitState;
|
|
8
|
-
const content = [
|
|
9
|
-
serializeFileImports('addHTTPRoute', routesFile, http.files, packageMappings),
|
|
10
|
-
`import { pikkuState } from '@pikku/core'\npikkuState('http', 'meta', ${JSON.stringify(http.meta, null, 2)})`
|
|
11
|
-
];
|
|
12
|
-
await writeFileInDir(routesFile, content.join('\n\n'));
|
|
13
|
-
});
|
|
14
|
-
};
|
|
15
|
-
async function action(cliOptions) {
|
|
16
|
-
logPikkuLogo();
|
|
17
|
-
const cliConfig = await getPikkuCLIConfig(cliOptions.config, ['rootDir', 'routeDirectories', 'routesFile'], cliOptions.tags);
|
|
18
|
-
const visitState = await inspectorGlob(cliConfig.rootDir, cliConfig.routeDirectories, cliConfig.filters);
|
|
19
|
-
await pikkuHTTP(cliConfig, visitState);
|
|
20
|
-
}
|
|
21
|
-
export const routes = (program) => {
|
|
22
|
-
program
|
|
23
|
-
.command('routes')
|
|
24
|
-
.description('Find all routes to import')
|
|
25
|
-
.option('-c | --config <string>', 'The path to pikku cli config file')
|
|
26
|
-
.action(action);
|
|
27
|
-
};
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { PikkuCLIConfig } from '../src/pikku-cli-config.js';
|
|
3
|
-
import { InspectorState } from '@pikku/inspector';
|
|
4
|
-
export declare const pikkuHTTPMap: ({ routesMapDeclarationFile, packageMappings }: PikkuCLIConfig, { http, functions }: InspectorState) => Promise<boolean>;
|
|
5
|
-
export declare const routesMap: (program: Command) => void;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { getPikkuCLIConfig } from '../src/pikku-cli-config.js';
|
|
2
|
-
import { logCommandInfoAndTime, logPikkuLogo, writeFileInDir, } from '../src/utils.js';
|
|
3
|
-
import { serializeTypedRoutesMap } from '../src/serialize-typed-route-map.js';
|
|
4
|
-
import { inspectorGlob } from '../src/inspector-glob.js';
|
|
5
|
-
export const pikkuHTTPMap = async ({ routesMapDeclarationFile, packageMappings }, { http, functions }) => {
|
|
6
|
-
return await logCommandInfoAndTime('Creating routes map', 'Created routes map', [http.files.size === 0], async () => {
|
|
7
|
-
const content = serializeTypedRoutesMap(routesMapDeclarationFile, packageMappings, functions.typesMap, http.meta, http.metaInputTypes);
|
|
8
|
-
await writeFileInDir(routesMapDeclarationFile, content);
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
async function action(cliOptions) {
|
|
12
|
-
logPikkuLogo();
|
|
13
|
-
const cliConfig = await getPikkuCLIConfig(cliOptions.config, ['rootDir', 'routeDirectories', 'routesFile'], cliOptions.tags);
|
|
14
|
-
const visitState = await inspectorGlob(cliConfig.rootDir, cliConfig.routeDirectories, cliConfig.filters);
|
|
15
|
-
await pikkuHTTPMap(cliConfig, visitState);
|
|
16
|
-
}
|
|
17
|
-
export const routesMap = (program) => {
|
|
18
|
-
program
|
|
19
|
-
.command('map')
|
|
20
|
-
.description('Generate a map of all routes to aid in type checking')
|
|
21
|
-
.option('-c | --config <string>', 'The path to pikku cli config file')
|
|
22
|
-
.action(action);
|
|
23
|
-
};
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import { ChannelsMeta } from '@pikku/core/channel';
|
|
2
|
-
export declare const serializeChannels: (outputPath: string, filesWithChannels: Set<string>, packageMappings?: Record<string, string>) => string;
|
|
3
|
-
export declare const serializeChannelMeta: (channelsMeta: ChannelsMeta) => string;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { getFileImportRelativePath } from '../utils.js';
|
|
2
|
-
export const serializeChannels = (outputPath, filesWithChannels, packageMappings = {}) => {
|
|
3
|
-
const serializedOutput = [
|
|
4
|
-
'/* The files with an addChannel function call */',
|
|
5
|
-
];
|
|
6
|
-
Array.from(filesWithChannels)
|
|
7
|
-
.sort()
|
|
8
|
-
.forEach((path) => {
|
|
9
|
-
const filePath = getFileImportRelativePath(outputPath, path, packageMappings);
|
|
10
|
-
serializedOutput.push(`import '${filePath}'`);
|
|
11
|
-
});
|
|
12
|
-
return serializedOutput.join('\n');
|
|
13
|
-
};
|
|
14
|
-
export const serializeChannelMeta = (channelsMeta) => {
|
|
15
|
-
const serializedOutput = [];
|
|
16
|
-
serializedOutput.push("import { pikkuState } from '@pikku/core'");
|
|
17
|
-
serializedOutput.push(`pikkuState('channel', 'meta', ${JSON.stringify(channelsMeta, null, 2)})`);
|
|
18
|
-
return serializedOutput.join('\n');
|
|
19
|
-
};
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { serializeImportMap } from '../core/serialize-import-map.js';
|
|
2
|
-
import { generateCustomTypes } from '../http/serialize-typed-route-map.js';
|
|
3
|
-
export const serializeTypedChannelsMap = (relativeToPath, packageMappings, typesMap, channelsMeta) => {
|
|
4
|
-
const { channels, requiredTypes } = generateChannels(channelsMeta);
|
|
5
|
-
typesMap.customTypes.forEach(({ references }) => {
|
|
6
|
-
for (const reference of references) {
|
|
7
|
-
requiredTypes.add(reference);
|
|
8
|
-
}
|
|
9
|
-
});
|
|
10
|
-
const imports = serializeImportMap(relativeToPath, packageMappings, typesMap, requiredTypes);
|
|
11
|
-
const serializedCustomTypes = generateCustomTypes(typesMap, requiredTypes);
|
|
12
|
-
return `/**
|
|
13
|
-
* This provides the structure needed for TypeScript to be aware of channels
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
${imports}
|
|
17
|
-
${serializedCustomTypes}
|
|
18
|
-
|
|
19
|
-
interface ChannelHandler<I, O> {
|
|
20
|
-
input: I;
|
|
21
|
-
output: O;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
${channels}
|
|
25
|
-
|
|
26
|
-
export type ChannelDefaultHandlerOf<Channel extends keyof ChannelsMap> =
|
|
27
|
-
ChannelsMap[Channel]['defaultMessage'] extends { input: infer I; output: infer O }
|
|
28
|
-
? ChannelHandler<I, O>
|
|
29
|
-
: never;
|
|
30
|
-
|
|
31
|
-
export type ChannelRouteHandlerOf<
|
|
32
|
-
Channel extends keyof ChannelsMap,
|
|
33
|
-
Route extends keyof ChannelsMap[Channel]['routes'],
|
|
34
|
-
Method extends keyof ChannelsMap[Channel]['routes'][Route],
|
|
35
|
-
> =
|
|
36
|
-
ChannelsMap[Channel]['routes'][Route][Method] extends { input: infer I; output: infer O }
|
|
37
|
-
? ChannelHandler<I, O>
|
|
38
|
-
: never;
|
|
39
|
-
`;
|
|
40
|
-
};
|
|
41
|
-
function generateChannels(channelsMeta) {
|
|
42
|
-
const requiredTypes = new Set();
|
|
43
|
-
const channelsObject = {};
|
|
44
|
-
for (const meta of Object.values(channelsMeta)) {
|
|
45
|
-
const { name, messageRoutes, message } = meta;
|
|
46
|
-
if (!channelsObject[name]) {
|
|
47
|
-
channelsObject[name] = { message, routes: {} };
|
|
48
|
-
}
|
|
49
|
-
for (const [key, route] of Object.entries(messageRoutes)) {
|
|
50
|
-
if (!channelsObject[name].routes[key]) {
|
|
51
|
-
channelsObject[name].routes[key] = {};
|
|
52
|
-
}
|
|
53
|
-
for (const [method, { inputs, outputs }] of Object.entries(route)) {
|
|
54
|
-
const inputTypes = inputs || null;
|
|
55
|
-
const outputTypes = outputs || null;
|
|
56
|
-
channelsObject[name].routes[key][method] = {
|
|
57
|
-
inputTypes,
|
|
58
|
-
outputTypes,
|
|
59
|
-
};
|
|
60
|
-
inputTypes?.forEach((type) => requiredTypes.add(type));
|
|
61
|
-
outputTypes?.forEach((type) => requiredTypes.add(type));
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
let routesStr = 'export type ChannelsMap = {\n';
|
|
66
|
-
for (const [channelPath, { routes, message }] of Object.entries(channelsObject)) {
|
|
67
|
-
routesStr += ` readonly '${channelPath}': {\n`;
|
|
68
|
-
// Add `routes` object
|
|
69
|
-
routesStr += ` readonly routes: {\n`;
|
|
70
|
-
for (const [key, methods] of Object.entries(routes)) {
|
|
71
|
-
routesStr += ` readonly ${key}: {\n`;
|
|
72
|
-
for (const [method, handler] of Object.entries(methods)) {
|
|
73
|
-
routesStr += ` readonly ${method}: ChannelHandler<${formatTypeArray(handler.inputTypes) || 'void'}, ${formatTypeArray(handler.outputTypes) || 'never'}>,\n`;
|
|
74
|
-
}
|
|
75
|
-
routesStr += ' },\n';
|
|
76
|
-
}
|
|
77
|
-
routesStr += ' },\n';
|
|
78
|
-
// Add `defaultMessage` outside `routes`
|
|
79
|
-
if (message) {
|
|
80
|
-
routesStr += ` readonly defaultMessage: ChannelHandler<${formatTypeArray(message.inputs)}, ${formatTypeArray(message.outputs)}>,\n`;
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
routesStr += ` readonly defaultMessage: never,\n`;
|
|
84
|
-
}
|
|
85
|
-
routesStr += ' },\n';
|
|
86
|
-
}
|
|
87
|
-
routesStr += '};';
|
|
88
|
-
return { channels: routesStr, requiredTypes };
|
|
89
|
-
}
|
|
90
|
-
// Utility to format type arrays
|
|
91
|
-
function formatTypeArray(types) {
|
|
92
|
-
return types ? types.join(' | ') : 'null';
|
|
93
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const serializeWebsocketWrapper: (channelsMapPath: string) => string;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { getFileImportRelativePath } from '../utils.js';
|
|
2
|
-
export const serializeImportMap = (relativeToPath, packageMappings, typesMap, requiredTypes) => {
|
|
3
|
-
const paths = new Map();
|
|
4
|
-
Array.from(requiredTypes).forEach((requiredType) => {
|
|
5
|
-
const { originalName, uniqueName, path } = typesMap.getTypeMeta(requiredType);
|
|
6
|
-
if (!path) {
|
|
7
|
-
// This is a custom type that exists in file, so we don't need to import it
|
|
8
|
-
return;
|
|
9
|
-
}
|
|
10
|
-
const variables = paths.get(path) || [];
|
|
11
|
-
if (originalName === uniqueName) {
|
|
12
|
-
variables.push(originalName);
|
|
13
|
-
}
|
|
14
|
-
else {
|
|
15
|
-
variables.push(`${originalName} as ${uniqueName}`);
|
|
16
|
-
}
|
|
17
|
-
paths.set(path, variables);
|
|
18
|
-
});
|
|
19
|
-
const imports = [];
|
|
20
|
-
for (const [path, variables] of paths.entries()) {
|
|
21
|
-
imports.push(`import type { ${variables.join(', ')} } from '${getFileImportRelativePath(relativeToPath, path, packageMappings)}'`);
|
|
22
|
-
}
|
|
23
|
-
return imports.join('\n');
|
|
24
|
-
};
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import { ChannelsMeta } from '@pikku/core/channel';
|
|
2
|
-
export declare const serializeChannels: (outputPath: string, filesWithChannels: Set<string>, packageMappings?: Record<string, string>) => string;
|
|
3
|
-
export declare const serializeChannelMeta: (channelsMeta: ChannelsMeta) => string;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { getFileImportRelativePath } from '../utils.js';
|
|
2
|
-
export const serializeChannels = (outputPath, filesWithChannels, packageMappings = {}) => {
|
|
3
|
-
const serializedOutput = [
|
|
4
|
-
'/* The files with an addChannel function call */',
|
|
5
|
-
];
|
|
6
|
-
Array.from(filesWithChannels)
|
|
7
|
-
.sort()
|
|
8
|
-
.forEach((path) => {
|
|
9
|
-
const filePath = getFileImportRelativePath(outputPath, path, packageMappings);
|
|
10
|
-
serializedOutput.push(`import '${filePath}'`);
|
|
11
|
-
});
|
|
12
|
-
return serializedOutput.join('\n');
|
|
13
|
-
};
|
|
14
|
-
export const serializeChannelMeta = (channelsMeta) => {
|
|
15
|
-
const serializedOutput = [];
|
|
16
|
-
serializedOutput.push("import { pikkuState } from '@pikku/core'");
|
|
17
|
-
serializedOutput.push(`pikkuState('channel', 'meta', ${JSON.stringify(channelsMeta, null, 2)})`);
|
|
18
|
-
return serializedOutput.join('\n');
|
|
19
|
-
};
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { serializeImportMap } from '../core/serialize-import-map.js';
|
|
2
|
-
import { generateCustomTypes } from '../http/serialize-typed-route-map.js';
|
|
3
|
-
export const serializeTypedChannelsMap = (relativeToPath, packageMappings, typesMap, channelsMeta) => {
|
|
4
|
-
const { channels, requiredTypes } = generateChannels(channelsMeta);
|
|
5
|
-
typesMap.customTypes.forEach(({ references }) => {
|
|
6
|
-
for (const reference of references) {
|
|
7
|
-
requiredTypes.add(reference);
|
|
8
|
-
}
|
|
9
|
-
});
|
|
10
|
-
const imports = serializeImportMap(relativeToPath, packageMappings, typesMap, requiredTypes);
|
|
11
|
-
const serializedCustomTypes = generateCustomTypes(typesMap, requiredTypes);
|
|
12
|
-
return `/**
|
|
13
|
-
* This provides the structure needed for TypeScript to be aware of channels
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
${imports}
|
|
17
|
-
${serializedCustomTypes}
|
|
18
|
-
|
|
19
|
-
interface ChannelHandler<I, O> {
|
|
20
|
-
input: I;
|
|
21
|
-
output: O;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
${channels}
|
|
25
|
-
|
|
26
|
-
export type ChannelDefaultHandlerOf<Channel extends keyof ChannelsMap> =
|
|
27
|
-
ChannelsMap[Channel]['defaultMessage'] extends { input: infer I; output: infer O }
|
|
28
|
-
? ChannelHandler<I, O>
|
|
29
|
-
: never;
|
|
30
|
-
|
|
31
|
-
export type ChannelRouteHandlerOf<
|
|
32
|
-
Channel extends keyof ChannelsMap,
|
|
33
|
-
Route extends keyof ChannelsMap[Channel]['routes'],
|
|
34
|
-
Method extends keyof ChannelsMap[Channel]['routes'][Route],
|
|
35
|
-
> =
|
|
36
|
-
ChannelsMap[Channel]['routes'][Route][Method] extends { input: infer I; output: infer O }
|
|
37
|
-
? ChannelHandler<I, O>
|
|
38
|
-
: never;
|
|
39
|
-
`;
|
|
40
|
-
};
|
|
41
|
-
function generateChannels(channelsMeta) {
|
|
42
|
-
const requiredTypes = new Set();
|
|
43
|
-
const channelsObject = {};
|
|
44
|
-
for (const meta of channelsMeta) {
|
|
45
|
-
const { name, messageRoutes, message } = meta;
|
|
46
|
-
if (!channelsObject[name]) {
|
|
47
|
-
channelsObject[name] = { message, routes: {} };
|
|
48
|
-
}
|
|
49
|
-
for (const [key, route] of Object.entries(messageRoutes)) {
|
|
50
|
-
if (!channelsObject[name].routes[key]) {
|
|
51
|
-
channelsObject[name].routes[key] = {};
|
|
52
|
-
}
|
|
53
|
-
for (const [method, { inputs, outputs }] of Object.entries(route)) {
|
|
54
|
-
const inputTypes = inputs || null;
|
|
55
|
-
const outputTypes = outputs || null;
|
|
56
|
-
channelsObject[name].routes[key][method] = {
|
|
57
|
-
inputTypes,
|
|
58
|
-
outputTypes,
|
|
59
|
-
};
|
|
60
|
-
inputTypes?.forEach((type) => requiredTypes.add(type));
|
|
61
|
-
outputTypes?.forEach((type) => requiredTypes.add(type));
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
let routesStr = 'export type ChannelsMap = {\n';
|
|
66
|
-
for (const [channelPath, { routes, message }] of Object.entries(channelsObject)) {
|
|
67
|
-
routesStr += ` readonly '${channelPath}': {\n`;
|
|
68
|
-
// Add `routes` object
|
|
69
|
-
routesStr += ` readonly routes: {\n`;
|
|
70
|
-
for (const [key, methods] of Object.entries(routes)) {
|
|
71
|
-
routesStr += ` readonly ${key}: {\n`;
|
|
72
|
-
for (const [method, handler] of Object.entries(methods)) {
|
|
73
|
-
routesStr += ` readonly ${method}: ChannelHandler<${formatTypeArray(handler.inputTypes) || 'void'}, ${formatTypeArray(handler.outputTypes) || 'never'}>,\n`;
|
|
74
|
-
}
|
|
75
|
-
routesStr += ' },\n';
|
|
76
|
-
}
|
|
77
|
-
routesStr += ' },\n';
|
|
78
|
-
// Add `defaultMessage` outside `routes`
|
|
79
|
-
if (message) {
|
|
80
|
-
routesStr += ` readonly defaultMessage: ChannelHandler<${formatTypeArray(message.inputs)}, ${formatTypeArray(message.outputs)}>,\n`;
|
|
81
|
-
}
|
|
82
|
-
routesStr += ' },\n';
|
|
83
|
-
}
|
|
84
|
-
routesStr += '};';
|
|
85
|
-
return { channels: routesStr, requiredTypes };
|
|
86
|
-
}
|
|
87
|
-
// Utility to format type arrays
|
|
88
|
-
function formatTypeArray(types) {
|
|
89
|
-
return types ? types.join(' | ') : 'null';
|
|
90
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const serializeWebsocketWrapper: (channelsMapPath: string) => string;
|