massimo-cli 0.4.0 → 1.0.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/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.d.mts`), 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 = moduleFormat === 'esm' ? 'd.mts' : 'd.cts'
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 = moduleFormat === 'esm' ? 'd.mts' : 'd.cts'
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}.${isESM ? 'd.mts' : 'd.cts'}`
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.d.mts').${camelCaseName}['setBaseUrl']} */`)
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.d.mts').${camelCaseName}['setDefaultHeaders']} */`)
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.d.mts').${camelCaseName}['setDefaultFetchParams']} */`)
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.d.mts').${camelCaseName}['${operationId}']} */`)
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.4.0",
3
+ "version": "1.0.1",
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.4.0"
33
+ "massimo": "1.0.1"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@platformatic/composer": "3.0.0-alpha.6",