@atproto/lex-builder 0.1.0 → 0.1.2

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.
@@ -15,6 +15,7 @@ import {
15
15
  LexiconError,
16
16
  LexiconIndexer,
17
17
  LexiconInteger,
18
+ LexiconMessage,
18
19
  LexiconObject,
19
20
  LexiconParameters,
20
21
  LexiconPayload,
@@ -50,15 +51,6 @@ export type LexDefBuilderOptions = RefResolverOptions & {
50
51
  * @default '@atproto/lex-schema'
51
52
  */
52
53
  lib?: string
53
- /**
54
- * Whether to add `#__PURE__` annotations to function calls.
55
- *
56
- * These annotations help bundlers with tree-shaking by marking
57
- * side-effect-free function calls.
58
- *
59
- * @default false
60
- */
61
- pureAnnotations?: boolean
62
54
  }
63
55
 
64
56
  /**
@@ -86,10 +78,6 @@ export class LexDefBuilder {
86
78
  this.refResolver = new RefResolver(doc, file, indexer, options)
87
79
  }
88
80
 
89
- private pure(code: string) {
90
- return this.options.pureAnnotations ? markPure(code) : code
91
- }
92
-
93
81
  async build() {
94
82
  this.file.addVariableStatement({
95
83
  declarationKind: VariableDeclarationKind.Const,
@@ -116,17 +104,13 @@ export class LexDefBuilder {
116
104
  }
117
105
 
118
106
  private addUtils(definitions: Record<string, undefined | string>) {
119
- const entries = Object.entries(definitions).filter(
120
- (e): e is [(typeof e)[0], NonNullable<(typeof e)[1]>] => e[1] != null,
121
- )
122
- if (entries.length) {
107
+ for (const [name, initializer] of Object.entries(definitions)) {
108
+ if (initializer == null) continue
109
+
123
110
  this.file.addVariableStatement({
124
111
  isExported: true,
125
112
  declarationKind: VariableDeclarationKind.Const,
126
- declarations: entries.map(([name, initializer]) => ({
127
- name,
128
- initializer,
129
- })),
113
+ declarations: [{ name, initializer }],
130
114
  })
131
115
  }
132
116
  }
@@ -164,7 +148,7 @@ export class LexDefBuilder {
164
148
  private async addPermissionSet(hash: string, def: LexiconPermissionSet) {
165
149
  const permission = def.permissions.map((def) => {
166
150
  const options = stringifyOptions(def, undefined, ['resource', 'type'])
167
- return this.pure(
151
+ return markPure(
168
152
  `l.permission(${JSON.stringify(def.resource)}, ${options})`,
169
153
  )
170
154
  })
@@ -177,143 +161,175 @@ export class LexDefBuilder {
177
161
  ] satisfies (keyof l.PermissionSetOptions)[])
178
162
 
179
163
  await this.addSchema(hash, def, {
180
- schema: this.pure(
164
+ schema: markPure(
181
165
  `l.permissionSet($nsid, [${permission.join(',')}], ${options})`,
182
166
  ),
183
167
  })
184
168
  }
185
169
 
186
- private async addProcedure(hash: string, def: LexiconProcedure) {
187
- if (hash !== 'main') {
188
- throw new Error(`Definition ${hash} cannot be of type ${def.type}`)
189
- }
170
+ private async addParameters(parameters?: LexiconParameters): Promise<string> {
171
+ const varName = '$params'
190
172
 
191
- // @TODO Build the types instead of using an inferred type.
173
+ this.addUtils({
174
+ [varName]: await this.compileParamsSchema(parameters),
175
+ })
192
176
 
193
- const ref = await this.addSchema(hash, def, {
194
- schema: this.pure(`
195
- l.procedure(
196
- $nsid,
197
- ${await this.compileParamsSchema(def.parameters)},
198
- ${await this.compilePayload(def.input)},
199
- ${await this.compilePayload(def.output)},
200
- ${await this.compileErrors(def.errors)}
201
- )
202
- `),
177
+ // @TODO Build the types instead of using an inferred type.
178
+ this.file.addTypeAlias({
179
+ isExported: true,
180
+ name: '$Params',
181
+ type: `l.InferOutput<typeof ${varName}>`,
182
+ docs: compileDocs(parameters?.description),
203
183
  })
204
184
 
205
- this.addMethodTypeUtils(ref, def)
185
+ return varName
186
+ }
187
+
188
+ private async addInput(input?: LexiconPayload): Promise<string> {
189
+ const varName = '$input'
190
+
206
191
  this.addUtils({
207
- $lxm: this.pure(`${ref.varName}.nsid`),
208
- $params: this.pure(`${ref.varName}.parameters`),
209
- $input: this.pure(`${ref.varName}.input`),
210
- $output: this.pure(`${ref.varName}.output`),
192
+ [varName]: await this.compilePayload(input),
193
+ })
194
+
195
+ // @TODO Build the types instead of using an inferred type.
196
+ this.file.addTypeAlias({
197
+ isExported: true,
198
+ name: '$Input<B = l.BinaryData>',
199
+ type: `l.InferPayload<typeof ${varName}, B>`,
200
+ docs: compileDocs(input?.description),
211
201
  })
202
+
203
+ this.file.addTypeAlias({
204
+ isExported: true,
205
+ name: '$InputBody<B = l.BinaryData>',
206
+ type: `l.InferPayloadBody<typeof ${varName}, B>`,
207
+ docs: compileDocs(input?.description),
208
+ })
209
+
210
+ return varName
212
211
  }
213
212
 
214
- private async addQuery(hash: string, def: LexiconQuery) {
215
- if (hash !== 'main') {
216
- throw new Error(`Definition ${hash} cannot be of type ${def.type}`)
217
- }
213
+ private async addOutput(output?: LexiconPayload): Promise<string> {
214
+ const varName = '$output'
215
+
216
+ this.addUtils({
217
+ [varName]: await this.compilePayload(output),
218
+ })
218
219
 
219
220
  // @TODO Build the types instead of using an inferred type.
221
+ this.file.addTypeAlias({
222
+ isExported: true,
223
+ name: '$Output<B = l.BinaryData>',
224
+ type: `l.InferPayload<typeof ${varName}, B>`,
225
+ docs: compileDocs(output?.description),
226
+ })
220
227
 
221
- const ref = await this.addSchema(hash, def, {
222
- schema: this.pure(`
223
- l.query(
224
- $nsid,
225
- ${await this.compileParamsSchema(def.parameters)},
226
- ${await this.compilePayload(def.output)},
227
- ${await this.compileErrors(def.errors)}
228
- )
229
- `),
228
+ this.file.addTypeAlias({
229
+ isExported: true,
230
+ name: '$OutputBody<B = l.BinaryData>',
231
+ type: `l.InferPayloadBody<typeof ${varName}, B>`,
232
+ docs: compileDocs(output?.description),
230
233
  })
231
234
 
232
- this.addMethodTypeUtils(ref, def)
235
+ return varName
236
+ }
237
+
238
+ private async addMessage(message?: LexiconMessage) {
239
+ const varName = '$message'
240
+
233
241
  this.addUtils({
234
- $lxm: this.pure(`${ref.varName}.nsid`),
235
- $params: `${ref.varName}.parameters`,
236
- $output: `${ref.varName}.output`,
242
+ [varName]: await this.compileBodySchema(message?.schema),
237
243
  })
244
+
245
+ // @TODO Build the types instead of using an inferred type.
246
+ this.file.addTypeAlias({
247
+ isExported: true,
248
+ name: '$Message',
249
+ type: `l.InferOutput<typeof ${varName}>`,
250
+ docs: compileDocs(message?.description),
251
+ })
252
+
253
+ return varName
238
254
  }
239
255
 
240
- private async addSubscription(hash: string, def: LexiconSubscription) {
256
+ private async addProcedure(hash: string, def: LexiconProcedure) {
241
257
  if (hash !== 'main') {
242
258
  throw new Error(`Definition ${hash} cannot be of type ${def.type}`)
243
259
  }
244
260
 
245
- // @TODO Build the types instead of using an inferred type.
261
+ // Declare each piece of the method as its own top-level exported const
262
+ // *before* `main`. This allows to export those pieces individually instead
263
+ // of "extracting" them from the "main" definition as below, which is bad
264
+ // for tree-shaking.
265
+ //
266
+ // export const $params = main.params`
267
+
268
+ const paramsVar = await this.addParameters(def.parameters)
269
+ const inputVar = await this.addInput(def.input)
270
+ const outputVar = await this.addOutput(def.output)
246
271
 
247
- const ref = await this.addSchema(hash, def, {
248
- schema: this.pure(`
249
- l.subscription(
250
- $nsid,
251
- ${await this.compileParamsSchema(def.parameters)},
252
- ${await this.compileBodySchema(def.message?.schema)},
253
- ${await this.compileErrors(def.errors)}
254
- )
255
- `),
272
+ await this.addSchema(hash, def, {
273
+ schema: markPure(
274
+ `l.procedure($nsid, ${paramsVar}, ${inputVar}, ${outputVar}${formatErrorsArg(await this.compileErrors(def.errors))})`,
275
+ ),
256
276
  })
257
277
 
258
- this.addMethodTypeUtils(ref, def)
259
278
  this.addUtils({
260
- $lxm: this.pure(`${ref.varName}.nsid`),
261
- $params: `${ref.varName}.parameters`,
262
- $message: `${ref.varName}.message`,
279
+ $lxm: '$nsid',
263
280
  })
264
281
  }
265
282
 
266
- addMethodTypeUtils(
267
- ref: ResolvedRef,
268
- def: LexiconProcedure | LexiconQuery | LexiconSubscription,
269
- ) {
270
- this.file.addTypeAlias({
271
- isExported: true,
272
- name: '$Params',
273
- type: `l.InferMethodParams<typeof ${ref.varName}>`,
274
- docs: compileDocs(def.parameters?.description),
283
+ private async addQuery(hash: string, def: LexiconQuery) {
284
+ if (hash !== 'main') {
285
+ throw new Error(`Definition ${hash} cannot be of type ${def.type}`)
286
+ }
287
+
288
+ // Declare each piece of the method as its own top-level exported const
289
+ // *before* `main`. This allows to export those pieces individually instead
290
+ // of "extracting" them from the "main" definition as below, which is bad
291
+ // for tree-shaking:
292
+ //
293
+ // export const $params = main.params
294
+
295
+ const paramsVar = await this.addParameters(def.parameters)
296
+ const outputVar = await this.addOutput(def.output)
297
+
298
+ await this.addSchema(hash, def, {
299
+ schema: markPure(
300
+ `l.query($nsid, ${paramsVar}, ${outputVar}${formatErrorsArg(await this.compileErrors(def.errors))})`,
301
+ ),
275
302
  })
276
303
 
277
- if (def.type === 'procedure') {
278
- this.file.addTypeAlias({
279
- isExported: true,
280
- name: '$Input<B = l.BinaryData>',
281
- type: `l.InferMethodInput<typeof ${ref.varName}, B>`,
282
- docs: compileDocs(def.input?.description),
283
- })
304
+ this.addUtils({
305
+ $lxm: '$nsid',
306
+ })
307
+ }
284
308
 
285
- this.file.addTypeAlias({
286
- isExported: true,
287
- name: '$InputBody<B = l.BinaryData>',
288
- type: `l.InferMethodInputBody<typeof ${ref.varName}, B>`,
289
- docs: compileDocs(def.input?.description),
290
- })
309
+ private async addSubscription(hash: string, def: LexiconSubscription) {
310
+ if (hash !== 'main') {
311
+ throw new Error(`Definition ${hash} cannot be of type ${def.type}`)
291
312
  }
292
313
 
293
- if (def.type === 'procedure' || def.type === 'query') {
294
- this.file.addTypeAlias({
295
- isExported: true,
296
- name: '$Output<B = l.BinaryData>',
297
- type: `l.InferMethodOutput<typeof ${ref.varName}, B>`,
298
- docs: compileDocs(def.output?.description),
299
- })
314
+ // Declare each piece of the method as its own top-level exported const
315
+ // *before* `main`. This allows to export those pieces individually instead
316
+ // of "extracting" them from the "main" definition as below, which is bad
317
+ // for tree-shaking.
318
+ //
319
+ // export const $params = main.params`
300
320
 
301
- this.file.addTypeAlias({
302
- isExported: true,
303
- name: '$OutputBody<B = l.BinaryData>',
304
- type: `l.InferMethodOutputBody<typeof ${ref.varName}, B>`,
305
- docs: compileDocs(def.output?.description),
306
- })
307
- }
321
+ const paramsVar = await this.addParameters(def.parameters)
322
+ const messageVar = await this.addMessage(def.message)
308
323
 
309
- if (def.type === 'subscription') {
310
- this.file.addTypeAlias({
311
- isExported: true,
312
- name: '$Message',
313
- type: `l.InferSubscriptionMessage<typeof ${ref.varName}>`,
314
- docs: compileDocs(def.message?.description),
315
- })
316
- }
324
+ await this.addSchema(hash, def, {
325
+ schema: markPure(
326
+ `l.subscription($nsid, ${paramsVar}, ${messageVar}${formatErrorsArg(await this.compileErrors(def.errors))})`,
327
+ ),
328
+ })
329
+
330
+ this.addUtils({
331
+ $lxm: '$nsid',
332
+ })
317
333
  }
318
334
 
319
335
  private async addRecord(hash: string, def: LexiconRecord) {
@@ -330,7 +346,7 @@ export class LexDefBuilder {
330
346
  await this.addSchema(hash, def, {
331
347
  type: `{ ${properties.join(';')} }`,
332
348
  schema: (ref) =>
333
- this.pure(
349
+ markPure(
334
350
  `l.record<${key}, ${ref.typeName}>(${key}, $nsid, ${objectSchema})`,
335
351
  ),
336
352
  objectUtils: true,
@@ -347,7 +363,7 @@ export class LexDefBuilder {
347
363
  await this.addSchema(hash, def, {
348
364
  type: `{ ${properties.join(';')} }`,
349
365
  schema: (ref) =>
350
- this.pure(
366
+ markPure(
351
367
  `l.typedObject<${ref.typeName}>($nsid, ${JSON.stringify(hash)}, ${objectSchema})`,
352
368
  ),
353
369
  objectUtils: true,
@@ -357,7 +373,7 @@ export class LexDefBuilder {
357
373
 
358
374
  private async addToken(hash: string, def: LexiconToken) {
359
375
  await this.addSchema(hash, def, {
360
- schema: this.pure(`l.token($nsid, ${JSON.stringify(hash)})`),
376
+ schema: markPure(`l.token($nsid, ${JSON.stringify(hash)})`),
361
377
  type: JSON.stringify(l.$type(this.doc.id, hash)),
362
378
  validationUtils: true,
363
379
  })
@@ -379,9 +395,7 @@ export class LexDefBuilder {
379
395
  // @NOTE Not using compileArraySchema to allow specifying the generic
380
396
  // parameter to l.array<>.
381
397
  schema: (ref) =>
382
- this.pure(
383
- `l.array<${ref.typeName}[number]>(${itemSchema}, ${options})`,
384
- ),
398
+ markPure(`l.array<${ref.typeName}[number]>(${itemSchema}, ${options})`),
385
399
  validationUtils: true,
386
400
  })
387
401
  }
@@ -452,9 +466,9 @@ export class LexDefBuilder {
452
466
 
453
467
  if (hash === 'main' && objectUtils) {
454
468
  this.addUtils({
469
+ $type: `$nsid`,
455
470
  $isTypeOf: markPure(`${ref.varName}.isTypeOf.bind(${ref.varName})`),
456
471
  $build: markPure(`${ref.varName}.build.bind(${ref.varName})`),
457
- $type: markPure(`${ref.varName}.$type`),
458
472
  })
459
473
  }
460
474
 
@@ -478,20 +492,20 @@ export class LexDefBuilder {
478
492
  }
479
493
 
480
494
  private async compilePayload(def: LexiconPayload | undefined) {
481
- if (!def) return this.pure(`l.payload()`)
495
+ if (!def) return markPure(`l.payload()`)
482
496
 
483
497
  // Special case for JSON object payloads
484
498
  if (def.encoding === 'application/json' && def.schema?.type === 'object') {
485
499
  const properties = await this.compilePropertiesSchemas(def.schema)
486
- return this.pure(`l.jsonPayload({${properties.join(',')}})`)
500
+ return markPure(`l.jsonPayload({${properties.join(',')}})`)
487
501
  }
488
502
 
489
503
  const encodedEncoding = JSON.stringify(def.encoding)
490
504
  if (def.schema) {
491
505
  const bodySchema = await this.compileBodySchema(def.schema)
492
- return this.pure(`l.payload(${encodedEncoding}, ${bodySchema})`)
506
+ return markPure(`l.payload(${encodedEncoding}, ${bodySchema})`)
493
507
  } else {
494
- return this.pure(`l.payload(${encodedEncoding})`)
508
+ return markPure(`l.payload(${encodedEncoding})`)
495
509
  }
496
510
  }
497
511
 
@@ -504,10 +518,10 @@ export class LexDefBuilder {
504
518
  }
505
519
 
506
520
  private async compileParamsSchema(def: undefined | LexiconParameters) {
507
- if (!def) return this.pure(`l.params()`)
521
+ if (!def) return markPure(`l.params()`)
508
522
 
509
523
  const properties = await this.compilePropertiesSchemas(def)
510
- return this.pure(
524
+ return markPure(
511
525
  properties.length === 0
512
526
  ? `l.params()`
513
527
  : `l.params({${properties.join(',')}})`,
@@ -521,7 +535,7 @@ export class LexDefBuilder {
521
535
 
522
536
  private async compileObjectSchema(def: LexiconObject): Promise<string> {
523
537
  const properties = await this.compilePropertiesSchemas(def)
524
- return this.pure(`l.object({${properties.join(',')}})`)
538
+ return markPure(`l.object({${properties.join(',')}})`)
525
539
  }
526
540
 
527
541
  private async compilePropertiesSchemas(options: {
@@ -571,11 +585,11 @@ export class LexDefBuilder {
571
585
  let schema = await this.compileContainedSchema(def)
572
586
 
573
587
  if (isNullable) {
574
- schema = this.pure(`l.nullable(${schema})`)
588
+ schema = markPure(`l.nullable(${schema})`)
575
589
  }
576
590
 
577
591
  if (!isRequired) {
578
- schema = this.pure(`l.optional(${schema})`)
592
+ schema = markPure(`l.optional(${schema})`)
579
593
  }
580
594
 
581
595
  return `${JSON.stringify(key)}:${schema}`
@@ -667,7 +681,7 @@ export class LexDefBuilder {
667
681
  'minLength',
668
682
  'maxLength',
669
683
  ] satisfies (keyof l.ArraySchemaOptions)[])
670
- return this.pure(`l.array(${itemSchema}, ${options})`)
684
+ return markPure(`l.array(${itemSchema}, ${options})`)
671
685
  }
672
686
 
673
687
  private async compileArrayType(def: LexiconArray): Promise<string> {
@@ -675,7 +689,7 @@ export class LexDefBuilder {
675
689
  }
676
690
 
677
691
  private async compileUnknownSchema(_def: LexiconUnknown): Promise<string> {
678
- return this.pure(`l.lexMap()`)
692
+ return markPure(`l.lexMap()`)
679
693
  }
680
694
 
681
695
  private async compileUnknownType(_def: LexiconUnknown): Promise<string> {
@@ -685,9 +699,7 @@ export class LexDefBuilder {
685
699
  private withDefault(schema: string, defaultValue: unknown) {
686
700
  if (defaultValue === undefined) return schema
687
701
 
688
- return this.pure(
689
- `l.withDefault(${schema}, ${JSON.stringify(defaultValue)})`,
690
- )
702
+ return markPure(`l.withDefault(${schema}, ${JSON.stringify(defaultValue)})`)
691
703
  }
692
704
 
693
705
  private async compileBooleanSchema(def: LexiconBoolean): Promise<string> {
@@ -699,7 +711,7 @@ export class LexDefBuilder {
699
711
 
700
712
  if (hasConst(def)) return this.compileConstSchema(def)
701
713
 
702
- return this.withDefault(this.pure(`l.boolean()`), def.default)
714
+ return this.withDefault(markPure(`l.boolean()`), def.default)
703
715
  }
704
716
 
705
717
  private async compileBooleanType(def: LexiconBoolean): Promise<string> {
@@ -730,7 +742,7 @@ export class LexDefBuilder {
730
742
  'minimum',
731
743
  ] satisfies (keyof l.IntegerSchemaOptions)[])
732
744
 
733
- return this.withDefault(this.pure(`l.integer(${options})`), def.default)
745
+ return this.withDefault(markPure(`l.integer(${options})`), def.default)
734
746
  }
735
747
 
736
748
  private async compileIntegerType(def: LexiconInteger): Promise<string> {
@@ -782,7 +794,7 @@ export class LexDefBuilder {
782
794
  : undefined
783
795
 
784
796
  return this.withDefault(
785
- this.pure(`l.string${generic ? `<${generic}>` : ''}(${options})`),
797
+ markPure(`l.string${generic ? `<${generic}>` : ''}(${options})`),
786
798
  def.default,
787
799
  )
788
800
  }
@@ -835,7 +847,7 @@ export class LexDefBuilder {
835
847
  'minLength',
836
848
  'maxLength',
837
849
  ] satisfies (keyof l.BytesSchemaOptions)[])
838
- return this.pure(`l.bytes(${options})`)
850
+ return markPure(`l.bytes(${options})`)
839
851
  }
840
852
 
841
853
  private async compileBytesType(_def: LexiconBytes): Promise<string> {
@@ -847,7 +859,7 @@ export class LexDefBuilder {
847
859
  'maxSize',
848
860
  'accept',
849
861
  ] satisfies (keyof l.BlobSchemaOptions)[])
850
- return this.pure(`l.blob(${options})`)
862
+ return markPure(`l.blob(${options})`)
851
863
  }
852
864
 
853
865
  private async compileBlobType(_def: LexiconBlob): Promise<string> {
@@ -855,7 +867,7 @@ export class LexDefBuilder {
855
867
  }
856
868
 
857
869
  private async compileCidLinkSchema(_def: LexiconCid): Promise<string> {
858
- return this.pure(`l.cid()`)
870
+ return markPure(`l.cid()`)
859
871
  }
860
872
 
861
873
  private async compileCidLinkType(_def: LexiconCid): Promise<string> {
@@ -866,7 +878,7 @@ export class LexDefBuilder {
866
878
  const { varName, typeName } = await this.refResolver.resolve(def.ref)
867
879
  // @NOTE "as any" is needed in schemas with circular refs as TypeScript
868
880
  // cannot infer the type of a value that depends on its initializer type
869
- return this.pure(`l.ref<${typeName}>((() => ${varName}) as any)`)
881
+ return markPure(`l.ref<${typeName}>((() => ${varName}) as any)`)
870
882
  }
871
883
 
872
884
  private async compileRefType(def: LexiconRef): Promise<string> {
@@ -876,7 +888,7 @@ export class LexDefBuilder {
876
888
 
877
889
  private async compileRefUnionSchema(def: LexiconRefUnion): Promise<string> {
878
890
  if (def.refs.length === 0 && def.closed) {
879
- return this.pure(`l.never()`)
891
+ return markPure(`l.never()`)
880
892
  }
881
893
 
882
894
  const refs = await Promise.all(
@@ -884,13 +896,11 @@ export class LexDefBuilder {
884
896
  const { varName, typeName } = await this.refResolver.resolve(ref)
885
897
  // @NOTE "as any" is needed in schemas with circular refs as TypeScript
886
898
  // cannot infer the type of a value that depends on its initializer type
887
- return this.pure(`l.typedRef<${typeName}>((() => ${varName}) as any)`)
899
+ return markPure(`l.typedRef<${typeName}>((() => ${varName}) as any)`)
888
900
  }),
889
901
  )
890
902
 
891
- return this.pure(
892
- `l.typedUnion([${refs.join(',')}], ${def.closed ?? false})`,
893
- )
903
+ return markPure(`l.typedUnion([${refs.join(',')}], ${def.closed ?? false})`)
894
904
  }
895
905
 
896
906
  private async compileRefUnionType(def: LexiconRefUnion): Promise<string> {
@@ -908,10 +918,10 @@ export class LexDefBuilder {
908
918
  T extends null | number | string | boolean,
909
919
  >(def: { const: T; enum?: readonly T[]; default?: T }): Promise<string> {
910
920
  if (hasEnum(def) && !def.enum.includes(def.const)) {
911
- return this.pure(`l.never()`)
921
+ return markPure(`l.never()`)
912
922
  }
913
923
 
914
- const result = this.pure(`l.literal(${JSON.stringify(def.const)})`)
924
+ const result = markPure(`l.literal(${JSON.stringify(def.const)})`)
915
925
 
916
926
  return this.withDefault(result, def.default)
917
927
  }
@@ -930,13 +940,13 @@ export class LexDefBuilder {
930
940
  default?: T
931
941
  }): Promise<string> {
932
942
  if (def.enum.length === 0) {
933
- return this.pure(`l.never()`)
943
+ return markPure(`l.never()`)
934
944
  }
935
945
 
936
946
  const result =
937
947
  def.enum.length === 1
938
- ? this.pure(`l.literal(${JSON.stringify(def.enum[0])})`)
939
- : this.pure(`l.enum(${JSON.stringify(def.enum)})`)
948
+ ? markPure(`l.literal(${JSON.stringify(def.enum[0])})`)
949
+ : markPure(`l.enum(${JSON.stringify(def.enum)})`)
940
950
 
941
951
  return this.withDefault(result, def.default)
942
952
  }
@@ -1019,3 +1029,7 @@ function hasEnum<T extends { enum?: readonly unknown[] }>(
1019
1029
  function markPure<T extends string>(v: T): `/*#__PURE__*/ ${T}` {
1020
1030
  return `/*#__PURE__*/ ${v}`
1021
1031
  }
1032
+
1033
+ function formatErrorsArg(errors: string) {
1034
+ return errors ? `, ${errors}` : ''
1035
+ }