massimo-cli 0.4.0 → 0.5.0
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/help/help.txt +2 -1
- package/index.js +110 -30
- package/lib/frontend-openapi-generator.js +10 -7
- package/package.json +2 -2
package/help/help.txt
CHANGED
|
@@ -55,7 +55,7 @@ You can generate only the types with the --types-only flag.
|
|
|
55
55
|
$ massimo http://exmaple.com/to/schema/file --name myclient --types-only
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
Will create the single myclient.d.ts file.
|
|
58
|
+
Will create the single myclient.d.ts file (or .d.mts/.d.cts depending on module format and --type-extension option).
|
|
59
59
|
|
|
60
60
|
Options:
|
|
61
61
|
|
|
@@ -77,3 +77,4 @@ Options:
|
|
|
77
77
|
* `--props-optional` - If `true`, properties will be defined as optional unless they're part of the `required` array. By default this option is `true`.
|
|
78
78
|
* `--skip-config-update` - If `true`, it will not update the `platformatic|watt` config found in your repo. By default this option is `true`.
|
|
79
79
|
* `--retry-timeout-ms` - If passed, the HTTP request to get an Open API schema will be retried (reference: https://undici.nodejs.org/#/docs/api/RetryHandler.md)
|
|
80
|
+
* `--type-extension` - Force the use of module-specific type extensions (.d.mts for ESM, .d.cts for CJS) instead of the default logic.
|
package/index.js
CHANGED
|
@@ -59,19 +59,55 @@ export async function detectModuleFormat (folder, explicitFormat) {
|
|
|
59
59
|
try {
|
|
60
60
|
const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf8'))
|
|
61
61
|
if (packageJson.type === 'module') return 'esm'
|
|
62
|
-
// If no type field or any other value, it's CommonJS per Node.js defaults
|
|
63
62
|
return 'cjs'
|
|
64
63
|
} catch (err) {
|
|
65
|
-
// If we can't parse it, continue searching
|
|
66
64
|
}
|
|
67
65
|
}
|
|
68
66
|
currentDir = dirname(currentDir)
|
|
69
67
|
}
|
|
70
68
|
|
|
71
|
-
// Default to ESM
|
|
72
69
|
return 'esm'
|
|
73
70
|
}
|
|
74
71
|
|
|
72
|
+
export async function determineTypeExtension (folder, moduleFormat, typeExtension, explicitModuleFormat, generateImplementation = true) {
|
|
73
|
+
if (typeExtension) {
|
|
74
|
+
return moduleFormat === 'esm' ? 'd.mts' : 'd.cts'
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!explicitModuleFormat && !generateImplementation) {
|
|
78
|
+
return 'd.ts'
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!explicitModuleFormat && generateImplementation) {
|
|
82
|
+
return 'd.ts'
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let currentDir = folder
|
|
86
|
+
while (currentDir !== dirname(currentDir)) {
|
|
87
|
+
const packageJsonPath = join(currentDir, 'package.json')
|
|
88
|
+
if (await isFileAccessible(packageJsonPath)) {
|
|
89
|
+
try {
|
|
90
|
+
const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf8'))
|
|
91
|
+
const parentType = packageJson.type === 'module' ? 'esm' : 'cjs'
|
|
92
|
+
|
|
93
|
+
if (moduleFormat === parentType) {
|
|
94
|
+
return 'd.ts'
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return moduleFormat === 'esm' ? 'd.mts' : 'd.cts'
|
|
98
|
+
} catch (err) {
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
currentDir = dirname(currentDir)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (explicitModuleFormat) {
|
|
105
|
+
return moduleFormat === 'esm' ? 'd.mts' : 'd.cts'
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return 'd.ts'
|
|
109
|
+
}
|
|
110
|
+
|
|
75
111
|
async function writeOpenAPIClient (
|
|
76
112
|
folder,
|
|
77
113
|
name,
|
|
@@ -88,7 +124,9 @@ async function writeOpenAPIClient (
|
|
|
88
124
|
logger,
|
|
89
125
|
withCredentials,
|
|
90
126
|
propsOptional,
|
|
91
|
-
moduleFormat
|
|
127
|
+
moduleFormat,
|
|
128
|
+
typeExtension,
|
|
129
|
+
explicitModuleFormat
|
|
92
130
|
) {
|
|
93
131
|
await createDirectory(folder)
|
|
94
132
|
|
|
@@ -107,6 +145,7 @@ async function writeOpenAPIClient (
|
|
|
107
145
|
}
|
|
108
146
|
|
|
109
147
|
if (isFrontend) {
|
|
148
|
+
const typeExt = await determineTypeExtension(folder, moduleFormat, typeExtension, explicitModuleFormat, generateImplementation)
|
|
110
149
|
const { types, implementation } = processFrontendOpenAPI({
|
|
111
150
|
schema,
|
|
112
151
|
name,
|
|
@@ -116,8 +155,9 @@ async function writeOpenAPIClient (
|
|
|
116
155
|
logger,
|
|
117
156
|
withCredentials,
|
|
118
157
|
propsOptional,
|
|
158
|
+
typeExt
|
|
119
159
|
})
|
|
120
|
-
await writeFile(join(folder, `${name}-types
|
|
160
|
+
await writeFile(join(folder, `${name}-types.${typeExt}`), types)
|
|
121
161
|
if (generateImplementation) {
|
|
122
162
|
const extension = language === 'js' ? 'mjs' : 'mts'
|
|
123
163
|
await writeFile(join(folder, `${name}.${extension}`), implementation)
|
|
@@ -134,7 +174,7 @@ async function writeOpenAPIClient (
|
|
|
134
174
|
propsOptional,
|
|
135
175
|
moduleFormat,
|
|
136
176
|
})
|
|
137
|
-
const typeExt =
|
|
177
|
+
const typeExt = await determineTypeExtension(folder, moduleFormat, typeExtension, explicitModuleFormat, generateImplementation)
|
|
138
178
|
const implExt = moduleFormat === 'esm' ? 'mjs' : 'cjs'
|
|
139
179
|
await writeFile(join(folder, `${name}.${typeExt}`), types)
|
|
140
180
|
if (generateImplementation) {
|
|
@@ -144,7 +184,7 @@ async function writeOpenAPIClient (
|
|
|
144
184
|
if (!typesOnly) {
|
|
145
185
|
await writeFile(
|
|
146
186
|
join(folder, 'package.json'),
|
|
147
|
-
getPackageJSON({ name, generateImplementation, moduleFormat })
|
|
187
|
+
await getPackageJSON({ name, generateImplementation, moduleFormat, folder, typeExtension, explicitModuleFormat })
|
|
148
188
|
)
|
|
149
189
|
}
|
|
150
190
|
}
|
|
@@ -156,7 +196,9 @@ async function writeGraphQLClient (
|
|
|
156
196
|
schema,
|
|
157
197
|
url,
|
|
158
198
|
generateImplementation,
|
|
159
|
-
moduleFormat
|
|
199
|
+
moduleFormat,
|
|
200
|
+
typeExtension,
|
|
201
|
+
explicitModuleFormat
|
|
160
202
|
) {
|
|
161
203
|
await createDirectory(folder, { recursive: true })
|
|
162
204
|
const { types, implementation } = processGraphQL({
|
|
@@ -168,7 +210,7 @@ async function writeGraphQLClient (
|
|
|
168
210
|
})
|
|
169
211
|
const clientSchema = graphql.buildClientSchema(schema)
|
|
170
212
|
const sdl = graphql.printSchema(clientSchema)
|
|
171
|
-
const typeExt =
|
|
213
|
+
const typeExt = await determineTypeExtension(folder, moduleFormat, typeExtension, explicitModuleFormat, generateImplementation)
|
|
172
214
|
const implExt = moduleFormat === 'esm' ? 'mjs' : 'cjs'
|
|
173
215
|
await writeFile(join(folder, `${name}.schema.graphql`), sdl)
|
|
174
216
|
await writeFile(join(folder, `${name}.${typeExt}`), types)
|
|
@@ -177,7 +219,7 @@ async function writeGraphQLClient (
|
|
|
177
219
|
}
|
|
178
220
|
await writeFile(
|
|
179
221
|
join(folder, 'package.json'),
|
|
180
|
-
getPackageJSON({ name, generateImplementation, moduleFormat })
|
|
222
|
+
await getPackageJSON({ name, generateImplementation, moduleFormat, folder, typeExtension, explicitModuleFormat })
|
|
181
223
|
)
|
|
182
224
|
}
|
|
183
225
|
|
|
@@ -199,7 +241,9 @@ async function downloadAndWriteOpenAPI (
|
|
|
199
241
|
withCredentials,
|
|
200
242
|
propsOptional,
|
|
201
243
|
retryTimeoutMs,
|
|
202
|
-
moduleFormat
|
|
244
|
+
moduleFormat,
|
|
245
|
+
typeExtension,
|
|
246
|
+
explicitModuleFormat
|
|
203
247
|
) {
|
|
204
248
|
logger.debug(`Trying to download OpenAPI schema from ${url}`)
|
|
205
249
|
let requestOptions
|
|
@@ -237,7 +281,9 @@ async function downloadAndWriteOpenAPI (
|
|
|
237
281
|
logger,
|
|
238
282
|
withCredentials,
|
|
239
283
|
propsOptional,
|
|
240
|
-
moduleFormat
|
|
284
|
+
moduleFormat,
|
|
285
|
+
typeExtension,
|
|
286
|
+
explicitModuleFormat
|
|
241
287
|
)
|
|
242
288
|
/* c8 ignore next 3 */
|
|
243
289
|
} catch (err) {
|
|
@@ -257,7 +303,9 @@ async function downloadAndWriteGraphQL (
|
|
|
257
303
|
folder,
|
|
258
304
|
name,
|
|
259
305
|
generateImplementation,
|
|
260
|
-
moduleFormat
|
|
306
|
+
moduleFormat,
|
|
307
|
+
typeExtension,
|
|
308
|
+
explicitModuleFormat
|
|
261
309
|
) {
|
|
262
310
|
logger.debug(`Trying to download GraphQL schema from ${url}`)
|
|
263
311
|
const query = graphql.getIntrospectionQuery()
|
|
@@ -284,7 +332,9 @@ async function downloadAndWriteGraphQL (
|
|
|
284
332
|
schema,
|
|
285
333
|
url,
|
|
286
334
|
generateImplementation,
|
|
287
|
-
moduleFormat
|
|
335
|
+
moduleFormat,
|
|
336
|
+
typeExtension,
|
|
337
|
+
explicitModuleFormat
|
|
288
338
|
)
|
|
289
339
|
return 'graphql'
|
|
290
340
|
}
|
|
@@ -305,7 +355,9 @@ async function readFromFileAndWrite (
|
|
|
305
355
|
typesComment,
|
|
306
356
|
withCredentials,
|
|
307
357
|
propsOptional,
|
|
308
|
-
moduleFormat
|
|
358
|
+
moduleFormat,
|
|
359
|
+
typeExtension,
|
|
360
|
+
explicitModuleFormat
|
|
309
361
|
) {
|
|
310
362
|
logger.info(`Trying to read schema from file ${file}`)
|
|
311
363
|
const text = await readFile(file, 'utf8')
|
|
@@ -327,7 +379,9 @@ async function readFromFileAndWrite (
|
|
|
327
379
|
logger,
|
|
328
380
|
withCredentials,
|
|
329
381
|
propsOptional,
|
|
330
|
-
moduleFormat
|
|
382
|
+
moduleFormat,
|
|
383
|
+
typeExtension,
|
|
384
|
+
explicitModuleFormat
|
|
331
385
|
)
|
|
332
386
|
return 'openapi'
|
|
333
387
|
} catch (err) {
|
|
@@ -346,7 +400,9 @@ async function readFromFileAndWrite (
|
|
|
346
400
|
introspectionResult,
|
|
347
401
|
'http://localhost:3042/graphql',
|
|
348
402
|
generateImplementation,
|
|
349
|
-
moduleFormat
|
|
403
|
+
moduleFormat,
|
|
404
|
+
typeExtension,
|
|
405
|
+
explicitModuleFormat
|
|
350
406
|
)
|
|
351
407
|
return 'graphql'
|
|
352
408
|
}
|
|
@@ -370,7 +426,9 @@ async function downloadAndProcess (options) {
|
|
|
370
426
|
withCredentials,
|
|
371
427
|
propsOptional,
|
|
372
428
|
retryTimeoutMs,
|
|
373
|
-
moduleFormat
|
|
429
|
+
moduleFormat,
|
|
430
|
+
typeExtension,
|
|
431
|
+
explicitModuleFormat
|
|
374
432
|
} = options
|
|
375
433
|
|
|
376
434
|
const generateImplementation = options.generateImplementation
|
|
@@ -399,7 +457,9 @@ async function downloadAndProcess (options) {
|
|
|
399
457
|
withCredentials,
|
|
400
458
|
propsOptional,
|
|
401
459
|
retryTimeoutMs,
|
|
402
|
-
moduleFormat
|
|
460
|
+
moduleFormat,
|
|
461
|
+
typeExtension,
|
|
462
|
+
explicitModuleFormat
|
|
403
463
|
)
|
|
404
464
|
)
|
|
405
465
|
toTry.push(
|
|
@@ -422,7 +482,9 @@ async function downloadAndProcess (options) {
|
|
|
422
482
|
withCredentials,
|
|
423
483
|
propsOptional,
|
|
424
484
|
retryTimeoutMs,
|
|
425
|
-
moduleFormat
|
|
485
|
+
moduleFormat,
|
|
486
|
+
typeExtension,
|
|
487
|
+
explicitModuleFormat
|
|
426
488
|
)
|
|
427
489
|
)
|
|
428
490
|
} else if (options.type === 'graphql') {
|
|
@@ -434,7 +496,9 @@ async function downloadAndProcess (options) {
|
|
|
434
496
|
folder,
|
|
435
497
|
name,
|
|
436
498
|
generateImplementation,
|
|
437
|
-
moduleFormat
|
|
499
|
+
moduleFormat,
|
|
500
|
+
typeExtension,
|
|
501
|
+
explicitModuleFormat
|
|
438
502
|
)
|
|
439
503
|
)
|
|
440
504
|
toTry.push(
|
|
@@ -445,7 +509,9 @@ async function downloadAndProcess (options) {
|
|
|
445
509
|
folder,
|
|
446
510
|
name,
|
|
447
511
|
generateImplementation,
|
|
448
|
-
moduleFormat
|
|
512
|
+
moduleFormat,
|
|
513
|
+
typeExtension,
|
|
514
|
+
explicitModuleFormat
|
|
449
515
|
)
|
|
450
516
|
)
|
|
451
517
|
} else {
|
|
@@ -470,7 +536,9 @@ async function downloadAndProcess (options) {
|
|
|
470
536
|
withCredentials,
|
|
471
537
|
propsOptional,
|
|
472
538
|
retryTimeoutMs,
|
|
473
|
-
moduleFormat
|
|
539
|
+
moduleFormat,
|
|
540
|
+
typeExtension,
|
|
541
|
+
explicitModuleFormat
|
|
474
542
|
)
|
|
475
543
|
)
|
|
476
544
|
toTry.push(
|
|
@@ -481,7 +549,9 @@ async function downloadAndProcess (options) {
|
|
|
481
549
|
folder,
|
|
482
550
|
name,
|
|
483
551
|
generateImplementation,
|
|
484
|
-
moduleFormat
|
|
552
|
+
moduleFormat,
|
|
553
|
+
typeExtension,
|
|
554
|
+
explicitModuleFormat
|
|
485
555
|
)
|
|
486
556
|
)
|
|
487
557
|
toTry.push(
|
|
@@ -504,7 +574,9 @@ async function downloadAndProcess (options) {
|
|
|
504
574
|
withCredentials,
|
|
505
575
|
propsOptional,
|
|
506
576
|
retryTimeoutMs,
|
|
507
|
-
moduleFormat
|
|
577
|
+
moduleFormat,
|
|
578
|
+
typeExtension,
|
|
579
|
+
explicitModuleFormat
|
|
508
580
|
)
|
|
509
581
|
)
|
|
510
582
|
toTry.push(
|
|
@@ -515,7 +587,9 @@ async function downloadAndProcess (options) {
|
|
|
515
587
|
folder,
|
|
516
588
|
name,
|
|
517
589
|
generateImplementation,
|
|
518
|
-
moduleFormat
|
|
590
|
+
moduleFormat,
|
|
591
|
+
typeExtension,
|
|
592
|
+
explicitModuleFormat
|
|
519
593
|
)
|
|
520
594
|
)
|
|
521
595
|
}
|
|
@@ -539,7 +613,9 @@ async function downloadAndProcess (options) {
|
|
|
539
613
|
typesComment,
|
|
540
614
|
withCredentials,
|
|
541
615
|
propsOptional,
|
|
542
|
-
moduleFormat
|
|
616
|
+
moduleFormat,
|
|
617
|
+
typeExtension,
|
|
618
|
+
explicitModuleFormat
|
|
543
619
|
)
|
|
544
620
|
)
|
|
545
621
|
}
|
|
@@ -557,11 +633,12 @@ async function downloadAndProcess (options) {
|
|
|
557
633
|
}
|
|
558
634
|
}
|
|
559
635
|
|
|
560
|
-
function getPackageJSON ({ name, generateImplementation, moduleFormat }) {
|
|
636
|
+
async function getPackageJSON ({ name, generateImplementation, moduleFormat, folder, typeExtension, explicitModuleFormat }) {
|
|
561
637
|
const isESM = moduleFormat === 'esm'
|
|
638
|
+
const typeExt = await determineTypeExtension(folder, moduleFormat, typeExtension, explicitModuleFormat, generateImplementation)
|
|
562
639
|
const obj = {
|
|
563
640
|
name,
|
|
564
|
-
types: `./${name}.${
|
|
641
|
+
types: `./${name}.${typeExt}`
|
|
565
642
|
}
|
|
566
643
|
|
|
567
644
|
if (isESM) {
|
|
@@ -604,7 +681,8 @@ export async function command (argv) {
|
|
|
604
681
|
'frontend',
|
|
605
682
|
'validate-response',
|
|
606
683
|
'props-optional',
|
|
607
|
-
'skip-config-update'
|
|
684
|
+
'skip-config-update',
|
|
685
|
+
'type-extension'
|
|
608
686
|
],
|
|
609
687
|
default: {
|
|
610
688
|
typescript: false,
|
|
@@ -672,6 +750,8 @@ export async function command (argv) {
|
|
|
672
750
|
options.withCredentials = options['with-credentials']
|
|
673
751
|
options.skipConfigUpdate = options['skip-config-update'] ?? true
|
|
674
752
|
options.retryTimeoutMs = options['retry-timeout-ms']
|
|
753
|
+
options.typeExtension = options['type-extension']
|
|
754
|
+
options.explicitModuleFormat = !!options.module
|
|
675
755
|
await downloadAndProcess({ url, ...options, logger })
|
|
676
756
|
logger.info(`Client generated successfully into ${options.folder}`)
|
|
677
757
|
logger.info('Check out the docs to know more: https://docs.platformatic.dev/docs/service/overview')
|
|
@@ -18,7 +18,8 @@ export function processFrontendOpenAPI ({
|
|
|
18
18
|
fullRequest,
|
|
19
19
|
logger,
|
|
20
20
|
withCredentials,
|
|
21
|
-
propsOptional
|
|
21
|
+
propsOptional,
|
|
22
|
+
typeExt = 'd.mts'
|
|
22
23
|
}) {
|
|
23
24
|
return {
|
|
24
25
|
types: generateTypesFromOpenAPI({ schema, name, fullResponse, fullRequest, propsOptional }),
|
|
@@ -29,7 +30,8 @@ export function processFrontendOpenAPI ({
|
|
|
29
30
|
fullResponse,
|
|
30
31
|
fullRequest,
|
|
31
32
|
logger,
|
|
32
|
-
withCredentials
|
|
33
|
+
withCredentials,
|
|
34
|
+
typeExt
|
|
33
35
|
})
|
|
34
36
|
}
|
|
35
37
|
}
|
|
@@ -41,7 +43,8 @@ function generateFrontendImplementationFromOpenAPI ({
|
|
|
41
43
|
fullResponse,
|
|
42
44
|
fullRequest,
|
|
43
45
|
logger,
|
|
44
|
-
withCredentials
|
|
46
|
+
withCredentials,
|
|
47
|
+
typeExt = 'd.mts'
|
|
45
48
|
}) {
|
|
46
49
|
const camelCaseName = capitalize(camelcase(name))
|
|
47
50
|
const { paths } = schema
|
|
@@ -111,13 +114,13 @@ function generateFrontendImplementationFromOpenAPI ({
|
|
|
111
114
|
writer.write('function sanitizeUrl(url)').block(() => {
|
|
112
115
|
writer.writeLine("if (url.endsWith('/')) { return url.slice(0, -1) } else { return url }")
|
|
113
116
|
})
|
|
114
|
-
writer.writeLine(`/** @type {import('./${name}-types
|
|
117
|
+
writer.writeLine(`/** @type {import('./${name}-types.${typeExt}').${camelCaseName}['setBaseUrl']} */`)
|
|
115
118
|
writer.writeLine('export const setBaseUrl = (newUrl) => { baseUrl = sanitizeUrl(newUrl) }')
|
|
116
119
|
writer.newLine()
|
|
117
|
-
writer.writeLine(`/** @type {import('./${name}-types
|
|
120
|
+
writer.writeLine(`/** @type {import('./${name}-types.${typeExt}').${camelCaseName}['setDefaultHeaders']} */`)
|
|
118
121
|
writer.writeLine('export const setDefaultHeaders = (headers) => { defaultHeaders = headers }')
|
|
119
122
|
writer.newLine()
|
|
120
|
-
writer.writeLine(`/** @type {import('./${name}-types
|
|
123
|
+
writer.writeLine(`/** @type {import('./${name}-types.${typeExt}').${camelCaseName}['setDefaultFetchParams']} */`)
|
|
121
124
|
writer.writeLine('export const setDefaultFetchParams = (fetchParams) => { defaultFetchParams = fetchParams }')
|
|
122
125
|
writer.newLine()
|
|
123
126
|
writer.write('function headersToJSON(headers) ').block(() => {
|
|
@@ -354,7 +357,7 @@ function generateFrontendImplementationFromOpenAPI ({
|
|
|
354
357
|
// ```
|
|
355
358
|
//
|
|
356
359
|
writer
|
|
357
|
-
.writeLine(`/** @type {import('./${name}-types
|
|
360
|
+
.writeLine(`/** @type {import('./${name}-types.${typeExt}').${camelCaseName}['${operationId}']} */`)
|
|
358
361
|
.write(`export const ${operationId} = async (request) =>`)
|
|
359
362
|
.block(() => {
|
|
360
363
|
writer.write(`return await ${underscoredOperationId}(baseUrl, request)`)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "massimo-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "A client for HTTP services.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"pino-pretty": "^13.0.0",
|
|
31
31
|
"undici": "^7.0.0",
|
|
32
32
|
"yaml": "^2.4.1",
|
|
33
|
-
"massimo": "0.
|
|
33
|
+
"massimo": "0.5.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@platformatic/composer": "3.0.0-alpha.6",
|