massimo-cli 0.3.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 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
@@ -4,7 +4,7 @@ import { access, mkdir, readFile, rm, writeFile } from 'fs/promises'
4
4
  import graphql from 'graphql'
5
5
  import helpMe from 'help-me'
6
6
  import parseArgs from 'minimist'
7
- import { join } from 'path'
7
+ import { join, dirname } from 'path'
8
8
  import pino from 'pino'
9
9
  import pinoPretty from 'pino-pretty'
10
10
  import { getGlobalDispatcher, interceptors, request } from 'undici'
@@ -43,6 +43,71 @@ export async function createDirectory (path, empty = false) {
43
43
  return mkdir(path, { recursive: true, maxRetries: 10, retryDelay: 1000 })
44
44
  }
45
45
 
46
+ export async function detectModuleFormat (folder, explicitFormat) {
47
+ if (explicitFormat) {
48
+ if (explicitFormat === 'esm' || explicitFormat === 'cjs') {
49
+ return explicitFormat
50
+ }
51
+ throw new Error(
52
+ `Invalid module format: ${explicitFormat}. Valid values are 'esm' or 'cjs'`
53
+ )
54
+ }
55
+ let currentDir = folder
56
+ while (currentDir !== dirname(currentDir)) {
57
+ const packageJsonPath = join(currentDir, 'package.json')
58
+ if (await isFileAccessible(packageJsonPath)) {
59
+ try {
60
+ const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf8'))
61
+ if (packageJson.type === 'module') return 'esm'
62
+ return 'cjs'
63
+ } catch (err) {
64
+ }
65
+ }
66
+ currentDir = dirname(currentDir)
67
+ }
68
+
69
+ return 'esm'
70
+ }
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
+
46
111
  async function writeOpenAPIClient (
47
112
  folder,
48
113
  name,
@@ -58,20 +123,29 @@ async function writeOpenAPIClient (
58
123
  typesComment,
59
124
  logger,
60
125
  withCredentials,
61
- propsOptional
126
+ propsOptional,
127
+ moduleFormat,
128
+ typeExtension,
129
+ explicitModuleFormat
62
130
  ) {
63
131
  await createDirectory(folder)
64
132
 
65
133
  // TODO deal with yaml
66
134
  const schema = parseFile(text)
67
135
  if (!schema) {
68
- throw new Error('Cannot parse OpenAPI file. Please make sure is a JSON or a YAML file.')
136
+ throw new Error(
137
+ 'Cannot parse OpenAPI file. Please make sure is a JSON or a YAML file.'
138
+ )
69
139
  }
70
140
  if (!typesOnly) {
71
- await writeFile(join(folder, `${name}.openapi.json`), JSON.stringify(schema, null, 2))
141
+ await writeFile(
142
+ join(folder, `${name}.openapi.json`),
143
+ JSON.stringify(schema, null, 2)
144
+ )
72
145
  }
73
146
 
74
147
  if (isFrontend) {
148
+ const typeExt = await determineTypeExtension(folder, moduleFormat, typeExtension, explicitModuleFormat, generateImplementation)
75
149
  const { types, implementation } = processFrontendOpenAPI({
76
150
  schema,
77
151
  name,
@@ -80,9 +154,10 @@ async function writeOpenAPIClient (
80
154
  language,
81
155
  logger,
82
156
  withCredentials,
83
- propsOptional
157
+ propsOptional,
158
+ typeExt
84
159
  })
85
- await writeFile(join(folder, `${name}-types.d.mts`), types)
160
+ await writeFile(join(folder, `${name}-types.${typeExt}`), types)
86
161
  if (generateImplementation) {
87
162
  const extension = language === 'js' ? 'mjs' : 'mts'
88
163
  await writeFile(join(folder, `${name}.${extension}`), implementation)
@@ -96,30 +171,56 @@ async function writeOpenAPIClient (
96
171
  optionalHeaders,
97
172
  validateResponse,
98
173
  typesComment,
99
- propsOptional
174
+ propsOptional,
175
+ moduleFormat,
100
176
  })
101
- await writeFile(join(folder, `${name}.d.mts`), types)
177
+ const typeExt = await determineTypeExtension(folder, moduleFormat, typeExtension, explicitModuleFormat, generateImplementation)
178
+ const implExt = moduleFormat === 'esm' ? 'mjs' : 'cjs'
179
+ await writeFile(join(folder, `${name}.${typeExt}`), types)
102
180
  if (generateImplementation) {
103
- await writeFile(join(folder, `${name}.mjs`), implementation)
181
+ await writeFile(join(folder, `${name}.${implExt}`), implementation)
104
182
  }
105
183
 
106
184
  if (!typesOnly) {
107
- await writeFile(join(folder, 'package.json'), getPackageJSON({ name, generateImplementation }))
185
+ await writeFile(
186
+ join(folder, 'package.json'),
187
+ await getPackageJSON({ name, generateImplementation, moduleFormat, folder, typeExtension, explicitModuleFormat })
188
+ )
108
189
  }
109
190
  }
110
191
  }
111
192
 
112
- async function writeGraphQLClient (folder, name, schema, url, generateImplementation) {
193
+ async function writeGraphQLClient (
194
+ folder,
195
+ name,
196
+ schema,
197
+ url,
198
+ generateImplementation,
199
+ moduleFormat,
200
+ typeExtension,
201
+ explicitModuleFormat
202
+ ) {
113
203
  await createDirectory(folder, { recursive: true })
114
- const { types, implementation } = processGraphQL({ schema, name, folder, url })
204
+ const { types, implementation } = processGraphQL({
205
+ schema,
206
+ name,
207
+ folder,
208
+ url,
209
+ moduleFormat,
210
+ })
115
211
  const clientSchema = graphql.buildClientSchema(schema)
116
212
  const sdl = graphql.printSchema(clientSchema)
213
+ const typeExt = await determineTypeExtension(folder, moduleFormat, typeExtension, explicitModuleFormat, generateImplementation)
214
+ const implExt = moduleFormat === 'esm' ? 'mjs' : 'cjs'
117
215
  await writeFile(join(folder, `${name}.schema.graphql`), sdl)
118
- await writeFile(join(folder, `${name}.d.mts`), types)
216
+ await writeFile(join(folder, `${name}.${typeExt}`), types)
119
217
  if (generateImplementation) {
120
- await writeFile(join(folder, `${name}.mjs`), implementation)
218
+ await writeFile(join(folder, `${name}.${implExt}`), implementation)
121
219
  }
122
- await writeFile(join(folder, 'package.json'), getPackageJSON({ name, generateImplementation }))
220
+ await writeFile(
221
+ join(folder, 'package.json'),
222
+ await getPackageJSON({ name, generateImplementation, moduleFormat, folder, typeExtension, explicitModuleFormat })
223
+ )
123
224
  }
124
225
 
125
226
  async function downloadAndWriteOpenAPI (
@@ -139,7 +240,10 @@ async function downloadAndWriteOpenAPI (
139
240
  typesComment,
140
241
  withCredentials,
141
242
  propsOptional,
142
- retryTimeoutMs
243
+ retryTimeoutMs,
244
+ moduleFormat,
245
+ typeExtension,
246
+ explicitModuleFormat
143
247
  ) {
144
248
  logger.debug(`Trying to download OpenAPI schema from ${url}`)
145
249
  let requestOptions
@@ -152,7 +256,9 @@ async function downloadAndWriteOpenAPI (
152
256
  }
153
257
 
154
258
  const dispatcher = retryTimeoutMs
155
- ? getGlobalDispatcher().compose([interceptors.retry({ minTimeout: retryTimeoutMs })])
259
+ ? getGlobalDispatcher().compose([
260
+ interceptors.retry({ minTimeout: retryTimeoutMs })
261
+ ])
156
262
  : undefined
157
263
  const res = await request(url, { ...requestOptions, dispatcher })
158
264
  if (res.statusCode === 200) {
@@ -174,7 +280,10 @@ async function downloadAndWriteOpenAPI (
174
280
  typesComment,
175
281
  logger,
176
282
  withCredentials,
177
- propsOptional
283
+ propsOptional,
284
+ moduleFormat,
285
+ typeExtension,
286
+ explicitModuleFormat
178
287
  )
179
288
  /* c8 ignore next 3 */
180
289
  } catch (err) {
@@ -188,7 +297,16 @@ async function downloadAndWriteOpenAPI (
188
297
  return false
189
298
  }
190
299
 
191
- async function downloadAndWriteGraphQL (logger, url, folder, name, generateImplementation) {
300
+ async function downloadAndWriteGraphQL (
301
+ logger,
302
+ url,
303
+ folder,
304
+ name,
305
+ generateImplementation,
306
+ moduleFormat,
307
+ typeExtension,
308
+ explicitModuleFormat
309
+ ) {
192
310
  logger.debug(`Trying to download GraphQL schema from ${url}`)
193
311
  const query = graphql.getIntrospectionQuery()
194
312
  const res = await request(url, {
@@ -198,7 +316,7 @@ async function downloadAndWriteGraphQL (logger, url, folder, name, generateImple
198
316
  },
199
317
  body: JSON.stringify({
200
318
  query
201
- })
319
+ }),
202
320
  })
203
321
 
204
322
  const text = await res.body.text()
@@ -208,7 +326,16 @@ async function downloadAndWriteGraphQL (logger, url, folder, name, generateImple
208
326
  }
209
327
 
210
328
  const { data: schema } = JSON.parse(text)
211
- await writeGraphQLClient(folder, name, schema, url, generateImplementation)
329
+ await writeGraphQLClient(
330
+ folder,
331
+ name,
332
+ schema,
333
+ url,
334
+ generateImplementation,
335
+ moduleFormat,
336
+ typeExtension,
337
+ explicitModuleFormat
338
+ )
212
339
  return 'graphql'
213
340
  }
214
341
 
@@ -227,7 +354,10 @@ async function readFromFileAndWrite (
227
354
  language,
228
355
  typesComment,
229
356
  withCredentials,
230
- propsOptional
357
+ propsOptional,
358
+ moduleFormat,
359
+ typeExtension,
360
+ explicitModuleFormat
231
361
  ) {
232
362
  logger.info(`Trying to read schema from file ${file}`)
233
363
  const text = await readFile(file, 'utf8')
@@ -248,17 +378,32 @@ async function readFromFileAndWrite (
248
378
  typesComment,
249
379
  logger,
250
380
  withCredentials,
251
- propsOptional
381
+ propsOptional,
382
+ moduleFormat,
383
+ typeExtension,
384
+ explicitModuleFormat
252
385
  )
253
386
  return 'openapi'
254
387
  } catch (err) {
255
- logger.error(err, `Error parsing OpenAPI definition: "${err.message}". Trying with GraphQL`)
388
+ logger.error(
389
+ err,
390
+ `Error parsing OpenAPI definition: "${err.message}". Trying with GraphQL`
391
+ )
256
392
  // try GraphQL
257
393
  const schema = graphql.buildSchema(text)
258
394
  const introspectionResult = graphql.introspectionFromSchema(schema)
259
395
 
260
396
  // dummy URL
261
- await writeGraphQLClient(folder, name, introspectionResult, 'http://localhost:3042/graphql', generateImplementation)
397
+ await writeGraphQLClient(
398
+ folder,
399
+ name,
400
+ introspectionResult,
401
+ 'http://localhost:3042/graphql',
402
+ generateImplementation,
403
+ moduleFormat,
404
+ typeExtension,
405
+ explicitModuleFormat
406
+ )
262
407
  return 'graphql'
263
408
  }
264
409
  }
@@ -280,7 +425,10 @@ async function downloadAndProcess (options) {
280
425
  typesComment,
281
426
  withCredentials,
282
427
  propsOptional,
283
- retryTimeoutMs
428
+ retryTimeoutMs,
429
+ moduleFormat,
430
+ typeExtension,
431
+ explicitModuleFormat
284
432
  } = options
285
433
 
286
434
  const generateImplementation = options.generateImplementation
@@ -308,7 +456,10 @@ async function downloadAndProcess (options) {
308
456
  typesComment,
309
457
  withCredentials,
310
458
  propsOptional,
311
- retryTimeoutMs
459
+ retryTimeoutMs,
460
+ moduleFormat,
461
+ typeExtension,
462
+ explicitModuleFormat
312
463
  )
313
464
  )
314
465
  toTry.push(
@@ -330,14 +481,39 @@ async function downloadAndProcess (options) {
330
481
  typesComment,
331
482
  withCredentials,
332
483
  propsOptional,
333
- retryTimeoutMs
484
+ retryTimeoutMs,
485
+ moduleFormat,
486
+ typeExtension,
487
+ explicitModuleFormat
334
488
  )
335
489
  )
336
490
  } else if (options.type === 'graphql') {
337
491
  toTry.push(
338
- downloadAndWriteGraphQL.bind(null, logger, url + '/graphql', folder, name, generateImplementation, typesOnly)
492
+ downloadAndWriteGraphQL.bind(
493
+ null,
494
+ logger,
495
+ url + '/graphql',
496
+ folder,
497
+ name,
498
+ generateImplementation,
499
+ moduleFormat,
500
+ typeExtension,
501
+ explicitModuleFormat
502
+ )
503
+ )
504
+ toTry.push(
505
+ downloadAndWriteGraphQL.bind(
506
+ null,
507
+ logger,
508
+ url,
509
+ folder,
510
+ name,
511
+ generateImplementation,
512
+ moduleFormat,
513
+ typeExtension,
514
+ explicitModuleFormat
515
+ )
339
516
  )
340
- toTry.push(downloadAndWriteGraphQL.bind(null, logger, url, folder, name, generateImplementation, typesOnly))
341
517
  } else {
342
518
  // add download functions only if it's an URL
343
519
  toTry.push(
@@ -359,11 +535,24 @@ async function downloadAndProcess (options) {
359
535
  typesComment,
360
536
  withCredentials,
361
537
  propsOptional,
362
- retryTimeoutMs
538
+ retryTimeoutMs,
539
+ moduleFormat,
540
+ typeExtension,
541
+ explicitModuleFormat
363
542
  )
364
543
  )
365
544
  toTry.push(
366
- downloadAndWriteGraphQL.bind(null, logger, url + '/graphql', folder, name, generateImplementation, typesOnly)
545
+ downloadAndWriteGraphQL.bind(
546
+ null,
547
+ logger,
548
+ url + '/graphql',
549
+ folder,
550
+ name,
551
+ generateImplementation,
552
+ moduleFormat,
553
+ typeExtension,
554
+ explicitModuleFormat
555
+ )
367
556
  )
368
557
  toTry.push(
369
558
  downloadAndWriteOpenAPI.bind(
@@ -384,10 +573,25 @@ async function downloadAndProcess (options) {
384
573
  typesComment,
385
574
  withCredentials,
386
575
  propsOptional,
387
- retryTimeoutMs
576
+ retryTimeoutMs,
577
+ moduleFormat,
578
+ typeExtension,
579
+ explicitModuleFormat
580
+ )
581
+ )
582
+ toTry.push(
583
+ downloadAndWriteGraphQL.bind(
584
+ null,
585
+ logger,
586
+ url,
587
+ folder,
588
+ name,
589
+ generateImplementation,
590
+ moduleFormat,
591
+ typeExtension,
592
+ explicitModuleFormat
388
593
  )
389
594
  )
390
- toTry.push(downloadAndWriteGraphQL.bind(null, logger, url, folder, name, generateImplementation, typesOnly))
391
595
  }
392
596
  } else {
393
597
  // add readFromFileAndWrite to the functions only if it's not an URL
@@ -408,7 +612,10 @@ async function downloadAndProcess (options) {
408
612
  language,
409
613
  typesComment,
410
614
  withCredentials,
411
- propsOptional
615
+ propsOptional,
616
+ moduleFormat,
617
+ typeExtension,
618
+ explicitModuleFormat
412
619
  )
413
620
  )
414
621
  }
@@ -420,18 +627,26 @@ async function downloadAndProcess (options) {
420
627
  }
421
628
  /* c8 ignore next 3 */
422
629
  if (!found) {
423
- throw new Error(`Could not find a valid OpenAPI or GraphQL schema at ${url}`)
630
+ throw new Error(
631
+ `Could not find a valid OpenAPI or GraphQL schema at ${url}`
632
+ )
424
633
  }
425
634
  }
426
635
 
427
- function getPackageJSON ({ name, generateImplementation }) {
636
+ async function getPackageJSON ({ name, generateImplementation, moduleFormat, folder, typeExtension, explicitModuleFormat }) {
637
+ const isESM = moduleFormat === 'esm'
638
+ const typeExt = await determineTypeExtension(folder, moduleFormat, typeExtension, explicitModuleFormat, generateImplementation)
428
639
  const obj = {
429
640
  name,
430
- types: `./${name}.d.mts`
641
+ types: `./${name}.${typeExt}`
642
+ }
643
+
644
+ if (isESM) {
645
+ obj.type = 'module'
431
646
  }
432
647
 
433
648
  if (generateImplementation) {
434
- obj.main = `./${name}.mjs`
649
+ obj.main = `./${name}.${isESM ? 'mjs' : 'cjs'}`
435
650
  }
436
651
 
437
652
  return JSON.stringify(obj, null, 2)
@@ -447,7 +662,16 @@ export async function command (argv) {
447
662
  _: [url],
448
663
  ...options
449
664
  } = parseArgs(argv, {
450
- string: ['name', 'folder', 'optional-headers', 'language', 'type', 'url-auth-headers', 'types-comment'],
665
+ string: [
666
+ 'name',
667
+ 'folder',
668
+ 'optional-headers',
669
+ 'language',
670
+ 'type',
671
+ 'url-auth-headers',
672
+ 'types-comment',
673
+ 'module'
674
+ ],
451
675
  boolean: [
452
676
  'typescript',
453
677
  'full-response',
@@ -457,7 +681,8 @@ export async function command (argv) {
457
681
  'frontend',
458
682
  'validate-response',
459
683
  'props-optional',
460
- 'skip-config-update'
684
+ 'skip-config-update',
685
+ 'type-extension'
461
686
  ],
462
687
  default: {
463
688
  typescript: false,
@@ -507,7 +732,7 @@ export async function command (argv) {
507
732
  options.propsOptional = options['props-optional'] ?? true
508
733
 
509
734
  options.optionalHeaders = options['optional-headers']
510
- ? options['optional-headers'].split(',').map(h => h.trim())
735
+ ? options['optional-headers'].split(',').map((h) => h.trim())
511
736
  : []
512
737
 
513
738
  options.validateResponse = options['validate-response']
@@ -516,11 +741,17 @@ export async function command (argv) {
516
741
  options.name = options.isFrontend ? 'api' : 'client'
517
742
  }
518
743
  options.folder = options.folder || join(process.cwd(), options.name)
744
+ options.moduleFormat = await detectModuleFormat(options.folder, options.module)
745
+ if (!options.module) {
746
+ logger.info(`Module format detected: ${options.moduleFormat}`)
747
+ }
519
748
  options.urlAuthHeaders = options['url-auth-headers']
520
749
  options.typesComment = options['types-comment']
521
750
  options.withCredentials = options['with-credentials']
522
751
  options.skipConfigUpdate = options['skip-config-update'] ?? true
523
752
  options.retryTimeoutMs = options['retry-timeout-ms']
753
+ options.typeExtension = options['type-extension']
754
+ options.explicitModuleFormat = !!options.module
524
755
  await downloadAndProcess({ url, ...options, logger })
525
756
  logger.info(`Client generated successfully into ${options.folder}`)
526
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)`)
@@ -2,11 +2,11 @@ import CodeBlockWriter from 'code-block-writer'
2
2
  import { UnknownTypeError } from './errors.js'
3
3
  import { capitalize, toJavaScriptName } from './utils.js'
4
4
 
5
- export function processGraphQL ({ schema, name, folder, url }) {
5
+ export function processGraphQL ({ schema, name, folder, url, moduleFormat }) {
6
6
  schema = schema.__schema
7
7
  return {
8
8
  types: generateTypesFromGraphQL({ schema, name }),
9
- implementation: generateImplementationFromGraqhQL({ schema, name, url })
9
+ implementation: generateImplementationFromGraqhQL({ schema, name, url, moduleFormat })
10
10
  }
11
11
  }
12
12
 
@@ -60,8 +60,9 @@ function generateTypesFromGraphQL ({ schema, name }) {
60
60
  return writer.toString()
61
61
  }
62
62
 
63
- function generateImplementationFromGraqhQL ({ name, url }) {
63
+ function generateImplementationFromGraqhQL ({ name, url, moduleFormat }) {
64
64
  const camelcasedName = toJavaScriptName(name)
65
+ const isESM = moduleFormat === 'esm'
65
66
 
66
67
  const writer = new CodeBlockWriter({
67
68
  indentNumberOfSpaces: 2,
@@ -69,27 +70,40 @@ function generateImplementationFromGraqhQL ({ name, url }) {
69
70
  useSingleQuote: true
70
71
  })
71
72
 
72
- writer.writeLine("import { buildGraphQLClient } from 'massimo'")
73
- writer.writeLine("import { join } from 'node:path'")
73
+ if (isESM) {
74
+ writer.writeLine("import { buildGraphQLClient } from 'massimo'")
75
+ writer.writeLine("import { join } from 'node:path'")
76
+ } else {
77
+ writer.writeLine("const { buildGraphQLClient } = require('massimo')")
78
+ writer.writeLine("const { join } = require('node:path')")
79
+ }
74
80
  writer.blankLine()
75
81
 
76
82
  url = new URL(url)
77
83
 
78
84
  const functionName = `generate${capitalize(camelcasedName)}Client`
79
- writer.write(`export async function ${functionName} (opts)`).block(() => {
85
+ const funcDecl = isESM ? `export async function ${functionName} (opts)` : `async function ${functionName} (opts)`
86
+ writer.write(funcDecl).block(() => {
80
87
  writer.writeLine('const url = new URL(opts.url)')
81
88
  writer.writeLine(`url.pathname = '${url.pathname}'`)
82
89
  writer.write('return buildGraphQLClient(').inlineBlock(() => {
83
90
  writer.writeLine("type: 'graphql',")
84
91
  writer.writeLine(`name: '${camelcasedName}',`)
85
- writer.writeLine(`path: join(import.meta.dirname, '${name}.schema.graphql'),`)
92
+ const pathExpr = isESM ? `join(import.meta.dirname, '${name}.schema.graphql')` : `join(__dirname, '${name}.schema.graphql')`
93
+ writer.writeLine(`path: ${pathExpr},`)
86
94
  writer.writeLine('serviceId: opts.serviceId,')
87
95
  writer.writeLine('url: url.toString()')
88
96
  })
89
97
  writer.write(')')
90
98
  })
91
99
  writer.blankLine()
92
- writer.writeLine(`export default ${functionName}`)
100
+ if (isESM) {
101
+ writer.writeLine(`export default ${functionName}`)
102
+ } else {
103
+ writer.writeLine(`module.exports = ${functionName}`)
104
+ writer.writeLine(`module.exports.default = ${functionName}`)
105
+ writer.writeLine(`module.exports.${functionName} = ${functionName}`)
106
+ }
93
107
  return writer.toString()
94
108
  }
95
109
 
@@ -11,7 +11,8 @@ export function processOpenAPI ({
11
11
  optionalHeaders,
12
12
  validateResponse,
13
13
  typesComment,
14
- propsOptional
14
+ propsOptional,
15
+ moduleFormat
15
16
  }) {
16
17
  return {
17
18
  types: generateTypesFromOpenAPI({
@@ -23,12 +24,13 @@ export function processOpenAPI ({
23
24
  typesComment,
24
25
  propsOptional
25
26
  }),
26
- implementation: generateImplementationFromOpenAPI({ name, fullResponse, fullRequest, validateResponse })
27
+ implementation: generateImplementationFromOpenAPI({ name, fullResponse, fullRequest, validateResponse, moduleFormat })
27
28
  }
28
29
  }
29
30
 
30
- function generateImplementationFromOpenAPI ({ name, fullResponse, fullRequest, validateResponse }) {
31
+ function generateImplementationFromOpenAPI ({ name, fullResponse, fullRequest, validateResponse, moduleFormat }) {
31
32
  const camelcasedName = toJavaScriptName(name)
33
+ const isESM = moduleFormat === 'esm'
32
34
 
33
35
  const writer = new CodeBlockWriter({
34
36
  indentNumberOfSpaces: 2,
@@ -36,16 +38,23 @@ function generateImplementationFromOpenAPI ({ name, fullResponse, fullRequest, v
36
38
  useSingleQuote: true
37
39
  })
38
40
 
39
- writer.writeLine("import { buildOpenAPIClient } from 'massimo'")
40
- writer.writeLine("import { join } from 'node:path'")
41
+ if (isESM) {
42
+ writer.writeLine("import { buildOpenAPIClient } from 'massimo'")
43
+ writer.writeLine("import { join } from 'node:path'")
44
+ } else {
45
+ writer.writeLine("const { buildOpenAPIClient } = require('massimo')")
46
+ writer.writeLine("const { join } = require('node:path')")
47
+ }
41
48
  writer.blankLine()
42
49
 
43
50
  const functionName = `generate${capitalize(camelcasedName)}Client`
44
- writer.write(`export async function ${functionName} (opts)`).block(() => {
51
+ const funcDecl = isESM ? `export async function ${functionName} (opts)` : `async function ${functionName} (opts)`
52
+ writer.write(funcDecl).block(() => {
45
53
  writer.write('return buildOpenAPIClient(').inlineBlock(() => {
46
54
  writer.writeLine("type: 'openapi',")
47
55
  writer.writeLine(`name: '${camelcasedName}',`)
48
- writer.writeLine(`path: join(import.meta.dirname, '${name}.openapi.json'),`)
56
+ const pathExpr = isESM ? `join(import.meta.dirname, '${name}.openapi.json')` : `join(__dirname, '${name}.openapi.json')`
57
+ writer.writeLine(`path: ${pathExpr},`)
49
58
  writer.writeLine('url: opts.url,')
50
59
  writer.writeLine('serviceId: opts.serviceId,')
51
60
  writer.writeLine('throwOnError: opts.throwOnError,')
@@ -57,7 +66,13 @@ function generateImplementationFromOpenAPI ({ name, fullResponse, fullRequest, v
57
66
  writer.write(')')
58
67
  })
59
68
  writer.blankLine()
60
- writer.writeLine(`export default ${functionName}`)
69
+ if (isESM) {
70
+ writer.writeLine(`export default ${functionName}`)
71
+ } else {
72
+ writer.writeLine(`module.exports = ${functionName}`)
73
+ writer.writeLine(`module.exports.default = ${functionName}`)
74
+ writer.writeLine(`module.exports.${functionName} = ${functionName}`)
75
+ }
61
76
  return writer.toString()
62
77
  }
63
78
 
@@ -68,7 +83,7 @@ function generateTypesFromOpenAPI ({
68
83
  fullRequest,
69
84
  optionalHeaders,
70
85
  typesComment,
71
- propsOptional
86
+ propsOptional,
72
87
  }) {
73
88
  const camelcasedName = toJavaScriptName(name)
74
89
  const capitalizedName = capitalize(camelcasedName)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "massimo-cli",
3
- "version": "0.3.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.3.0"
33
+ "massimo": "0.5.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@platformatic/composer": "3.0.0-alpha.6",