@angeloashmore/prismic-cli-poc 0.0.0-pr.8.b80fefa → 0.0.0-pr.9.5366ece

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.
Files changed (58) hide show
  1. package/dist/index.mjs +302 -168
  2. package/package.json +1 -1
  3. package/src/custom-type-add-field-boolean.ts +49 -12
  4. package/src/custom-type-add-field-color.ts +46 -12
  5. package/src/custom-type-add-field-date.ts +46 -12
  6. package/src/custom-type-add-field-embed.ts +46 -12
  7. package/src/custom-type-add-field-geo-point.ts +46 -12
  8. package/src/custom-type-add-field-group.ts +179 -0
  9. package/src/custom-type-add-field-image.ts +46 -12
  10. package/src/custom-type-add-field-key-text.ts +46 -12
  11. package/src/custom-type-add-field-link.ts +46 -12
  12. package/src/custom-type-add-field-number.ts +46 -12
  13. package/src/custom-type-add-field-rich-text.ts +46 -12
  14. package/src/custom-type-add-field-select.ts +47 -21
  15. package/src/custom-type-add-field-timestamp.ts +46 -12
  16. package/src/custom-type-add-field-uid.ts +17 -0
  17. package/src/custom-type-add-field.ts +5 -0
  18. package/src/index.ts +5 -0
  19. package/src/lib/field-path.ts +81 -0
  20. package/src/page-type-add-field-boolean.ts +66 -13
  21. package/src/page-type-add-field-color.ts +66 -13
  22. package/src/page-type-add-field-date.ts +66 -13
  23. package/src/page-type-add-field-embed.ts +66 -13
  24. package/src/page-type-add-field-geo-point.ts +66 -13
  25. package/src/page-type-add-field-group.ts +198 -0
  26. package/src/page-type-add-field-image.ts +66 -13
  27. package/src/page-type-add-field-key-text.ts +66 -13
  28. package/src/page-type-add-field-link.ts +66 -13
  29. package/src/page-type-add-field-number.ts +66 -13
  30. package/src/page-type-add-field-rich-text.ts +66 -13
  31. package/src/page-type-add-field-select.ts +67 -22
  32. package/src/page-type-add-field-timestamp.ts +66 -13
  33. package/src/page-type-add-field-uid.ts +37 -1
  34. package/src/page-type-add-field.ts +5 -0
  35. package/src/page-type-create.ts +25 -0
  36. package/src/repo-create.ts +59 -0
  37. package/src/skill-install.ts +177 -0
  38. package/src/skill-uninstall.ts +85 -0
  39. package/src/skill.ts +50 -0
  40. package/src/slice-add-field-boolean.ts +90 -16
  41. package/src/slice-add-field-color.ts +90 -16
  42. package/src/slice-add-field-date.ts +90 -16
  43. package/src/slice-add-field-embed.ts +90 -16
  44. package/src/slice-add-field-geo-point.ts +90 -16
  45. package/src/slice-add-field-group.ts +191 -0
  46. package/src/slice-add-field-image.ts +90 -16
  47. package/src/slice-add-field-key-text.ts +90 -16
  48. package/src/slice-add-field-link.ts +90 -16
  49. package/src/slice-add-field-number.ts +90 -16
  50. package/src/slice-add-field-rich-text.ts +90 -16
  51. package/src/slice-add-field-select.ts +91 -25
  52. package/src/slice-add-field-timestamp.ts +90 -16
  53. package/src/slice-add-field.ts +5 -0
  54. package/src/slice-create.ts +66 -5
  55. package/src/slice-set-screenshot.ts +235 -0
  56. package/src/slice-view.ts +3 -0
  57. package/src/slice.ts +5 -0
  58. package/src/status.ts +164 -124
@@ -4,6 +4,8 @@ import { writeFile } from "node:fs/promises";
4
4
  import { parseArgs } from "node:util";
5
5
 
6
6
  import { buildTypes } from "./codegen-types";
7
+ import { findGroupInVariation, isGroupField, parseFieldPath, validateNestedFieldPath } from "./lib/field-path";
8
+ import { type Framework, detectFrameworkInfo } from "./lib/framework";
7
9
  import { stringify } from "./lib/json";
8
10
  import { findSliceModel } from "./lib/slice";
9
11
  import { humanReadable } from "./lib/string";
@@ -31,6 +33,28 @@ EXAMPLES
31
33
  prismic slice add-field date promo end_date --variation "countdown"
32
34
  `.trim();
33
35
 
36
+ function getDocsPath(framework: Framework): string {
37
+ switch (framework) {
38
+ case "next":
39
+ return "nextjs/with-cli";
40
+ case "nuxt":
41
+ return "nuxt/with-cli";
42
+ case "sveltekit":
43
+ return "sveltekit/with-cli";
44
+ }
45
+ }
46
+
47
+ function getWriteComponentsAnchor(framework: Framework): string {
48
+ switch (framework) {
49
+ case "nuxt":
50
+ return "#write-vue-components";
51
+ case "sveltekit":
52
+ return "#write-svelte-components";
53
+ default:
54
+ return "#write-react-components";
55
+ }
56
+ }
57
+
34
58
  export async function sliceAddFieldDate(): Promise<void> {
35
59
  const {
36
60
  values: { help, variation, label, placeholder, types },
@@ -66,6 +90,15 @@ export async function sliceAddFieldDate(): Promise<void> {
66
90
  return;
67
91
  }
68
92
 
93
+ // Parse and validate field path
94
+ const fieldPath = parseFieldPath(fieldId);
95
+ const pathValidation = validateNestedFieldPath(fieldPath);
96
+ if (!pathValidation.ok) {
97
+ console.error(pathValidation.error);
98
+ process.exitCode = 1;
99
+ return;
100
+ }
101
+
69
102
  // Find the slice model
70
103
  const result = await findSliceModel(sliceId);
71
104
  if (!result.ok) {
@@ -101,26 +134,53 @@ export async function sliceAddFieldDate(): Promise<void> {
101
134
  targetVariation.primary = {};
102
135
  }
103
136
 
104
- // Check if field already exists in any variation
105
- for (const v of model.variations) {
106
- if (v.primary?.[fieldId]) {
107
- console.error(`Field "${fieldId}" already exists in variation "${v.id}"`);
108
- process.exitCode = 1;
109
- return;
110
- }
111
- }
112
-
113
137
  // Build field definition
114
138
  const fieldDefinition: DateField = {
115
139
  type: "Date",
116
140
  config: {
117
- label: label ?? humanReadable(fieldId),
141
+ label: label ?? humanReadable(fieldPath.type === "nested" ? fieldPath.nestedFieldId : fieldId),
118
142
  ...(placeholder && { placeholder }),
119
143
  },
120
144
  };
121
145
 
122
- // Add field to variation
123
- targetVariation.primary[fieldId] = fieldDefinition;
146
+ // Add field to variation (with nested field support)
147
+ if (fieldPath.type === "nested") {
148
+ const groupResult = findGroupInVariation(targetVariation.primary, fieldPath.groupId, targetVariation.id);
149
+ if (!groupResult.ok) {
150
+ console.error(groupResult.error);
151
+ process.exitCode = 1;
152
+ return;
153
+ }
154
+ // Check if nested field already exists
155
+ if (groupResult.group.config.fields[fieldPath.nestedFieldId]) {
156
+ console.error(
157
+ `Field "${fieldPath.nestedFieldId}" already exists in group "${fieldPath.groupId}"`,
158
+ );
159
+ process.exitCode = 1;
160
+ return;
161
+ }
162
+ groupResult.group.config.fields[fieldPath.nestedFieldId] = fieldDefinition;
163
+ } else {
164
+ // Check if field already exists in any variation (at top level or in groups)
165
+ for (const v of model.variations) {
166
+ if (v.primary?.[fieldId]) {
167
+ console.error(`Field "${fieldId}" already exists in variation "${v.id}"`);
168
+ process.exitCode = 1;
169
+ return;
170
+ }
171
+ // Also check inside groups
172
+ for (const [groupFieldId, groupField] of Object.entries(v.primary ?? {})) {
173
+ if (isGroupField(groupField) && groupField.config.fields[fieldId]) {
174
+ console.error(
175
+ `Field "${fieldId}" already exists in group "${groupFieldId}" in variation "${v.id}"`,
176
+ );
177
+ process.exitCode = 1;
178
+ return;
179
+ }
180
+ }
181
+ }
182
+ targetVariation.primary[fieldId] = fieldDefinition;
183
+ }
124
184
 
125
185
  // Write updated model
126
186
  try {
@@ -135,9 +195,15 @@ export async function sliceAddFieldDate(): Promise<void> {
135
195
  return;
136
196
  }
137
197
 
138
- console.info(
139
- `Added field "${fieldId}" (Date) to "${targetVariation.id}" variation in ${sliceId}`,
140
- );
198
+ if (fieldPath.type === "nested") {
199
+ console.info(
200
+ `Added field "${fieldPath.nestedFieldId}" (Date) to group "${fieldPath.groupId}" in ${sliceId}`,
201
+ );
202
+ } else {
203
+ console.info(
204
+ `Added field "${fieldId}" (Date) to "${targetVariation.id}" variation in ${sliceId}`,
205
+ );
206
+ }
141
207
 
142
208
  try {
143
209
  await buildTypes({ output: types });
@@ -148,5 +214,13 @@ export async function sliceAddFieldDate(): Promise<void> {
148
214
 
149
215
  console.info();
150
216
  console.info("Next: Add more fields with `prismic slice add-field`");
151
- console.info(" Run `prismic status` when done to find next steps");
217
+
218
+ const frameworkInfo = await detectFrameworkInfo();
219
+ if (frameworkInfo?.framework) {
220
+ const docsPath = getDocsPath(frameworkInfo.framework);
221
+ const anchor = getWriteComponentsAnchor(frameworkInfo.framework);
222
+ console.info(
223
+ ` Run \`prismic docs ${docsPath}${anchor}\` to learn how to implement the slice's component`,
224
+ );
225
+ }
152
226
  }
@@ -4,6 +4,8 @@ import { writeFile } from "node:fs/promises";
4
4
  import { parseArgs } from "node:util";
5
5
 
6
6
  import { buildTypes } from "./codegen-types";
7
+ import { findGroupInVariation, isGroupField, parseFieldPath, validateNestedFieldPath } from "./lib/field-path";
8
+ import { type Framework, detectFrameworkInfo } from "./lib/framework";
7
9
  import { stringify } from "./lib/json";
8
10
  import { findSliceModel } from "./lib/slice";
9
11
  import { humanReadable } from "./lib/string";
@@ -31,6 +33,28 @@ EXAMPLES
31
33
  prismic slice add-field embed social tweet --variation "twitter"
32
34
  `.trim();
33
35
 
36
+ function getDocsPath(framework: Framework): string {
37
+ switch (framework) {
38
+ case "next":
39
+ return "nextjs/with-cli";
40
+ case "nuxt":
41
+ return "nuxt/with-cli";
42
+ case "sveltekit":
43
+ return "sveltekit/with-cli";
44
+ }
45
+ }
46
+
47
+ function getWriteComponentsAnchor(framework: Framework): string {
48
+ switch (framework) {
49
+ case "nuxt":
50
+ return "#write-vue-components";
51
+ case "sveltekit":
52
+ return "#write-svelte-components";
53
+ default:
54
+ return "#write-react-components";
55
+ }
56
+ }
57
+
34
58
  export async function sliceAddFieldEmbed(): Promise<void> {
35
59
  const {
36
60
  values: { help, variation, label, placeholder, types },
@@ -66,6 +90,15 @@ export async function sliceAddFieldEmbed(): Promise<void> {
66
90
  return;
67
91
  }
68
92
 
93
+ // Parse and validate field path
94
+ const fieldPath = parseFieldPath(fieldId);
95
+ const pathValidation = validateNestedFieldPath(fieldPath);
96
+ if (!pathValidation.ok) {
97
+ console.error(pathValidation.error);
98
+ process.exitCode = 1;
99
+ return;
100
+ }
101
+
69
102
  // Find the slice model
70
103
  const result = await findSliceModel(sliceId);
71
104
  if (!result.ok) {
@@ -101,26 +134,53 @@ export async function sliceAddFieldEmbed(): Promise<void> {
101
134
  targetVariation.primary = {};
102
135
  }
103
136
 
104
- // Check if field already exists in any variation
105
- for (const v of model.variations) {
106
- if (v.primary?.[fieldId]) {
107
- console.error(`Field "${fieldId}" already exists in variation "${v.id}"`);
108
- process.exitCode = 1;
109
- return;
110
- }
111
- }
112
-
113
137
  // Build field definition
114
138
  const fieldDefinition: Embed = {
115
139
  type: "Embed",
116
140
  config: {
117
- label: label ?? humanReadable(fieldId),
141
+ label: label ?? humanReadable(fieldPath.type === "nested" ? fieldPath.nestedFieldId : fieldId),
118
142
  ...(placeholder && { placeholder }),
119
143
  },
120
144
  };
121
145
 
122
- // Add field to variation
123
- targetVariation.primary[fieldId] = fieldDefinition;
146
+ // Add field to variation (with nested field support)
147
+ if (fieldPath.type === "nested") {
148
+ const groupResult = findGroupInVariation(targetVariation.primary, fieldPath.groupId, targetVariation.id);
149
+ if (!groupResult.ok) {
150
+ console.error(groupResult.error);
151
+ process.exitCode = 1;
152
+ return;
153
+ }
154
+ // Check if nested field already exists
155
+ if (groupResult.group.config.fields[fieldPath.nestedFieldId]) {
156
+ console.error(
157
+ `Field "${fieldPath.nestedFieldId}" already exists in group "${fieldPath.groupId}"`,
158
+ );
159
+ process.exitCode = 1;
160
+ return;
161
+ }
162
+ groupResult.group.config.fields[fieldPath.nestedFieldId] = fieldDefinition;
163
+ } else {
164
+ // Check if field already exists in any variation (at top level or in groups)
165
+ for (const v of model.variations) {
166
+ if (v.primary?.[fieldId]) {
167
+ console.error(`Field "${fieldId}" already exists in variation "${v.id}"`);
168
+ process.exitCode = 1;
169
+ return;
170
+ }
171
+ // Also check inside groups
172
+ for (const [groupFieldId, groupField] of Object.entries(v.primary ?? {})) {
173
+ if (isGroupField(groupField) && groupField.config.fields[fieldId]) {
174
+ console.error(
175
+ `Field "${fieldId}" already exists in group "${groupFieldId}" in variation "${v.id}"`,
176
+ );
177
+ process.exitCode = 1;
178
+ return;
179
+ }
180
+ }
181
+ }
182
+ targetVariation.primary[fieldId] = fieldDefinition;
183
+ }
124
184
 
125
185
  // Write updated model
126
186
  try {
@@ -135,9 +195,15 @@ export async function sliceAddFieldEmbed(): Promise<void> {
135
195
  return;
136
196
  }
137
197
 
138
- console.info(
139
- `Added field "${fieldId}" (Embed) to "${targetVariation.id}" variation in ${sliceId}`,
140
- );
198
+ if (fieldPath.type === "nested") {
199
+ console.info(
200
+ `Added field "${fieldPath.nestedFieldId}" (Embed) to group "${fieldPath.groupId}" in ${sliceId}`,
201
+ );
202
+ } else {
203
+ console.info(
204
+ `Added field "${fieldId}" (Embed) to "${targetVariation.id}" variation in ${sliceId}`,
205
+ );
206
+ }
141
207
 
142
208
  try {
143
209
  await buildTypes({ output: types });
@@ -148,5 +214,13 @@ export async function sliceAddFieldEmbed(): Promise<void> {
148
214
 
149
215
  console.info();
150
216
  console.info("Next: Add more fields with `prismic slice add-field`");
151
- console.info(" Run `prismic status` when done to find next steps");
217
+
218
+ const frameworkInfo = await detectFrameworkInfo();
219
+ if (frameworkInfo?.framework) {
220
+ const docsPath = getDocsPath(frameworkInfo.framework);
221
+ const anchor = getWriteComponentsAnchor(frameworkInfo.framework);
222
+ console.info(
223
+ ` Run \`prismic docs ${docsPath}${anchor}\` to learn how to implement the slice's component`,
224
+ );
225
+ }
152
226
  }
@@ -4,6 +4,8 @@ import { writeFile } from "node:fs/promises";
4
4
  import { parseArgs } from "node:util";
5
5
 
6
6
  import { buildTypes } from "./codegen-types";
7
+ import { findGroupInVariation, isGroupField, parseFieldPath, validateNestedFieldPath } from "./lib/field-path";
8
+ import { type Framework, detectFrameworkInfo } from "./lib/framework";
7
9
  import { stringify } from "./lib/json";
8
10
  import { findSliceModel } from "./lib/slice";
9
11
  import { humanReadable } from "./lib/string";
@@ -30,6 +32,28 @@ EXAMPLES
30
32
  prismic slice add-field geo-point map marker --variation "interactive"
31
33
  `.trim();
32
34
 
35
+ function getDocsPath(framework: Framework): string {
36
+ switch (framework) {
37
+ case "next":
38
+ return "nextjs/with-cli";
39
+ case "nuxt":
40
+ return "nuxt/with-cli";
41
+ case "sveltekit":
42
+ return "sveltekit/with-cli";
43
+ }
44
+ }
45
+
46
+ function getWriteComponentsAnchor(framework: Framework): string {
47
+ switch (framework) {
48
+ case "nuxt":
49
+ return "#write-vue-components";
50
+ case "sveltekit":
51
+ return "#write-svelte-components";
52
+ default:
53
+ return "#write-react-components";
54
+ }
55
+ }
56
+
33
57
  export async function sliceAddFieldGeoPoint(): Promise<void> {
34
58
  const {
35
59
  values: { help, variation, label, types },
@@ -64,6 +88,15 @@ export async function sliceAddFieldGeoPoint(): Promise<void> {
64
88
  return;
65
89
  }
66
90
 
91
+ // Parse and validate field path
92
+ const fieldPath = parseFieldPath(fieldId);
93
+ const pathValidation = validateNestedFieldPath(fieldPath);
94
+ if (!pathValidation.ok) {
95
+ console.error(pathValidation.error);
96
+ process.exitCode = 1;
97
+ return;
98
+ }
99
+
67
100
  // Find the slice model
68
101
  const result = await findSliceModel(sliceId);
69
102
  if (!result.ok) {
@@ -99,25 +132,52 @@ export async function sliceAddFieldGeoPoint(): Promise<void> {
99
132
  targetVariation.primary = {};
100
133
  }
101
134
 
102
- // Check if field already exists in any variation
103
- for (const v of model.variations) {
104
- if (v.primary?.[fieldId]) {
105
- console.error(`Field "${fieldId}" already exists in variation "${v.id}"`);
106
- process.exitCode = 1;
107
- return;
108
- }
109
- }
110
-
111
135
  // Build field definition
112
136
  const fieldDefinition: GeoPoint = {
113
137
  type: "GeoPoint",
114
138
  config: {
115
- label: label ?? humanReadable(fieldId),
139
+ label: label ?? humanReadable(fieldPath.type === "nested" ? fieldPath.nestedFieldId : fieldId),
116
140
  },
117
141
  };
118
142
 
119
- // Add field to variation
120
- targetVariation.primary[fieldId] = fieldDefinition;
143
+ // Add field to variation (with nested field support)
144
+ if (fieldPath.type === "nested") {
145
+ const groupResult = findGroupInVariation(targetVariation.primary, fieldPath.groupId, targetVariation.id);
146
+ if (!groupResult.ok) {
147
+ console.error(groupResult.error);
148
+ process.exitCode = 1;
149
+ return;
150
+ }
151
+ // Check if nested field already exists
152
+ if (groupResult.group.config.fields[fieldPath.nestedFieldId]) {
153
+ console.error(
154
+ `Field "${fieldPath.nestedFieldId}" already exists in group "${fieldPath.groupId}"`,
155
+ );
156
+ process.exitCode = 1;
157
+ return;
158
+ }
159
+ groupResult.group.config.fields[fieldPath.nestedFieldId] = fieldDefinition;
160
+ } else {
161
+ // Check if field already exists in any variation (at top level or in groups)
162
+ for (const v of model.variations) {
163
+ if (v.primary?.[fieldId]) {
164
+ console.error(`Field "${fieldId}" already exists in variation "${v.id}"`);
165
+ process.exitCode = 1;
166
+ return;
167
+ }
168
+ // Also check inside groups
169
+ for (const [groupFieldId, groupField] of Object.entries(v.primary ?? {})) {
170
+ if (isGroupField(groupField) && groupField.config.fields[fieldId]) {
171
+ console.error(
172
+ `Field "${fieldId}" already exists in group "${groupFieldId}" in variation "${v.id}"`,
173
+ );
174
+ process.exitCode = 1;
175
+ return;
176
+ }
177
+ }
178
+ }
179
+ targetVariation.primary[fieldId] = fieldDefinition;
180
+ }
121
181
 
122
182
  // Write updated model
123
183
  try {
@@ -132,9 +192,15 @@ export async function sliceAddFieldGeoPoint(): Promise<void> {
132
192
  return;
133
193
  }
134
194
 
135
- console.info(
136
- `Added field "${fieldId}" (GeoPoint) to "${targetVariation.id}" variation in ${sliceId}`,
137
- );
195
+ if (fieldPath.type === "nested") {
196
+ console.info(
197
+ `Added field "${fieldPath.nestedFieldId}" (GeoPoint) to group "${fieldPath.groupId}" in ${sliceId}`,
198
+ );
199
+ } else {
200
+ console.info(
201
+ `Added field "${fieldId}" (GeoPoint) to "${targetVariation.id}" variation in ${sliceId}`,
202
+ );
203
+ }
138
204
 
139
205
  try {
140
206
  await buildTypes({ output: types });
@@ -145,5 +211,13 @@ export async function sliceAddFieldGeoPoint(): Promise<void> {
145
211
 
146
212
  console.info();
147
213
  console.info("Next: Add more fields with `prismic slice add-field`");
148
- console.info(" Run `prismic status` when done to find next steps");
214
+
215
+ const frameworkInfo = await detectFrameworkInfo();
216
+ if (frameworkInfo?.framework) {
217
+ const docsPath = getDocsPath(frameworkInfo.framework);
218
+ const anchor = getWriteComponentsAnchor(frameworkInfo.framework);
219
+ console.info(
220
+ ` Run \`prismic docs ${docsPath}${anchor}\` to learn how to implement the slice's component`,
221
+ );
222
+ }
149
223
  }
@@ -0,0 +1,191 @@
1
+ import type { Group, SharedSlice } from "@prismicio/types-internal/lib/customtypes";
2
+
3
+ import { writeFile } from "node:fs/promises";
4
+ import { parseArgs } from "node:util";
5
+
6
+ import { buildTypes } from "./codegen-types";
7
+ import { type Framework, detectFrameworkInfo } from "./lib/framework";
8
+ import { stringify } from "./lib/json";
9
+ import { findSliceModel } from "./lib/slice";
10
+ import { humanReadable } from "./lib/string";
11
+
12
+ const HELP = `
13
+ Add a group field to an existing slice.
14
+
15
+ USAGE
16
+ prismic slice add-field group <slice-id> <field-id> [flags]
17
+
18
+ ARGUMENTS
19
+ slice-id Slice identifier (required)
20
+ field-id Field identifier (required)
21
+
22
+ FLAGS
23
+ -v, --variation string Target variation (default: first variation)
24
+ -l, --label string Display label for the field (inferred from field-id if omitted)
25
+ --non-repeatable Make this a non-repeating group (default: repeatable)
26
+ --types string Output file for generated types (default: "prismicio-types.d.ts")
27
+ -h, --help Show help for command
28
+
29
+ EXAMPLES
30
+ prismic slice add-field group my_slice buttons
31
+ prismic slice add-field group hero ctas --non-repeatable
32
+ prismic slice add-field group product variants --variation "withImage"
33
+ `.trim();
34
+
35
+ function getDocsPath(framework: Framework): string {
36
+ switch (framework) {
37
+ case "next":
38
+ return "nextjs/with-cli";
39
+ case "nuxt":
40
+ return "nuxt/with-cli";
41
+ case "sveltekit":
42
+ return "sveltekit/with-cli";
43
+ }
44
+ }
45
+
46
+ function getWriteComponentsAnchor(framework: Framework): string {
47
+ switch (framework) {
48
+ case "nuxt":
49
+ return "#write-vue-components";
50
+ case "sveltekit":
51
+ return "#write-svelte-components";
52
+ default:
53
+ return "#write-react-components";
54
+ }
55
+ }
56
+
57
+ export async function sliceAddFieldGroup(): Promise<void> {
58
+ const {
59
+ values: { help, variation, label, "non-repeatable": nonRepeatable, types },
60
+ positionals: [sliceId, fieldId],
61
+ } = parseArgs({
62
+ args: process.argv.slice(5), // skip: node, script, "slice", "add-field", "group"
63
+ options: {
64
+ variation: { type: "string", short: "v" },
65
+ label: { type: "string", short: "l" },
66
+ "non-repeatable": { type: "boolean" },
67
+ types: { type: "string" },
68
+ help: { type: "boolean", short: "h" },
69
+ },
70
+ allowPositionals: true,
71
+ });
72
+
73
+ if (help) {
74
+ console.info(HELP);
75
+ return;
76
+ }
77
+
78
+ if (!sliceId) {
79
+ console.error("Missing required argument: slice-id\n");
80
+ console.error("Usage: prismic slice add-field group <slice-id> <field-id>");
81
+ process.exitCode = 1;
82
+ return;
83
+ }
84
+
85
+ if (!fieldId) {
86
+ console.error("Missing required argument: field-id\n");
87
+ console.error("Usage: prismic slice add-field group <slice-id> <field-id>");
88
+ process.exitCode = 1;
89
+ return;
90
+ }
91
+
92
+ // Groups cannot be nested
93
+ if (fieldId.includes(".")) {
94
+ console.error("Groups cannot be nested inside other groups");
95
+ process.exitCode = 1;
96
+ return;
97
+ }
98
+
99
+ // Find the slice model
100
+ const result = await findSliceModel(sliceId);
101
+ if (!result.ok) {
102
+ console.error(result.error);
103
+ process.exitCode = 1;
104
+ return;
105
+ }
106
+
107
+ const { model, modelPath } = result;
108
+
109
+ // Check for variations
110
+ if (model.variations.length === 0) {
111
+ console.error(`Slice "${sliceId}" has no variations.\n`);
112
+ console.error("Add a variation first before adding fields.");
113
+ process.exitCode = 1;
114
+ return;
115
+ }
116
+
117
+ // Find target variation
118
+ const targetVariation = variation
119
+ ? model.variations.find((v) => v.id === variation)
120
+ : model.variations[0];
121
+
122
+ if (!targetVariation) {
123
+ console.error(`Variation "${variation}" not found in slice "${sliceId}"\n`);
124
+ console.error(`Available variations: ${model.variations.map((v) => v.id).join(", ")}`);
125
+ process.exitCode = 1;
126
+ return;
127
+ }
128
+
129
+ // Initialize primary if it doesn't exist
130
+ if (!targetVariation.primary) {
131
+ targetVariation.primary = {};
132
+ }
133
+
134
+ // Check if field already exists in any variation
135
+ for (const v of model.variations) {
136
+ if (v.primary?.[fieldId]) {
137
+ console.error(`Field "${fieldId}" already exists in variation "${v.id}"`);
138
+ process.exitCode = 1;
139
+ return;
140
+ }
141
+ }
142
+
143
+ // Build field definition
144
+ const fieldDefinition: Group = {
145
+ type: "Group",
146
+ config: {
147
+ label: label ?? humanReadable(fieldId),
148
+ repeat: !nonRepeatable,
149
+ fields: {},
150
+ },
151
+ };
152
+
153
+ // Add field to variation
154
+ targetVariation.primary[fieldId] = fieldDefinition;
155
+
156
+ // Write updated model
157
+ try {
158
+ await writeFile(modelPath, stringify(model as SharedSlice));
159
+ } catch (error) {
160
+ if (error instanceof Error) {
161
+ console.error(`Failed to update slice: ${error.message}`);
162
+ } else {
163
+ console.error("Failed to update slice");
164
+ }
165
+ process.exitCode = 1;
166
+ return;
167
+ }
168
+
169
+ console.info(
170
+ `Added field "${fieldId}" (Group) to "${targetVariation.id}" variation in ${sliceId}`,
171
+ );
172
+
173
+ try {
174
+ await buildTypes({ output: types });
175
+ console.info(`Updated types in ${types ?? "prismicio-types.d.ts"}`);
176
+ } catch (error) {
177
+ console.warn(`Could not generate types: ${error instanceof Error ? error.message : error}`);
178
+ }
179
+
180
+ console.info();
181
+ console.info(`Next: Add fields to the group with \`prismic slice add-field <type> ${sliceId} ${fieldId}.<field-id>\``);
182
+
183
+ const frameworkInfo = await detectFrameworkInfo();
184
+ if (frameworkInfo?.framework) {
185
+ const docsPath = getDocsPath(frameworkInfo.framework);
186
+ const anchor = getWriteComponentsAnchor(frameworkInfo.framework);
187
+ console.info(
188
+ ` Run \`prismic docs ${docsPath}${anchor}\` to learn how to implement the slice's component`,
189
+ );
190
+ }
191
+ }