@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.
Files changed (95) hide show
  1. package/CHANGELOG.md +445 -0
  2. package/README.md +3 -0
  3. package/bin/pikku-all.ts +110 -0
  4. package/bin/pikku-channels-map.ts +53 -0
  5. package/bin/pikku-channels.ts +57 -0
  6. package/bin/pikku-fetch.ts +54 -0
  7. package/bin/pikku-function-types.ts +76 -0
  8. package/bin/pikku-nextjs.test.ts +279 -0
  9. package/bin/pikku-nextjs.ts +113 -0
  10. package/bin/pikku-openapi.ts +71 -0
  11. package/bin/pikku-routes-map.ts +54 -0
  12. package/bin/pikku-routes.ts +55 -0
  13. package/bin/pikku-scheduler.ts +61 -0
  14. package/bin/pikku-schemas.ts +51 -0
  15. package/bin/pikku-websocket.ts +54 -0
  16. package/bin/pikku.ts +26 -0
  17. package/cli.schema.json +212 -0
  18. package/dist/bin/pikku-all.d.ts +4 -0
  19. package/dist/bin/pikku-all.js +71 -0
  20. package/dist/bin/pikku-channels-map.d.ts +5 -0
  21. package/dist/bin/pikku-channels-map.js +27 -0
  22. package/dist/bin/pikku-channels.d.ts +5 -0
  23. package/dist/bin/pikku-channels.js +32 -0
  24. package/dist/bin/pikku-fetch.d.ts +6 -0
  25. package/dist/bin/pikku-fetch.js +25 -0
  26. package/dist/bin/pikku-function-types.d.ts +6 -0
  27. package/dist/bin/pikku-function-types.js +32 -0
  28. package/dist/bin/pikku-nextjs.d.ts +7 -0
  29. package/dist/bin/pikku-nextjs.js +40 -0
  30. package/dist/bin/pikku-openapi.d.ts +5 -0
  31. package/dist/bin/pikku-openapi.js +41 -0
  32. package/dist/bin/pikku-routes-map.d.ts +5 -0
  33. package/dist/bin/pikku-routes-map.js +27 -0
  34. package/dist/bin/pikku-routes.d.ts +5 -0
  35. package/dist/bin/pikku-routes.js +33 -0
  36. package/dist/bin/pikku-scheduler.d.ts +5 -0
  37. package/dist/bin/pikku-scheduler.js +32 -0
  38. package/dist/bin/pikku-schemas.d.ts +5 -0
  39. package/dist/bin/pikku-schemas.js +27 -0
  40. package/dist/bin/pikku-websocket.d.ts +6 -0
  41. package/dist/bin/pikku-websocket.js +25 -0
  42. package/dist/bin/pikku.d.ts +2 -0
  43. package/dist/bin/pikku.js +23 -0
  44. package/dist/src/channels/serialize-channels.d.ts +3 -0
  45. package/dist/src/channels/serialize-channels.js +19 -0
  46. package/dist/src/channels/serialize-typed-channel-map.d.ts +3 -0
  47. package/dist/src/channels/serialize-typed-channel-map.js +87 -0
  48. package/dist/src/channels/serialize-websocket-wrapper.d.ts +1 -0
  49. package/dist/src/channels/serialize-websocket-wrapper.js +61 -0
  50. package/dist/src/core/serialize-import-map.d.ts +2 -0
  51. package/dist/src/core/serialize-import-map.js +23 -0
  52. package/dist/src/core/serialize-pikku-types.d.ts +4 -0
  53. package/dist/src/core/serialize-pikku-types.js +48 -0
  54. package/dist/src/http/serialize-fetch-wrapper.d.ts +1 -0
  55. package/dist/src/http/serialize-fetch-wrapper.js +57 -0
  56. package/dist/src/http/serialize-route-imports.d.ts +1 -0
  57. package/dist/src/http/serialize-route-imports.js +13 -0
  58. package/dist/src/http/serialize-route-meta.d.ts +2 -0
  59. package/dist/src/http/serialize-route-meta.js +6 -0
  60. package/dist/src/http/serialize-typed-route-map.d.ts +3 -0
  61. package/dist/src/http/serialize-typed-route-map.js +107 -0
  62. package/dist/src/inspector-glob.d.ts +2 -0
  63. package/dist/src/inspector-glob.js +12 -0
  64. package/dist/src/nextjs/serialize-nextjs-wrapper.d.ts +1 -0
  65. package/dist/src/nextjs/serialize-nextjs-wrapper.js +149 -0
  66. package/dist/src/openapi/openapi-spec-generator.d.ts +79 -0
  67. package/dist/src/openapi/openapi-spec-generator.js +135 -0
  68. package/dist/src/pikku-cli-config.d.ts +31 -0
  69. package/dist/src/pikku-cli-config.js +113 -0
  70. package/dist/src/scheduler/serialize-schedulers.d.ts +3 -0
  71. package/dist/src/scheduler/serialize-schedulers.js +22 -0
  72. package/dist/src/schema/schema-generator.d.ts +5 -0
  73. package/dist/src/schema/schema-generator.js +79 -0
  74. package/dist/src/utils.d.ts +34 -0
  75. package/dist/src/utils.js +109 -0
  76. package/dist/tsconfig.tsbuildinfo +1 -0
  77. package/package.json +42 -0
  78. package/run-tests.sh +53 -0
  79. package/src/channels/serialize-channels.ts +34 -0
  80. package/src/channels/serialize-typed-channel-map.ts +133 -0
  81. package/src/channels/serialize-websocket-wrapper.ts +61 -0
  82. package/src/core/serialize-import-map.ts +33 -0
  83. package/src/core/serialize-pikku-types.ts +53 -0
  84. package/src/http/serialize-fetch-wrapper.ts +57 -0
  85. package/src/http/serialize-route-imports.ts +24 -0
  86. package/src/http/serialize-route-meta.ts +10 -0
  87. package/src/http/serialize-typed-route-map.ts +147 -0
  88. package/src/inspector-glob.ts +27 -0
  89. package/src/nextjs/serialize-nextjs-wrapper.ts +156 -0
  90. package/src/openapi/openapi-spec-generator.ts +229 -0
  91. package/src/pikku-cli-config.ts +189 -0
  92. package/src/scheduler/serialize-schedulers.ts +43 -0
  93. package/src/schema/schema-generator.ts +117 -0
  94. package/src/utils.ts +219 -0
  95. 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
+ }