prismic-ts-codegen 0.0.7 → 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.
@@ -132,7 +132,10 @@ const addInterfacePropertyForField = (
132
132
  }
133
133
 
134
134
  case "Image": {
135
- if (config.model.config.thumbnails.length > 0) {
135
+ if (
136
+ config.model.config?.thumbnails &&
137
+ config.model.config.thumbnails.length > 0
138
+ ) {
136
139
  const thumbnailNames = config.model.config.thumbnails
137
140
  .map((thumbnail) => `"${thumbnail.name}"`)
138
141
  .join(" | ");
@@ -150,7 +153,7 @@ const addInterfacePropertyForField = (
150
153
  } else {
151
154
  config.interface.addProperty({
152
155
  name: config.id,
153
- type: "prismicT.ImageField<null>",
156
+ type: "prismicT.ImageField<never>",
154
157
  docs: buildFieldDocs({
155
158
  id: config.id,
156
159
  model: config.model,
@@ -164,10 +167,11 @@ const addInterfacePropertyForField = (
164
167
  }
165
168
 
166
169
  case "IntegrationFields": {
167
- const catalogType =
168
- config.fieldConfigs.integrationFields?.catalogTypes?.[
169
- config.model.config.catalog
170
- ];
170
+ const catalogType = config.model.config?.catalog
171
+ ? config.fieldConfigs.integrationFields?.catalogTypes?.[
172
+ config.model.config.catalog
173
+ ]
174
+ : undefined;
171
175
 
172
176
  config.interface.addProperty({
173
177
  name: config.id,
@@ -186,11 +190,13 @@ const addInterfacePropertyForField = (
186
190
  }
187
191
 
188
192
  case "Link": {
189
- switch (config.model.config.select) {
193
+ switch (config.model.config?.select) {
190
194
  case CustomTypeModelLinkSelectType.Document: {
195
+ config.model.config;
191
196
  config.interface.addProperty({
192
197
  name: config.id,
193
198
  type:
199
+ "customtypes" in config.model.config &&
194
200
  config.model.config.customtypes &&
195
201
  config.model.config.customtypes.length > 0
196
202
  ? `prismicT.RelationField<${config.model.config.customtypes
@@ -257,7 +263,9 @@ const addInterfacePropertyForField = (
257
263
 
258
264
  case "StructuredText": {
259
265
  const isTitleField =
266
+ config.model.config &&
260
267
  "single" in config.model.config &&
268
+ config.model.config.single &&
261
269
  config.model.config.single
262
270
  .split(",")
263
271
  .every((blockType) => /heading/.test(blockType));
@@ -290,15 +298,15 @@ const addInterfacePropertyForField = (
290
298
  }
291
299
 
292
300
  case "Select": {
293
- const options = config.model.config.options
294
- .map((option) => `"${option}"`)
301
+ const options = config.model.config?.options
302
+ ?.map((option) => `"${option}"`)
295
303
  .join(" | ");
296
- const hasDefault = Boolean(config.model.config.default_value);
304
+ const hasDefault = Boolean(config.model.config?.default_value);
297
305
 
298
306
  if (hasDefault) {
299
307
  config.interface.addProperty({
300
308
  name: config.id,
301
- type: `prismicT.SelectField<${options}, "filled">`,
309
+ type: `prismicT.SelectField<${options || "string"}, "filled">`,
302
310
  docs: buildFieldDocs({
303
311
  id: config.id,
304
312
  model: config.model,
@@ -376,19 +384,22 @@ const addInterfacePropertyForField = (
376
384
  ],
377
385
  isExported: true,
378
386
  });
379
- addInterfacePropertiesForFields({
380
- interface: itemInterface,
381
- sourceFile: config.sourceFile,
382
- fields: config.model.config.fields,
383
- path: [
384
- ...config.path,
385
- {
386
- id: config.id,
387
- model: config.model,
388
- },
389
- ],
390
- fieldConfigs: config.fieldConfigs,
391
- });
387
+
388
+ if (config.model.config?.fields) {
389
+ addInterfacePropertiesForFields({
390
+ interface: itemInterface,
391
+ sourceFile: config.sourceFile,
392
+ fields: config.model.config.fields,
393
+ path: [
394
+ ...config.path,
395
+ {
396
+ id: config.id,
397
+ model: config.model,
398
+ },
399
+ ],
400
+ fieldConfigs: config.fieldConfigs,
401
+ });
402
+ }
392
403
 
393
404
  config.interface.addProperty({
394
405
  name: config.id,
@@ -407,140 +418,147 @@ const addInterfacePropertyForField = (
407
418
  case "Slices": {
408
419
  const choiceInterfaceNames: string[] = [];
409
420
 
410
- for (const choiceId in config.model.config.choices) {
411
- const choice = config.model.config.choices[choiceId];
412
-
413
- if (choice.type === CustomTypeModelSliceType.SharedSlice) {
414
- choiceInterfaceNames.push(
415
- buildSharedSliceInterfaceName({ id: choiceId }),
416
- );
417
- } else if (choice.type === CustomTypeModelSliceType.Slice) {
418
- let primaryInterface: InterfaceDeclaration | undefined;
419
- if (Object.keys(choice["non-repeat"]).length > 0) {
420
- primaryInterface = config.sourceFile.addInterface({
421
- name: pascalCase(
422
- `${config.path[0].id} Document Data ${config.id} ${choiceId} Slice Primary`,
423
- ),
424
- docs: [
425
- {
426
- description: (writer) => {
427
- const humanReadablePath = getHumanReadableFieldPath({
428
- path: [
429
- ...config.path,
430
- {
431
- id: config.id,
432
- model: config.model,
433
- },
434
- {
435
- id: choiceId,
436
- model: choice,
437
- },
438
- {
439
- id: "primary",
440
- label: "Primary",
441
- },
442
- ],
443
- });
444
-
445
- writer.writeLine(`Primary content in ${humanReadablePath}`);
421
+ if (config.model.config?.choices) {
422
+ for (const choiceId in config.model.config.choices) {
423
+ const choice = config.model.config.choices[choiceId];
424
+
425
+ if (choice.type === CustomTypeModelSliceType.SharedSlice) {
426
+ choiceInterfaceNames.push(
427
+ buildSharedSliceInterfaceName({ id: choiceId }),
428
+ );
429
+ } else if (choice.type === CustomTypeModelSliceType.Slice) {
430
+ let primaryInterface: InterfaceDeclaration | undefined;
431
+ if (
432
+ choice["non-repeat"] &&
433
+ Object.keys(choice["non-repeat"]).length > 0
434
+ ) {
435
+ primaryInterface = config.sourceFile.addInterface({
436
+ name: pascalCase(
437
+ `${config.path[0].id} Document Data ${config.id} ${choiceId} Slice Primary`,
438
+ ),
439
+ docs: [
440
+ {
441
+ description: (writer) => {
442
+ const humanReadablePath = getHumanReadableFieldPath({
443
+ path: [
444
+ ...config.path,
445
+ {
446
+ id: config.id,
447
+ model: config.model,
448
+ },
449
+ {
450
+ id: choiceId,
451
+ model: choice,
452
+ },
453
+ {
454
+ id: "primary",
455
+ label: "Primary",
456
+ },
457
+ ],
458
+ });
459
+
460
+ writer.writeLine(
461
+ `Primary content in ${humanReadablePath}`,
462
+ );
463
+ },
446
464
  },
447
- },
448
- ],
449
- });
450
- addInterfacePropertiesForFields({
451
- interface: primaryInterface,
452
- sourceFile: config.sourceFile,
453
- fields: choice["non-repeat"],
454
- path: [
455
- ...config.path,
456
- {
457
- id: config.id,
458
- model: config.model,
459
- },
460
- {
461
- id: choiceId,
462
- model: choice,
463
- },
464
- {
465
- id: "primary",
466
- label: "Primary",
467
- },
468
- ],
469
- fieldConfigs: config.fieldConfigs,
470
- });
471
- }
465
+ ],
466
+ });
467
+ addInterfacePropertiesForFields({
468
+ interface: primaryInterface,
469
+ sourceFile: config.sourceFile,
470
+ fields: choice["non-repeat"],
471
+ path: [
472
+ ...config.path,
473
+ {
474
+ id: config.id,
475
+ model: config.model,
476
+ },
477
+ {
478
+ id: choiceId,
479
+ model: choice,
480
+ },
481
+ {
482
+ id: "primary",
483
+ label: "Primary",
484
+ },
485
+ ],
486
+ fieldConfigs: config.fieldConfigs,
487
+ });
488
+ }
489
+
490
+ let itemInterface: InterfaceDeclaration | undefined;
491
+ if (choice.repeat && Object.keys(choice.repeat).length > 0) {
492
+ itemInterface = config.sourceFile.addInterface({
493
+ name: pascalCase(
494
+ `${config.path[0].id} Document Data ${config.id} ${choiceId} Slice Item`,
495
+ ),
496
+ docs: [
497
+ {
498
+ description: (writer) => {
499
+ const humanReadablePath = getHumanReadableFieldPath({
500
+ path: [
501
+ ...config.path,
502
+ {
503
+ id: config.id,
504
+ model: config.model,
505
+ },
506
+ {
507
+ id: choiceId,
508
+ model: choice,
509
+ },
510
+ {
511
+ id: "items",
512
+ label: "Items",
513
+ },
514
+ ],
515
+ });
516
+
517
+ writer.writeLine(`Item in ${humanReadablePath}`);
518
+ },
519
+ },
520
+ ],
521
+ isExported: true,
522
+ });
523
+ addInterfacePropertiesForFields({
524
+ interface: itemInterface,
525
+ sourceFile: config.sourceFile,
526
+ fields: choice.repeat,
527
+ path: [
528
+ ...config.path,
529
+ {
530
+ id: config.id,
531
+ model: config.model,
532
+ },
533
+ {
534
+ id: choiceId,
535
+ model: choice,
536
+ },
537
+ {
538
+ id: "items",
539
+ label: "Items",
540
+ },
541
+ ],
542
+ fieldConfigs: config.fieldConfigs,
543
+ });
544
+ }
472
545
 
473
- let itemInterface: InterfaceDeclaration | undefined;
474
- if (Object.keys(choice.repeat).length > 0) {
475
- itemInterface = config.sourceFile.addInterface({
546
+ const sliceType = config.sourceFile.addTypeAlias({
476
547
  name: pascalCase(
477
- `${config.path[0].id} Document Data ${config.id} ${choiceId} Slice Item`,
548
+ `${config.path[0].id} Document Data ${config.id} ${choiceId} Slice`,
478
549
  ),
479
- docs: [
480
- {
481
- description: (writer) => {
482
- const humanReadablePath = getHumanReadableFieldPath({
483
- path: [
484
- ...config.path,
485
- {
486
- id: config.id,
487
- model: config.model,
488
- },
489
- {
490
- id: choiceId,
491
- model: choice,
492
- },
493
- {
494
- id: "items",
495
- label: "Items",
496
- },
497
- ],
498
- });
499
-
500
- writer.writeLine(`Item in ${humanReadablePath}`);
501
- },
502
- },
503
- ],
550
+ type: `prismicT.Slice<"${choiceId}", ${
551
+ primaryInterface
552
+ ? `Simplify<${primaryInterface.getName()}>`
553
+ : "Record<string, never>"
554
+ }, ${
555
+ itemInterface ? `Simplify<${itemInterface.getName()}>` : "never"
556
+ }>`,
504
557
  isExported: true,
505
558
  });
506
- addInterfacePropertiesForFields({
507
- interface: itemInterface,
508
- sourceFile: config.sourceFile,
509
- fields: choice.repeat,
510
- path: [
511
- ...config.path,
512
- {
513
- id: config.id,
514
- model: config.model,
515
- },
516
- {
517
- id: choiceId,
518
- model: choice,
519
- },
520
- {
521
- id: "items",
522
- label: "Items",
523
- },
524
- ],
525
- fieldConfigs: config.fieldConfigs,
526
- });
527
- }
528
559
 
529
- const sliceType = config.sourceFile.addTypeAlias({
530
- name: pascalCase(
531
- `${config.path[0].id} Document Data ${config.id} ${choiceId} Slice`,
532
- ),
533
- type: `prismicT.Slice<"${choiceId}", ${
534
- primaryInterface
535
- ? `Simplify<${primaryInterface.getName()}>`
536
- : "Record<string, never>"
537
- }, ${
538
- itemInterface ? `Simplify<${itemInterface.getName()}>` : "never"
539
- }>`,
540
- isExported: true,
541
- });
542
-
543
- choiceInterfaceNames.push(sliceType.getName());
560
+ choiceInterfaceNames.push(sliceType.getName());
561
+ }
544
562
  }
545
563
  }
546
564
 
@@ -614,7 +632,7 @@ export const addInterfacePropertiesForFields = (
614
632
  for (const name in config.fields) {
615
633
  addInterfacePropertyForField({
616
634
  ...config,
617
- id: name.includes("-") ? `"${name}"` : name,
635
+ id: name.includes("-") || /^[0-9]/.test(name) ? `"${name}"` : name,
618
636
  model: config.fields[name],
619
637
  tabName: config.tabName,
620
638
  });
@@ -25,7 +25,7 @@ export const addTypeAliasForSharedSlice = (
25
25
 
26
26
  for (const variation of config.model.variations) {
27
27
  let primaryInterface: InterfaceDeclaration | undefined;
28
- if (Object.keys(variation.primary).length > 0) {
28
+ if (variation.primary && Object.keys(variation.primary).length > 0) {
29
29
  primaryInterface = config.sourceFile.addInterface({
30
30
  name: pascalCase(
31
31
  `${buildSharedSliceInterfaceName({ id: config.model.id })} ${
@@ -72,7 +72,7 @@ export const addTypeAliasForSharedSlice = (
72
72
  }
73
73
 
74
74
  let itemInterface: InterfaceDeclaration | undefined;
75
- if (Object.keys(variation.items).length > 0) {
75
+ if (variation.items && Object.keys(variation.items).length > 0) {
76
76
  itemInterface = config.sourceFile.addInterface({
77
77
  name: pascalCase(
78
78
  `${buildSharedSliceInterfaceName({ id: config.model.id })} ${
@@ -43,13 +43,14 @@ export const buildFieldDocs = (
43
43
  });
44
44
 
45
45
  const placeholder =
46
+ config.model.config &&
46
47
  "placeholder" in config.model.config &&
47
48
  config.model.config.placeholder
48
49
  ? config.model.config.placeholder
49
50
  : `*None*`;
50
51
 
51
52
  const defaultValue =
52
- "default_value" in config.model.config
53
+ config.model.config && "default_value" in config.model.config
53
54
  ? config.model.config.default_value
54
55
  : undefined;
55
56
 
@@ -8,6 +8,8 @@ type GetFieldDocumentationURLConfig = {
8
8
 
9
9
  export const getFieldDocumentationURL = (
10
10
  config: GetFieldDocumentationURLConfig,
11
- ): string => {
12
- return FIELD_DOCUMENTATION_URLS[config.model.type];
11
+ ): string | undefined => {
12
+ return FIELD_DOCUMENTATION_URLS[
13
+ config.model.type as keyof typeof FIELD_DOCUMENTATION_URLS
14
+ ];
13
15
  };
@@ -11,7 +11,9 @@ export const getHumanReadableFieldType = (
11
11
  switch (config.model.type) {
12
12
  case "StructuredText": {
13
13
  const isTitleField =
14
+ config.model.config &&
14
15
  "single" in config.model.config &&
16
+ config.model.config.single &&
15
17
  config.model.config.single
16
18
  .split(",")
17
19
  .every((blockType) => /heading/.test(blockType));
@@ -20,11 +22,15 @@ export const getHumanReadableFieldType = (
20
22
  }
21
23
 
22
24
  case "IntegrationFields": {
23
- return `Integration Fields (Catalog: \`${config.model.config.catalog}\`)`;
25
+ const catalog = config.model.config?.catalog;
26
+
27
+ return `Integration Fields (Catalog: ${
28
+ catalog ? `\`${catalog}\`` : "*unknown*"
29
+ })`;
24
30
  }
25
31
 
26
32
  case "Link": {
27
- switch (config.model.config.select) {
33
+ switch (config.model.config?.select) {
28
34
  case CustomTypeModelLinkSelectType.Document: {
29
35
  return "Content Relationship";
30
36
  }
@@ -23,18 +23,20 @@ export const getHumanReadableModelName = (
23
23
  config: GetModelHumanNameConfig,
24
24
  ): string => {
25
25
  if (isCustomTypeModel(config.model)) {
26
- return config.model.label;
26
+ return config.model.label || config.model.id;
27
27
  } else if (isSharedSliceModel(config.model)) {
28
28
  return config.model.name;
29
- } else if (
30
- isCustomTypeModelField(config.model) &&
31
- "label" in config.model.config
32
- ) {
33
- return config.model.config.label;
29
+ } else if (isCustomTypeModelField(config.model)) {
30
+ if (config.model.config && "label" in config.model.config) {
31
+ // Non-Slice Zone fields
32
+ return config.model.config.label || config.id;
33
+ } else if (config.model.config && "fieldset" in config.model) {
34
+ // Slice Zone
35
+ return config.model.fieldset || config.id;
36
+ }
34
37
  } else if (isCustomTypeModelSlice(config.model)) {
35
- return config.model.fieldset;
36
- } else {
37
- // Slice Zone
38
- return `Slice Zone (\`${config.id}\`)`;
38
+ return config.model.fieldset || config.id;
39
39
  }
40
+
41
+ return `\`${config.id}\``;
40
42
  };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Determines if an object contains a given property. It augments the provided
3
+ * object's type to include the property.
4
+ *
5
+ * @remarks
6
+ * This function is only necessary because TypeScript does not narrow a type
7
+ * after using the `in` operator on `object`.
8
+ * @returns `true` if `obj` contains a `prop` property, `false` otherwise.
9
+ */
10
+ export const hasOwnProperty = <X extends object, Y extends PropertyKey>(
11
+ obj: X,
12
+ prop: Y,
13
+ ): obj is X & Record<Y, unknown> => {
14
+ return obj.hasOwnProperty(prop);
15
+ };
@@ -3,10 +3,5 @@ import type { CustomTypeModelField } from "@prismicio/types";
3
3
  export const isCustomTypeModelField = (
4
4
  model: unknown,
5
5
  ): model is CustomTypeModelField => {
6
- return (
7
- typeof model === "object" &&
8
- model !== null &&
9
- "type" in model &&
10
- "config" in model
11
- );
6
+ return typeof model === "object" && model !== null && "type" in model;
12
7
  };
@@ -1,4 +1,9 @@
1
- import type { CustomTypeModelSlice } from "@prismicio/types";
1
+ import {
2
+ CustomTypeModelSlice,
3
+ CustomTypeModelSliceType,
4
+ } from "@prismicio/types";
5
+
6
+ import { hasOwnProperty } from "./hasOwnProperty";
2
7
 
3
8
  export const isCustomTypeModelSlice = (
4
9
  model: unknown,
@@ -6,7 +11,7 @@ export const isCustomTypeModelSlice = (
6
11
  return (
7
12
  typeof model === "object" &&
8
13
  model !== null &&
9
- "non-repeat" in model &&
10
- "repeat" in model
14
+ hasOwnProperty(model, "type") &&
15
+ model.type === CustomTypeModelSliceType.Slice
11
16
  );
12
17
  };
@@ -1,7 +1,14 @@
1
- import type { SharedSliceModel } from "@prismicio/types";
1
+ import { CustomTypeModelSliceType, SharedSliceModel } from "@prismicio/types";
2
+
3
+ import { hasOwnProperty } from "./hasOwnProperty";
2
4
 
3
5
  export const isSharedSliceModel = (
4
6
  model: unknown,
5
7
  ): model is SharedSliceModel => {
6
- return typeof model === "object" && model !== null && "variations" in model;
8
+ return (
9
+ typeof model === "object" &&
10
+ model !== null &&
11
+ hasOwnProperty(model, "type") &&
12
+ model.type === CustomTypeModelSliceType.SharedSlice
13
+ );
7
14
  };