@angeloashmore/prismic-cli-poc 0.0.0-canary.fe51fbb → 0.0.0-pr.10.032f7ef

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 (54) hide show
  1. package/dist/index.mjs +229 -136
  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 +46 -12
  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/docs-fetch.ts +146 -0
  19. package/src/docs-list.ts +131 -0
  20. package/src/docs.ts +26 -121
  21. package/src/index.ts +1 -1
  22. package/src/lib/field-path.ts +81 -0
  23. package/src/page-type-add-field-boolean.ts +47 -13
  24. package/src/page-type-add-field-color.ts +47 -13
  25. package/src/page-type-add-field-date.ts +47 -13
  26. package/src/page-type-add-field-embed.ts +47 -13
  27. package/src/page-type-add-field-geo-point.ts +47 -13
  28. package/src/page-type-add-field-group.ts +198 -0
  29. package/src/page-type-add-field-image.ts +47 -13
  30. package/src/page-type-add-field-key-text.ts +47 -13
  31. package/src/page-type-add-field-link.ts +47 -13
  32. package/src/page-type-add-field-number.ts +47 -13
  33. package/src/page-type-add-field-rich-text.ts +47 -13
  34. package/src/page-type-add-field-select.ts +47 -13
  35. package/src/page-type-add-field-timestamp.ts +47 -13
  36. package/src/page-type-add-field-uid.ts +18 -1
  37. package/src/page-type-add-field.ts +5 -0
  38. package/src/page-type-create.ts +1 -1
  39. package/src/repo-create.ts +28 -1
  40. package/src/slice-add-field-boolean.ts +59 -16
  41. package/src/slice-add-field-color.ts +59 -16
  42. package/src/slice-add-field-date.ts +59 -16
  43. package/src/slice-add-field-embed.ts +59 -16
  44. package/src/slice-add-field-geo-point.ts +59 -16
  45. package/src/slice-add-field-group.ts +191 -0
  46. package/src/slice-add-field-image.ts +59 -16
  47. package/src/slice-add-field-key-text.ts +59 -16
  48. package/src/slice-add-field-link.ts +59 -16
  49. package/src/slice-add-field-number.ts +59 -16
  50. package/src/slice-add-field-rich-text.ts +59 -16
  51. package/src/slice-add-field-select.ts +59 -16
  52. package/src/slice-add-field-timestamp.ts +59 -16
  53. package/src/slice-add-field.ts +5 -0
  54. package/src/status.ts +1 -1
@@ -6,6 +6,7 @@ import * as v from "valibot";
6
6
 
7
7
  import { buildTypes } from "./codegen-types";
8
8
  import { findUpward } from "./lib/file";
9
+ import { findGroupInTab, isGroupField, parseFieldPath, validateNestedFieldPath } from "./lib/field-path";
9
10
  import { type Framework, detectFrameworkInfo } from "./lib/framework";
10
11
  import { stringify } from "./lib/json";
11
12
  import { humanReadable } from "./lib/string";
@@ -90,6 +91,15 @@ export async function pageTypeAddFieldTimestamp(): Promise<void> {
90
91
  return;
91
92
  }
92
93
 
94
+ // Parse and validate field path
95
+ const fieldPath = parseFieldPath(fieldId);
96
+ const pathValidation = validateNestedFieldPath(fieldPath);
97
+ if (!pathValidation.ok) {
98
+ console.error(pathValidation.error);
99
+ process.exitCode = 1;
100
+ return;
101
+ }
102
+
93
103
  // Find the page type file
94
104
  const projectRoot = await findUpward("package.json");
95
105
  if (!projectRoot) {
@@ -136,27 +146,47 @@ export async function pageTypeAddFieldTimestamp(): Promise<void> {
136
146
  model.json[targetTab] = {};
137
147
  }
138
148
 
139
- // Check if field already exists in any tab
140
- for (const [tabName, tabFields] of Object.entries(model.json)) {
141
- if (tabFields[fieldId]) {
142
- console.error(`Field "${fieldId}" already exists in tab "${tabName}"`);
143
- process.exitCode = 1;
144
- return;
145
- }
146
- }
147
-
148
149
  // Build field definition
149
150
  const fieldDefinition: Timestamp = {
150
151
  type: "Timestamp",
151
152
  config: {
152
- label: label ?? humanReadable(fieldId),
153
+ label: label ?? humanReadable(fieldPath.type === "nested" ? fieldPath.nestedFieldId : fieldId),
153
154
  ...(placeholder && { placeholder }),
154
155
  ...(defaultValue && { default: defaultValue }),
155
156
  },
156
157
  };
157
158
 
158
159
  // Add field to model
159
- model.json[targetTab][fieldId] = fieldDefinition;
160
+ if (fieldPath.type === "nested") {
161
+ const groupResult = findGroupInTab(model.json[targetTab], fieldPath.groupId, targetTab);
162
+ if (!groupResult.ok) {
163
+ console.error(groupResult.error);
164
+ process.exitCode = 1;
165
+ return;
166
+ }
167
+ if (groupResult.group.config.fields[fieldPath.nestedFieldId]) {
168
+ console.error(`Field "${fieldPath.nestedFieldId}" already exists in group "${fieldPath.groupId}"`);
169
+ process.exitCode = 1;
170
+ return;
171
+ }
172
+ groupResult.group.config.fields[fieldPath.nestedFieldId] = fieldDefinition;
173
+ } else {
174
+ for (const [tabName, tabFields] of Object.entries(model.json)) {
175
+ if (tabFields[fieldId]) {
176
+ console.error(`Field "${fieldId}" already exists in tab "${tabName}"`);
177
+ process.exitCode = 1;
178
+ return;
179
+ }
180
+ for (const [groupFieldId, groupField] of Object.entries(tabFields)) {
181
+ if (isGroupField(groupField) && groupField.config.fields[fieldId]) {
182
+ console.error(`Field "${fieldId}" already exists in group "${groupFieldId}" in tab "${tabName}"`);
183
+ process.exitCode = 1;
184
+ return;
185
+ }
186
+ }
187
+ }
188
+ model.json[targetTab][fieldId] = fieldDefinition;
189
+ }
160
190
 
161
191
  // Write updated model
162
192
  try {
@@ -171,7 +201,11 @@ export async function pageTypeAddFieldTimestamp(): Promise<void> {
171
201
  return;
172
202
  }
173
203
 
174
- console.info(`Added field "${fieldId}" (Timestamp) to "${targetTab}" tab in ${typeId}`);
204
+ if (fieldPath.type === "nested") {
205
+ console.info(`Added field "${fieldPath.nestedFieldId}" (Timestamp) to group "${fieldPath.groupId}" in ${typeId}`);
206
+ } else {
207
+ console.info(`Added field "${fieldId}" (Timestamp) to "${targetTab}" tab in ${typeId}`);
208
+ }
175
209
 
176
210
  try {
177
211
  await buildTypes({ output: types });
@@ -187,7 +221,7 @@ export async function pageTypeAddFieldTimestamp(): Promise<void> {
187
221
  if (frameworkInfo?.framework) {
188
222
  const docsPath = getDocsPath(frameworkInfo.framework);
189
223
  console.info(
190
- ` Run \`prismic docs ${docsPath}#write-page-components\` to learn how to implement a page file`,
224
+ ` Run \`prismic docs fetch ${docsPath}#write-page-components\` to learn how to implement a page file`,
191
225
  );
192
226
  }
193
227
  }
@@ -6,6 +6,7 @@ import * as v from "valibot";
6
6
 
7
7
  import { buildTypes } from "./codegen-types";
8
8
  import { findUpward } from "./lib/file";
9
+ import { parseFieldPath, validateNestedFieldPath } from "./lib/field-path";
9
10
  import { type Framework, detectFrameworkInfo } from "./lib/framework";
10
11
  import { stringify } from "./lib/json";
11
12
  import { humanReadable } from "./lib/string";
@@ -88,6 +89,22 @@ export async function pageTypeAddFieldUid(): Promise<void> {
88
89
  return;
89
90
  }
90
91
 
92
+ // Parse and validate field path
93
+ const fieldPath = parseFieldPath(fieldId);
94
+ const pathValidation = validateNestedFieldPath(fieldPath);
95
+ if (!pathValidation.ok) {
96
+ console.error(pathValidation.error);
97
+ process.exitCode = 1;
98
+ return;
99
+ }
100
+
101
+ // UID fields cannot be nested in groups
102
+ if (fieldPath.type === "nested") {
103
+ console.error("UID fields cannot be nested inside groups");
104
+ process.exitCode = 1;
105
+ return;
106
+ }
107
+
91
108
  // Find the page type file
92
109
  const projectRoot = await findUpward("package.json");
93
110
  if (!projectRoot) {
@@ -184,7 +201,7 @@ export async function pageTypeAddFieldUid(): Promise<void> {
184
201
  if (frameworkInfo?.framework) {
185
202
  const docsPath = getDocsPath(frameworkInfo.framework);
186
203
  console.info(
187
- ` Run \`prismic docs ${docsPath}#write-page-components\` to learn how to implement a page file`,
204
+ ` Run \`prismic docs fetch ${docsPath}#write-page-components\` to learn how to implement a page file`,
188
205
  );
189
206
  }
190
207
  }
@@ -5,6 +5,7 @@ import { pageTypeAddFieldColor } from "./page-type-add-field-color";
5
5
  import { pageTypeAddFieldDate } from "./page-type-add-field-date";
6
6
  import { pageTypeAddFieldEmbed } from "./page-type-add-field-embed";
7
7
  import { pageTypeAddFieldGeoPoint } from "./page-type-add-field-geo-point";
8
+ import { pageTypeAddFieldGroup } from "./page-type-add-field-group";
8
9
  import { pageTypeAddFieldImage } from "./page-type-add-field-image";
9
10
  import { pageTypeAddFieldKeyText } from "./page-type-add-field-key-text";
10
11
  import { pageTypeAddFieldLink } from "./page-type-add-field-link";
@@ -26,6 +27,7 @@ FIELD TYPES
26
27
  date Date picker
27
28
  embed Embed (oEmbed)
28
29
  geo-point Geographic coordinates
30
+ group Repeatable group of fields
29
31
  image Image
30
32
  key-text Single-line text
31
33
  link Any link type
@@ -76,6 +78,9 @@ export async function pageTypeAddField(): Promise<void> {
76
78
  case "geo-point":
77
79
  await pageTypeAddFieldGeoPoint();
78
80
  break;
81
+ case "group":
82
+ await pageTypeAddFieldGroup();
83
+ break;
79
84
  case "image":
80
85
  await pageTypeAddFieldImage();
81
86
  break;
@@ -151,7 +151,7 @@ export async function pageTypeCreate(): Promise<void> {
151
151
  const docsPath = getDocsPath(frameworkInfo.framework);
152
152
  const anchor = getWritePageComponentsAnchor(frameworkInfo.framework);
153
153
  console.info(
154
- ` Run \`prismic docs ${docsPath}${anchor}\` to learn how to implement a page file`,
154
+ ` Run \`prismic docs fetch ${docsPath}${anchor}\` to learn how to implement a page file`,
155
155
  );
156
156
  }
157
157
  }
@@ -102,6 +102,23 @@ export async function repoCreate(): Promise<void> {
102
102
  return;
103
103
  }
104
104
 
105
+ // Check if domain is available
106
+ const available = await checkDomainAvailable(domain);
107
+ if (!available.ok) {
108
+ if (available.error instanceof ForbiddenRequestError) {
109
+ handleUnauthenticated();
110
+ } else {
111
+ console.error(`Failed to check domain availability: ${stringify(available.error)}`);
112
+ process.exitCode = 1;
113
+ }
114
+ return;
115
+ }
116
+ if (!available.value) {
117
+ console.error(`Repository name "${domain}" is already taken.`);
118
+ process.exitCode = 1;
119
+ return;
120
+ }
121
+
105
122
  const response = await createRepository(domain, name);
106
123
  if (!response.ok) {
107
124
  if (response.error instanceof ForbiddenRequestError) {
@@ -143,8 +160,18 @@ export async function repoCreate(): Promise<void> {
143
160
  const clientFile = getClientFilePath(frameworkInfo);
144
161
  const fileDesc = clientFile ? `creating ${clientFile}` : "configuring Prismic";
145
162
  console.info();
146
- console.info(`Next: Run \`prismic docs ${docsPath}${anchor}\` for instructions on ${fileDesc}`);
163
+ console.info(`Next: Run \`prismic docs fetch ${docsPath}${anchor}\` for instructions on ${fileDesc}`);
164
+ }
165
+ }
166
+
167
+ async function checkDomainAvailable(domain: string) {
168
+ const url = new URL(`/app/dashboard/repositories/${domain}/exists`, await readHost());
169
+ const response = await request<string>(url);
170
+ if (!response.ok) {
171
+ return response;
147
172
  }
173
+ // Endpoint returns "false" when repository exists, "true" when available
174
+ return { ok: true as const, value: response.value === "true" };
148
175
  }
149
176
 
150
177
  async function createRepository(domain: string, name = domain) {
@@ -4,6 +4,7 @@ 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";
7
8
  import { type Framework, detectFrameworkInfo } from "./lib/framework";
8
9
  import { stringify } from "./lib/json";
9
10
  import { findSliceModel } from "./lib/slice";
@@ -101,6 +102,15 @@ export async function sliceAddFieldBoolean(): Promise<void> {
101
102
  return;
102
103
  }
103
104
 
105
+ // Parse and validate field path
106
+ const fieldPath = parseFieldPath(fieldId);
107
+ const pathValidation = validateNestedFieldPath(fieldPath);
108
+ if (!pathValidation.ok) {
109
+ console.error(pathValidation.error);
110
+ process.exitCode = 1;
111
+ return;
112
+ }
113
+
104
114
  // Find the slice model
105
115
  const result = await findSliceModel(sliceId);
106
116
  if (!result.ok) {
@@ -136,28 +146,55 @@ export async function sliceAddFieldBoolean(): Promise<void> {
136
146
  targetVariation.primary = {};
137
147
  }
138
148
 
139
- // Check if field already exists in any variation
140
- for (const v of model.variations) {
141
- if (v.primary?.[fieldId]) {
142
- console.error(`Field "${fieldId}" already exists in variation "${v.id}"`);
143
- process.exitCode = 1;
144
- return;
145
- }
146
- }
147
-
148
149
  // Build field definition
149
150
  const fieldDefinition: BooleanField = {
150
151
  type: "Boolean",
151
152
  config: {
152
- label: label ?? humanReadable(fieldId),
153
+ label: label ?? humanReadable(fieldPath.type === "nested" ? fieldPath.nestedFieldId : fieldId),
153
154
  ...(defaultValue && { default_value: true }),
154
155
  ...(trueLabel && { placeholder_true: trueLabel }),
155
156
  ...(falseLabel && { placeholder_false: falseLabel }),
156
157
  },
157
158
  };
158
159
 
159
- // Add field to variation
160
- targetVariation.primary[fieldId] = fieldDefinition;
160
+ // Add field to variation (with nested field support)
161
+ if (fieldPath.type === "nested") {
162
+ const groupResult = findGroupInVariation(targetVariation.primary, fieldPath.groupId, targetVariation.id);
163
+ if (!groupResult.ok) {
164
+ console.error(groupResult.error);
165
+ process.exitCode = 1;
166
+ return;
167
+ }
168
+ // Check if nested field already exists
169
+ if (groupResult.group.config.fields[fieldPath.nestedFieldId]) {
170
+ console.error(
171
+ `Field "${fieldPath.nestedFieldId}" already exists in group "${fieldPath.groupId}"`,
172
+ );
173
+ process.exitCode = 1;
174
+ return;
175
+ }
176
+ groupResult.group.config.fields[fieldPath.nestedFieldId] = fieldDefinition;
177
+ } else {
178
+ // Check if field already exists in any variation (at top level or in groups)
179
+ for (const v of model.variations) {
180
+ if (v.primary?.[fieldId]) {
181
+ console.error(`Field "${fieldId}" already exists in variation "${v.id}"`);
182
+ process.exitCode = 1;
183
+ return;
184
+ }
185
+ // Also check inside groups
186
+ for (const [groupFieldId, groupField] of Object.entries(v.primary ?? {})) {
187
+ if (isGroupField(groupField) && groupField.config.fields[fieldId]) {
188
+ console.error(
189
+ `Field "${fieldId}" already exists in group "${groupFieldId}" in variation "${v.id}"`,
190
+ );
191
+ process.exitCode = 1;
192
+ return;
193
+ }
194
+ }
195
+ }
196
+ targetVariation.primary[fieldId] = fieldDefinition;
197
+ }
161
198
 
162
199
  // Write updated model
163
200
  try {
@@ -172,9 +209,15 @@ export async function sliceAddFieldBoolean(): Promise<void> {
172
209
  return;
173
210
  }
174
211
 
175
- console.info(
176
- `Added field "${fieldId}" (Boolean) to "${targetVariation.id}" variation in ${sliceId}`,
177
- );
212
+ if (fieldPath.type === "nested") {
213
+ console.info(
214
+ `Added field "${fieldPath.nestedFieldId}" (Boolean) to group "${fieldPath.groupId}" in ${sliceId}`,
215
+ );
216
+ } else {
217
+ console.info(
218
+ `Added field "${fieldId}" (Boolean) to "${targetVariation.id}" variation in ${sliceId}`,
219
+ );
220
+ }
178
221
 
179
222
  try {
180
223
  await buildTypes({ output: types });
@@ -191,7 +234,7 @@ export async function sliceAddFieldBoolean(): Promise<void> {
191
234
  const docsPath = getDocsPath(frameworkInfo.framework);
192
235
  const anchor = getWriteComponentsAnchor(frameworkInfo.framework);
193
236
  console.info(
194
- ` Run \`prismic docs ${docsPath}${anchor}\` to learn how to implement the slice's component`,
237
+ ` Run \`prismic docs fetch ${docsPath}${anchor}\` to learn how to implement the slice's component`,
195
238
  );
196
239
  }
197
240
  }
@@ -4,6 +4,7 @@ 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";
7
8
  import { type Framework, detectFrameworkInfo } from "./lib/framework";
8
9
  import { stringify } from "./lib/json";
9
10
  import { findSliceModel } from "./lib/slice";
@@ -89,6 +90,15 @@ export async function sliceAddFieldColor(): Promise<void> {
89
90
  return;
90
91
  }
91
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
+
92
102
  // Find the slice model
93
103
  const result = await findSliceModel(sliceId);
94
104
  if (!result.ok) {
@@ -124,26 +134,53 @@ export async function sliceAddFieldColor(): Promise<void> {
124
134
  targetVariation.primary = {};
125
135
  }
126
136
 
127
- // Check if field already exists in any variation
128
- for (const v of model.variations) {
129
- if (v.primary?.[fieldId]) {
130
- console.error(`Field "${fieldId}" already exists in variation "${v.id}"`);
131
- process.exitCode = 1;
132
- return;
133
- }
134
- }
135
-
136
137
  // Build field definition
137
138
  const fieldDefinition: Color = {
138
139
  type: "Color",
139
140
  config: {
140
- label: label ?? humanReadable(fieldId),
141
+ label: label ?? humanReadable(fieldPath.type === "nested" ? fieldPath.nestedFieldId : fieldId),
141
142
  ...(placeholder && { placeholder }),
142
143
  },
143
144
  };
144
145
 
145
- // Add field to variation
146
- 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
+ }
147
184
 
148
185
  // Write updated model
149
186
  try {
@@ -158,9 +195,15 @@ export async function sliceAddFieldColor(): Promise<void> {
158
195
  return;
159
196
  }
160
197
 
161
- console.info(
162
- `Added field "${fieldId}" (Color) to "${targetVariation.id}" variation in ${sliceId}`,
163
- );
198
+ if (fieldPath.type === "nested") {
199
+ console.info(
200
+ `Added field "${fieldPath.nestedFieldId}" (Color) to group "${fieldPath.groupId}" in ${sliceId}`,
201
+ );
202
+ } else {
203
+ console.info(
204
+ `Added field "${fieldId}" (Color) to "${targetVariation.id}" variation in ${sliceId}`,
205
+ );
206
+ }
164
207
 
165
208
  try {
166
209
  await buildTypes({ output: types });
@@ -177,7 +220,7 @@ export async function sliceAddFieldColor(): Promise<void> {
177
220
  const docsPath = getDocsPath(frameworkInfo.framework);
178
221
  const anchor = getWriteComponentsAnchor(frameworkInfo.framework);
179
222
  console.info(
180
- ` Run \`prismic docs ${docsPath}${anchor}\` to learn how to implement the slice's component`,
223
+ ` Run \`prismic docs fetch ${docsPath}${anchor}\` to learn how to implement the slice's component`,
181
224
  );
182
225
  }
183
226
  }
@@ -4,6 +4,7 @@ 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";
7
8
  import { type Framework, detectFrameworkInfo } from "./lib/framework";
8
9
  import { stringify } from "./lib/json";
9
10
  import { findSliceModel } from "./lib/slice";
@@ -89,6 +90,15 @@ export async function sliceAddFieldDate(): Promise<void> {
89
90
  return;
90
91
  }
91
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
+
92
102
  // Find the slice model
93
103
  const result = await findSliceModel(sliceId);
94
104
  if (!result.ok) {
@@ -124,26 +134,53 @@ export async function sliceAddFieldDate(): Promise<void> {
124
134
  targetVariation.primary = {};
125
135
  }
126
136
 
127
- // Check if field already exists in any variation
128
- for (const v of model.variations) {
129
- if (v.primary?.[fieldId]) {
130
- console.error(`Field "${fieldId}" already exists in variation "${v.id}"`);
131
- process.exitCode = 1;
132
- return;
133
- }
134
- }
135
-
136
137
  // Build field definition
137
138
  const fieldDefinition: DateField = {
138
139
  type: "Date",
139
140
  config: {
140
- label: label ?? humanReadable(fieldId),
141
+ label: label ?? humanReadable(fieldPath.type === "nested" ? fieldPath.nestedFieldId : fieldId),
141
142
  ...(placeholder && { placeholder }),
142
143
  },
143
144
  };
144
145
 
145
- // Add field to variation
146
- 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
+ }
147
184
 
148
185
  // Write updated model
149
186
  try {
@@ -158,9 +195,15 @@ export async function sliceAddFieldDate(): Promise<void> {
158
195
  return;
159
196
  }
160
197
 
161
- console.info(
162
- `Added field "${fieldId}" (Date) to "${targetVariation.id}" variation in ${sliceId}`,
163
- );
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
+ }
164
207
 
165
208
  try {
166
209
  await buildTypes({ output: types });
@@ -177,7 +220,7 @@ export async function sliceAddFieldDate(): Promise<void> {
177
220
  const docsPath = getDocsPath(frameworkInfo.framework);
178
221
  const anchor = getWriteComponentsAnchor(frameworkInfo.framework);
179
222
  console.info(
180
- ` Run \`prismic docs ${docsPath}${anchor}\` to learn how to implement the slice's component`,
223
+ ` Run \`prismic docs fetch ${docsPath}${anchor}\` to learn how to implement the slice's component`,
181
224
  );
182
225
  }
183
226
  }