@pikku/cli 0.6.6
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 +445 -0
- package/README.md +3 -0
- package/bin/pikku-all.ts +110 -0
- package/bin/pikku-channels-map.ts +53 -0
- package/bin/pikku-channels.ts +57 -0
- package/bin/pikku-fetch.ts +54 -0
- package/bin/pikku-function-types.ts +76 -0
- package/bin/pikku-nextjs.test.ts +279 -0
- package/bin/pikku-nextjs.ts +113 -0
- package/bin/pikku-openapi.ts +71 -0
- package/bin/pikku-routes-map.ts +54 -0
- package/bin/pikku-routes.ts +55 -0
- package/bin/pikku-scheduler.ts +61 -0
- package/bin/pikku-schemas.ts +51 -0
- package/bin/pikku-websocket.ts +54 -0
- package/bin/pikku.ts +26 -0
- package/cli.schema.json +212 -0
- package/dist/bin/pikku-all.d.ts +4 -0
- package/dist/bin/pikku-all.js +71 -0
- package/dist/bin/pikku-channels-map.d.ts +5 -0
- package/dist/bin/pikku-channels-map.js +27 -0
- package/dist/bin/pikku-channels.d.ts +5 -0
- package/dist/bin/pikku-channels.js +32 -0
- package/dist/bin/pikku-fetch.d.ts +6 -0
- package/dist/bin/pikku-fetch.js +25 -0
- package/dist/bin/pikku-function-types.d.ts +6 -0
- package/dist/bin/pikku-function-types.js +32 -0
- package/dist/bin/pikku-nextjs.d.ts +7 -0
- package/dist/bin/pikku-nextjs.js +40 -0
- package/dist/bin/pikku-openapi.d.ts +5 -0
- package/dist/bin/pikku-openapi.js +41 -0
- package/dist/bin/pikku-routes-map.d.ts +5 -0
- package/dist/bin/pikku-routes-map.js +27 -0
- package/dist/bin/pikku-routes.d.ts +5 -0
- package/dist/bin/pikku-routes.js +33 -0
- package/dist/bin/pikku-scheduler.d.ts +5 -0
- package/dist/bin/pikku-scheduler.js +32 -0
- package/dist/bin/pikku-schemas.d.ts +5 -0
- package/dist/bin/pikku-schemas.js +27 -0
- package/dist/bin/pikku-websocket.d.ts +6 -0
- package/dist/bin/pikku-websocket.js +25 -0
- package/dist/bin/pikku.d.ts +2 -0
- package/dist/bin/pikku.js +23 -0
- package/dist/src/channels/serialize-channels.d.ts +3 -0
- package/dist/src/channels/serialize-channels.js +19 -0
- package/dist/src/channels/serialize-typed-channel-map.d.ts +3 -0
- package/dist/src/channels/serialize-typed-channel-map.js +87 -0
- package/dist/src/channels/serialize-websocket-wrapper.d.ts +1 -0
- package/dist/src/channels/serialize-websocket-wrapper.js +61 -0
- package/dist/src/core/serialize-import-map.d.ts +2 -0
- package/dist/src/core/serialize-import-map.js +23 -0
- package/dist/src/core/serialize-pikku-types.d.ts +4 -0
- package/dist/src/core/serialize-pikku-types.js +48 -0
- package/dist/src/http/serialize-fetch-wrapper.d.ts +1 -0
- package/dist/src/http/serialize-fetch-wrapper.js +57 -0
- package/dist/src/http/serialize-route-imports.d.ts +1 -0
- package/dist/src/http/serialize-route-imports.js +13 -0
- package/dist/src/http/serialize-route-meta.d.ts +2 -0
- package/dist/src/http/serialize-route-meta.js +6 -0
- package/dist/src/http/serialize-typed-route-map.d.ts +3 -0
- package/dist/src/http/serialize-typed-route-map.js +107 -0
- package/dist/src/inspector-glob.d.ts +2 -0
- package/dist/src/inspector-glob.js +12 -0
- package/dist/src/nextjs/serialize-nextjs-wrapper.d.ts +1 -0
- package/dist/src/nextjs/serialize-nextjs-wrapper.js +149 -0
- package/dist/src/openapi/openapi-spec-generator.d.ts +79 -0
- package/dist/src/openapi/openapi-spec-generator.js +135 -0
- package/dist/src/pikku-cli-config.d.ts +31 -0
- package/dist/src/pikku-cli-config.js +113 -0
- package/dist/src/scheduler/serialize-schedulers.d.ts +3 -0
- package/dist/src/scheduler/serialize-schedulers.js +22 -0
- package/dist/src/schema/schema-generator.d.ts +5 -0
- package/dist/src/schema/schema-generator.js +79 -0
- package/dist/src/utils.d.ts +34 -0
- package/dist/src/utils.js +109 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +42 -0
- package/run-tests.sh +53 -0
- package/src/channels/serialize-channels.ts +34 -0
- package/src/channels/serialize-typed-channel-map.ts +133 -0
- package/src/channels/serialize-websocket-wrapper.ts +61 -0
- package/src/core/serialize-import-map.ts +33 -0
- package/src/core/serialize-pikku-types.ts +53 -0
- package/src/http/serialize-fetch-wrapper.ts +57 -0
- package/src/http/serialize-route-imports.ts +24 -0
- package/src/http/serialize-route-meta.ts +10 -0
- package/src/http/serialize-typed-route-map.ts +147 -0
- package/src/inspector-glob.ts +27 -0
- package/src/nextjs/serialize-nextjs-wrapper.ts +156 -0
- package/src/openapi/openapi-spec-generator.ts +229 -0
- package/src/pikku-cli-config.ts +189 -0
- package/src/scheduler/serialize-schedulers.ts +43 -0
- package/src/schema/schema-generator.ts +117 -0
- package/src/utils.ts +219 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { join, dirname, resolve, isAbsolute } from 'path'
|
|
2
|
+
import { readdir, readFile } from 'fs/promises'
|
|
3
|
+
import { OpenAPISpecInfo } from './openapi/openapi-spec-generator.js'
|
|
4
|
+
|
|
5
|
+
export interface PikkuCLICoreOutputFiles {
|
|
6
|
+
outDir?: string
|
|
7
|
+
routesFile: string
|
|
8
|
+
channelsFile: string
|
|
9
|
+
schedulersFile: string
|
|
10
|
+
schemaDirectory: string
|
|
11
|
+
typesDeclarationFile: string
|
|
12
|
+
routesMapDeclarationFile: string
|
|
13
|
+
channelsMapDeclarationFile: string
|
|
14
|
+
bootstrapFile: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type PikkuCLIConfig = {
|
|
18
|
+
$schema?: string
|
|
19
|
+
|
|
20
|
+
extends?: string
|
|
21
|
+
|
|
22
|
+
rootDir: string
|
|
23
|
+
routeDirectories: string[]
|
|
24
|
+
packageMappings: Record<string, string>
|
|
25
|
+
supportsImportAttributes: boolean
|
|
26
|
+
|
|
27
|
+
configDir: string
|
|
28
|
+
tsconfig: string
|
|
29
|
+
|
|
30
|
+
nextJSfile?: string
|
|
31
|
+
fetchFile?: string
|
|
32
|
+
websocketFile?: string
|
|
33
|
+
|
|
34
|
+
openAPI?: {
|
|
35
|
+
outputFile: string
|
|
36
|
+
additionalInfo: OpenAPISpecInfo
|
|
37
|
+
}
|
|
38
|
+
} & PikkuCLICoreOutputFiles
|
|
39
|
+
|
|
40
|
+
const CONFIG_DIR_FILES = ['nextJSfile', 'fetchFile', 'websocketFile']
|
|
41
|
+
|
|
42
|
+
export const getPikkuCLIConfig = async (
|
|
43
|
+
configFile: string | undefined = undefined,
|
|
44
|
+
requiredFields: Array<keyof PikkuCLIConfig>,
|
|
45
|
+
exitProcess: boolean = false
|
|
46
|
+
): Promise<PikkuCLIConfig> => {
|
|
47
|
+
const config = await _getPikkuCLIConfig(
|
|
48
|
+
configFile,
|
|
49
|
+
requiredFields,
|
|
50
|
+
exitProcess
|
|
51
|
+
)
|
|
52
|
+
return config
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const _getPikkuCLIConfig = async (
|
|
56
|
+
configFile: string | undefined = undefined,
|
|
57
|
+
requiredFields: Array<keyof PikkuCLIConfig>,
|
|
58
|
+
exitProcess: boolean = false
|
|
59
|
+
): Promise<PikkuCLIConfig> => {
|
|
60
|
+
if (!configFile) {
|
|
61
|
+
let execDirectory = process.cwd()
|
|
62
|
+
const files = await readdir(execDirectory)
|
|
63
|
+
const file = files.find((file) => /pikku\.config\.(ts|js|json)$/.test(file))
|
|
64
|
+
if (!file) {
|
|
65
|
+
const errorMessage =
|
|
66
|
+
'\nConfig file pikku.config.json not found\nExiting...'
|
|
67
|
+
if (exitProcess) {
|
|
68
|
+
console.error(errorMessage)
|
|
69
|
+
process.exit(1)
|
|
70
|
+
}
|
|
71
|
+
throw new Error(errorMessage)
|
|
72
|
+
}
|
|
73
|
+
configFile = join(execDirectory, file)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
let result: PikkuCLIConfig
|
|
78
|
+
const file = await readFile(configFile, 'utf-8')
|
|
79
|
+
const configDir = dirname(configFile)
|
|
80
|
+
const config: PikkuCLIConfig = JSON.parse(file)
|
|
81
|
+
if (config.extends) {
|
|
82
|
+
const extendedConfig = await getPikkuCLIConfig(
|
|
83
|
+
resolve(configDir, config.extends),
|
|
84
|
+
[],
|
|
85
|
+
exitProcess
|
|
86
|
+
)
|
|
87
|
+
result = {
|
|
88
|
+
...extendedConfig,
|
|
89
|
+
...config,
|
|
90
|
+
configDir,
|
|
91
|
+
packageMappings: {
|
|
92
|
+
...extendedConfig.packageMappings,
|
|
93
|
+
...config.packageMappings,
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
result = {
|
|
98
|
+
...config,
|
|
99
|
+
configDir,
|
|
100
|
+
packageMappings: config.packageMappings || {},
|
|
101
|
+
rootDir: config.rootDir
|
|
102
|
+
? resolve(configDir, config.rootDir)
|
|
103
|
+
: configDir,
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (result.outDir) {
|
|
108
|
+
if (!result.schemaDirectory) {
|
|
109
|
+
result.schemaDirectory = join(result.outDir, 'pikku-schemas')
|
|
110
|
+
}
|
|
111
|
+
if (!result.routesFile) {
|
|
112
|
+
result.routesFile = join(result.outDir, 'pikku-routes.gen.ts')
|
|
113
|
+
}
|
|
114
|
+
if (!result.schedulersFile) {
|
|
115
|
+
result.schedulersFile = join(result.outDir, 'pikku-schedules.gen.ts')
|
|
116
|
+
}
|
|
117
|
+
if (!result.channelsFile) {
|
|
118
|
+
result.channelsFile = join(result.outDir, 'pikku-channels.gen.ts')
|
|
119
|
+
}
|
|
120
|
+
if (!result.typesDeclarationFile) {
|
|
121
|
+
result.typesDeclarationFile = join(
|
|
122
|
+
result.outDir,
|
|
123
|
+
'pikku-types.gen.d.ts'
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
if (!result.routesMapDeclarationFile) {
|
|
127
|
+
result.routesMapDeclarationFile = join(
|
|
128
|
+
result.outDir,
|
|
129
|
+
'pikku-routes-map.gen.d.ts'
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
if (!result.channelsMapDeclarationFile) {
|
|
133
|
+
result.channelsMapDeclarationFile = join(
|
|
134
|
+
result.outDir,
|
|
135
|
+
'pikku-channels-map.gen.d.ts'
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
if (!result.bootstrapFile) {
|
|
139
|
+
result.bootstrapFile = join(result.outDir, 'pikku-bootstrap.gen.ts')
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (requiredFields.length > 0) {
|
|
144
|
+
validateCLIConfig(result, requiredFields)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
for (const objectKey of Object.keys(result)) {
|
|
148
|
+
if (objectKey.endsWith('File') || objectKey.endsWith('Directory')) {
|
|
149
|
+
const relativeTo = CONFIG_DIR_FILES.includes(objectKey)
|
|
150
|
+
? result.configDir
|
|
151
|
+
: result.rootDir
|
|
152
|
+
if (result[objectKey]) {
|
|
153
|
+
if (!isAbsolute(result[objectKey])) {
|
|
154
|
+
result[objectKey] = join(relativeTo, result[objectKey])
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!isAbsolute(result.tsconfig)) {
|
|
161
|
+
result.tsconfig = join(result.rootDir, result.tsconfig)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return result
|
|
165
|
+
} catch (e: any) {
|
|
166
|
+
console.error(e)
|
|
167
|
+
console.error(`Config file not found: ${configFile}`)
|
|
168
|
+
process.exit(1)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export const validateCLIConfig = (
|
|
173
|
+
cliConfig: PikkuCLIConfig,
|
|
174
|
+
required: Array<keyof PikkuCLIConfig>
|
|
175
|
+
) => {
|
|
176
|
+
let errors: string[] = []
|
|
177
|
+
for (const key of required) {
|
|
178
|
+
if (!cliConfig[key]) {
|
|
179
|
+
errors.push(key)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (errors.length > 0) {
|
|
184
|
+
console.error(
|
|
185
|
+
`${errors.join(', ')} ${errors.length === 1 ? 'is' : 'are'} required in pikku.config.json`
|
|
186
|
+
)
|
|
187
|
+
process.exit(1)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ScheduledTasksMeta } from '@pikku/core/scheduler'
|
|
2
|
+
import { getFileImportRelativePath } from '../utils.js'
|
|
3
|
+
|
|
4
|
+
export const serializeSchedulers = (
|
|
5
|
+
outputPath: string,
|
|
6
|
+
filesWithScheduledTasks: Set<string>,
|
|
7
|
+
packageMappings: Record<string, string> = {}
|
|
8
|
+
) => {
|
|
9
|
+
const serializedOutput: string[] = [
|
|
10
|
+
'/* The files with an addSerializedTasks function call */',
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
Array.from(filesWithScheduledTasks)
|
|
14
|
+
.sort()
|
|
15
|
+
.forEach((path) => {
|
|
16
|
+
const filePath = getFileImportRelativePath(
|
|
17
|
+
outputPath,
|
|
18
|
+
path,
|
|
19
|
+
packageMappings
|
|
20
|
+
)
|
|
21
|
+
serializedOutput.push(`import '${filePath}'`)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
return serializedOutput.join('\n')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const serializeSchedulerMeta = (
|
|
28
|
+
scheduledTasksMeta: ScheduledTasksMeta
|
|
29
|
+
) => {
|
|
30
|
+
const serializedOutput: string[] = []
|
|
31
|
+
serializedOutput.push(
|
|
32
|
+
"import { setScheduledTasksMeta } from '@pikku/core/scheduler'"
|
|
33
|
+
)
|
|
34
|
+
serializedOutput.push(
|
|
35
|
+
`setScheduledTasksMeta(${JSON.stringify(scheduledTasksMeta, null, 2)})`
|
|
36
|
+
)
|
|
37
|
+
if (scheduledTasksMeta.length > 0) {
|
|
38
|
+
serializedOutput.push(
|
|
39
|
+
`export type ScheduledTaskNames = '${scheduledTasksMeta.map((s) => s.name).join("' | '")}'`
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
return serializedOutput.join('\n')
|
|
43
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { createGenerator, RootlessError } from 'ts-json-schema-generator'
|
|
2
|
+
import { writeFileInDir } from '../utils.js'
|
|
3
|
+
import { mkdir, writeFile } from 'fs/promises'
|
|
4
|
+
import { JSONValue } from '@pikku/core'
|
|
5
|
+
import { HTTPRoutesMeta } from '@pikku/core/http'
|
|
6
|
+
import { TypesMap } from '@pikku/inspector'
|
|
7
|
+
|
|
8
|
+
export async function generateSchemas(
|
|
9
|
+
tsconfig: string,
|
|
10
|
+
typesMap: TypesMap,
|
|
11
|
+
routesMeta: HTTPRoutesMeta
|
|
12
|
+
): Promise<Record<string, JSONValue>> {
|
|
13
|
+
const schemasSet = new Set(typesMap.customTypes.keys())
|
|
14
|
+
for (const { input, output, inputTypes } of routesMeta) {
|
|
15
|
+
if (input) {
|
|
16
|
+
schemasSet.add(typesMap.getTypeMeta(input).uniqueName)
|
|
17
|
+
}
|
|
18
|
+
if (output) {
|
|
19
|
+
schemasSet.add(typesMap.getTypeMeta(output).uniqueName)
|
|
20
|
+
}
|
|
21
|
+
if (inputTypes?.body) {
|
|
22
|
+
schemasSet.add(inputTypes.body)
|
|
23
|
+
}
|
|
24
|
+
if (inputTypes?.query) {
|
|
25
|
+
schemasSet.add(inputTypes.query)
|
|
26
|
+
}
|
|
27
|
+
if (inputTypes?.params) {
|
|
28
|
+
schemasSet.add(inputTypes.params)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const generator = createGenerator({
|
|
33
|
+
tsconfig,
|
|
34
|
+
skipTypeCheck: true,
|
|
35
|
+
topRef: false,
|
|
36
|
+
discriminatorType: 'open-api',
|
|
37
|
+
})
|
|
38
|
+
const schemas: Record<string, JSONValue> = {}
|
|
39
|
+
schemasSet.forEach((schema) => {
|
|
40
|
+
try {
|
|
41
|
+
schemas[schema] = generator.createSchema(schema) as JSONValue
|
|
42
|
+
} catch (e) {
|
|
43
|
+
// Ignore rootless errors
|
|
44
|
+
if (e instanceof RootlessError) {
|
|
45
|
+
console.log('Error generating schema since it has no root:', schema)
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
throw e
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
return schemas
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function saveSchemas(
|
|
56
|
+
schemaParentDir: string,
|
|
57
|
+
schemas: Record<string, JSONValue>,
|
|
58
|
+
typesMap: TypesMap,
|
|
59
|
+
routesMeta: HTTPRoutesMeta,
|
|
60
|
+
supportsImportAttributes: boolean
|
|
61
|
+
) {
|
|
62
|
+
await writeFileInDir(
|
|
63
|
+
`${schemaParentDir}/register.gen.ts`,
|
|
64
|
+
'export const empty = null;'
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
const desiredSchemas = new Set([
|
|
68
|
+
...routesMeta
|
|
69
|
+
.map(({ input, output }) => [
|
|
70
|
+
input ? typesMap.getUniqueName(input) : undefined,
|
|
71
|
+
output ? typesMap.getUniqueName(output) : undefined,
|
|
72
|
+
])
|
|
73
|
+
.flat()
|
|
74
|
+
.filter(
|
|
75
|
+
(s) =>
|
|
76
|
+
!!s &&
|
|
77
|
+
!['boolean', 'string', 'number', 'null', 'undefined'].includes(s)
|
|
78
|
+
),
|
|
79
|
+
...typesMap.customTypes.keys(),
|
|
80
|
+
])
|
|
81
|
+
|
|
82
|
+
if (desiredSchemas.size === 0) {
|
|
83
|
+
console.log(`\x1b[34m• Skipping schemas since none found.\x1b[0m`)
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
await mkdir(`${schemaParentDir}/schemas`, { recursive: true })
|
|
88
|
+
await Promise.all(
|
|
89
|
+
Object.entries(schemas).map(async ([schemaName, schema]) => {
|
|
90
|
+
if (desiredSchemas.has(schemaName)) {
|
|
91
|
+
await writeFile(
|
|
92
|
+
`${schemaParentDir}/schemas/${schemaName}.schema.json`,
|
|
93
|
+
JSON.stringify(schema),
|
|
94
|
+
'utf-8'
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
const schemaImports = Array.from(desiredSchemas)
|
|
101
|
+
.map(
|
|
102
|
+
(schema) => `
|
|
103
|
+
import * as ${schema} from './schemas/${schema}.schema.json' ${supportsImportAttributes ? `with { type: 'json' }` : ''}
|
|
104
|
+
addSchema('${schema}', ${schema})
|
|
105
|
+
// addSchema('${schema}', require('./schemas/${schema}.schema.json'))
|
|
106
|
+
`
|
|
107
|
+
)
|
|
108
|
+
.join('\n')
|
|
109
|
+
|
|
110
|
+
await writeFileInDir(
|
|
111
|
+
`${schemaParentDir}/register.gen.ts`,
|
|
112
|
+
`import { addSchema } from '@pikku/core/schema'
|
|
113
|
+
// import { createRequire } from "module"
|
|
114
|
+
// const require = createRequire(import.meta.url)
|
|
115
|
+
${schemaImports}`
|
|
116
|
+
)
|
|
117
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// import packageInfo from '../package.json'
|
|
2
|
+
import { relative, dirname } from 'path'
|
|
3
|
+
import { PathToNameAndType, InspectorState } from '@pikku/inspector'
|
|
4
|
+
import { mkdir, writeFile } from 'fs/promises'
|
|
5
|
+
|
|
6
|
+
export const getFileImportRelativePath = (
|
|
7
|
+
from: string,
|
|
8
|
+
to: string,
|
|
9
|
+
packageMappings: Record<string, string>
|
|
10
|
+
): string => {
|
|
11
|
+
let filePath = relative(dirname(from), to)
|
|
12
|
+
if (!/^\.+\//.test(filePath)) {
|
|
13
|
+
filePath = `./${filePath}`
|
|
14
|
+
}
|
|
15
|
+
let usesPackageName = false
|
|
16
|
+
for (const [path, packageName] of Object.entries(packageMappings)) {
|
|
17
|
+
if (filePath.includes(path)) {
|
|
18
|
+
usesPackageName = true
|
|
19
|
+
filePath = filePath.replace(new RegExp(`.*${path}`), packageName)
|
|
20
|
+
break
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (usesPackageName) {
|
|
24
|
+
return filePath.replace('.ts', '')
|
|
25
|
+
}
|
|
26
|
+
return filePath.replace('.ts', '.js')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface Meta {
|
|
30
|
+
file: string
|
|
31
|
+
variable: string
|
|
32
|
+
type: string
|
|
33
|
+
typePath: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type FilesAndMethods = {
|
|
37
|
+
userSessionType: Meta
|
|
38
|
+
sessionServicesType: Meta
|
|
39
|
+
pikkuConfigFactory: Meta
|
|
40
|
+
singletonServicesFactory: Meta
|
|
41
|
+
sessionServicesFactory: Meta
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface PikkuCLIOptions {
|
|
45
|
+
config?: string
|
|
46
|
+
configFileType?: string
|
|
47
|
+
userSessionType?: string
|
|
48
|
+
singletonServicesFactoryType?: string
|
|
49
|
+
sessionServicesFactoryType?: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const getMetaTypes = (
|
|
53
|
+
type: string,
|
|
54
|
+
errors: Map<string, PathToNameAndType>,
|
|
55
|
+
map: PathToNameAndType,
|
|
56
|
+
desiredType?: string
|
|
57
|
+
) => {
|
|
58
|
+
if (desiredType) {
|
|
59
|
+
const entries = Object.entries(map)
|
|
60
|
+
for (const [file, meta] of entries) {
|
|
61
|
+
for (const { type, variable, typePath } of meta) {
|
|
62
|
+
if (type === desiredType) {
|
|
63
|
+
return { file, variable, type, typePath }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
errors.set(`No ${desiredType} found that extends ${type}`, map)
|
|
68
|
+
return undefined
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const totalValues = Object.values(map).flat()
|
|
72
|
+
if (totalValues.length === 0) {
|
|
73
|
+
errors.set(`No ${type} found`, map)
|
|
74
|
+
} else if (totalValues.length > 1) {
|
|
75
|
+
errors.set(`More than one ${type} found`, map)
|
|
76
|
+
} else {
|
|
77
|
+
const entry = Object.entries(map)[0]
|
|
78
|
+
if (entry) {
|
|
79
|
+
const [file, [{ type, variable, typePath }]] = entry
|
|
80
|
+
return { file, type, variable, typePath }
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return undefined
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const getPikkuFilesAndMethods = async (
|
|
88
|
+
{
|
|
89
|
+
sessionServicesTypeImportMap: httpSessionServicesTypeImportMap,
|
|
90
|
+
userSessionTypeImportMap,
|
|
91
|
+
sessionServicesFactories,
|
|
92
|
+
singletonServicesFactories,
|
|
93
|
+
configFactories,
|
|
94
|
+
}: InspectorState,
|
|
95
|
+
packageMappings: Record<string, string>,
|
|
96
|
+
outputFile: string,
|
|
97
|
+
{
|
|
98
|
+
configFileType,
|
|
99
|
+
singletonServicesFactoryType,
|
|
100
|
+
sessionServicesFactoryType,
|
|
101
|
+
}: PikkuCLIOptions,
|
|
102
|
+
requires: Partial<{
|
|
103
|
+
config: boolean
|
|
104
|
+
sessionServiceType: boolean
|
|
105
|
+
userSessionType: boolean
|
|
106
|
+
singletonServicesFactory: boolean
|
|
107
|
+
sessionServicesFactory: boolean
|
|
108
|
+
}> = {
|
|
109
|
+
config: false,
|
|
110
|
+
sessionServiceType: false,
|
|
111
|
+
userSessionType: false,
|
|
112
|
+
singletonServicesFactory: false,
|
|
113
|
+
sessionServicesFactory: false,
|
|
114
|
+
}
|
|
115
|
+
): Promise<FilesAndMethods> => {
|
|
116
|
+
let errors = new Map<string, PathToNameAndType>()
|
|
117
|
+
|
|
118
|
+
const result: Partial<FilesAndMethods> = {
|
|
119
|
+
userSessionType: getMetaTypes(
|
|
120
|
+
'CoreUserSession',
|
|
121
|
+
requires.userSessionType ? errors : new Map(),
|
|
122
|
+
userSessionTypeImportMap,
|
|
123
|
+
configFileType
|
|
124
|
+
),
|
|
125
|
+
sessionServicesType: getMetaTypes(
|
|
126
|
+
'CoreServices',
|
|
127
|
+
requires.sessionServiceType ? errors : new Map(),
|
|
128
|
+
httpSessionServicesTypeImportMap
|
|
129
|
+
),
|
|
130
|
+
pikkuConfigFactory: getMetaTypes(
|
|
131
|
+
'CoreConfig',
|
|
132
|
+
requires.config ? errors : new Map(),
|
|
133
|
+
configFactories,
|
|
134
|
+
configFileType
|
|
135
|
+
),
|
|
136
|
+
singletonServicesFactory: getMetaTypes(
|
|
137
|
+
'CreateSingletonServices',
|
|
138
|
+
requires.singletonServicesFactory ? errors : new Map(),
|
|
139
|
+
singletonServicesFactories,
|
|
140
|
+
singletonServicesFactoryType
|
|
141
|
+
),
|
|
142
|
+
sessionServicesFactory: getMetaTypes(
|
|
143
|
+
'CreateSessionServices',
|
|
144
|
+
requires.sessionServicesFactory ? errors : new Map(),
|
|
145
|
+
sessionServicesFactories,
|
|
146
|
+
sessionServicesFactoryType
|
|
147
|
+
),
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (errors.size > 0) {
|
|
151
|
+
const result: string[] = ['Found errors:']
|
|
152
|
+
errors.forEach((filesAndMethods, message) => {
|
|
153
|
+
result.push(`- ${message}`)
|
|
154
|
+
for (const [file, methods] of Object.entries(filesAndMethods)) {
|
|
155
|
+
result.push(
|
|
156
|
+
`\t* file: ${getFileImportRelativePath(outputFile, file, packageMappings)}`
|
|
157
|
+
)
|
|
158
|
+
result.push(
|
|
159
|
+
`\t* methods: ${methods.map(({ variable, type }) => `${variable}: ${type}`).join(', ')}`
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
console.error(result.join('\n'))
|
|
165
|
+
process.exit(1)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return result as FilesAndMethods
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export const writeFileInDir = async (
|
|
172
|
+
path: string,
|
|
173
|
+
content: string,
|
|
174
|
+
ignoreModifyComment: boolean = false
|
|
175
|
+
) => {
|
|
176
|
+
if (content.includes('server-only')) {
|
|
177
|
+
content = content.replace(
|
|
178
|
+
"'server-only'",
|
|
179
|
+
`'server-only'\n\n${ignoreModifyComment ? '' : DO_NOT_MODIFY_COMMENT}`
|
|
180
|
+
)
|
|
181
|
+
} else {
|
|
182
|
+
content = `${ignoreModifyComment ? '' : DO_NOT_MODIFY_COMMENT}${content}`
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
await mkdir(dirname(path), { recursive: true })
|
|
186
|
+
await writeFile(path, content, 'utf-8')
|
|
187
|
+
console.log(`\x1b[32m✓ File written to ${path}\x1b[0m`)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export const logCommandInfoAndTime = async (
|
|
191
|
+
commandStart: string,
|
|
192
|
+
commandEnd: string,
|
|
193
|
+
[skipCondition, skipMessage = 'none found']: [boolean] | [boolean, string],
|
|
194
|
+
callback: (...args: any[]) => Promise<unknown>
|
|
195
|
+
): Promise<boolean> => {
|
|
196
|
+
if (skipCondition === true) {
|
|
197
|
+
console.log(
|
|
198
|
+
`\x1b[34m• Skipping ${commandStart} since ${skipMessage}.\x1b[0m`
|
|
199
|
+
)
|
|
200
|
+
return false
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const start = Date.now()
|
|
204
|
+
console.log(`\x1b[34m• ${commandStart}...\x1b[0m`)
|
|
205
|
+
await callback()
|
|
206
|
+
|
|
207
|
+
console.log(`\x1b[32m✓ ${commandEnd} in ${Date.now() - start}ms.\x1b[0m`)
|
|
208
|
+
return true
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export const logPikkuLogo = () => {
|
|
212
|
+
console.log(`\x1b[33m⚙️ PIKKU CLI ⚙️\n-------------------\x1b[0m`)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// TODO: add version back in once the ESM dust settles
|
|
216
|
+
export const DO_NOT_MODIFY_COMMENT = `/**
|
|
217
|
+
* This file was generated by the @pikku/cli
|
|
218
|
+
*/
|
|
219
|
+
`
|
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
|
+
}
|