@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.
- package/dist/index.mjs +302 -168
- package/package.json +1 -1
- package/src/custom-type-add-field-boolean.ts +49 -12
- package/src/custom-type-add-field-color.ts +46 -12
- package/src/custom-type-add-field-date.ts +46 -12
- package/src/custom-type-add-field-embed.ts +46 -12
- package/src/custom-type-add-field-geo-point.ts +46 -12
- package/src/custom-type-add-field-group.ts +179 -0
- package/src/custom-type-add-field-image.ts +46 -12
- package/src/custom-type-add-field-key-text.ts +46 -12
- package/src/custom-type-add-field-link.ts +46 -12
- package/src/custom-type-add-field-number.ts +46 -12
- package/src/custom-type-add-field-rich-text.ts +46 -12
- package/src/custom-type-add-field-select.ts +47 -21
- package/src/custom-type-add-field-timestamp.ts +46 -12
- package/src/custom-type-add-field-uid.ts +17 -0
- package/src/custom-type-add-field.ts +5 -0
- package/src/index.ts +5 -0
- package/src/lib/field-path.ts +81 -0
- package/src/page-type-add-field-boolean.ts +66 -13
- package/src/page-type-add-field-color.ts +66 -13
- package/src/page-type-add-field-date.ts +66 -13
- package/src/page-type-add-field-embed.ts +66 -13
- package/src/page-type-add-field-geo-point.ts +66 -13
- package/src/page-type-add-field-group.ts +198 -0
- package/src/page-type-add-field-image.ts +66 -13
- package/src/page-type-add-field-key-text.ts +66 -13
- package/src/page-type-add-field-link.ts +66 -13
- package/src/page-type-add-field-number.ts +66 -13
- package/src/page-type-add-field-rich-text.ts +66 -13
- package/src/page-type-add-field-select.ts +67 -22
- package/src/page-type-add-field-timestamp.ts +66 -13
- package/src/page-type-add-field-uid.ts +37 -1
- package/src/page-type-add-field.ts +5 -0
- package/src/page-type-create.ts +25 -0
- package/src/repo-create.ts +59 -0
- package/src/skill-install.ts +177 -0
- package/src/skill-uninstall.ts +85 -0
- package/src/skill.ts +50 -0
- package/src/slice-add-field-boolean.ts +90 -16
- package/src/slice-add-field-color.ts +90 -16
- package/src/slice-add-field-date.ts +90 -16
- package/src/slice-add-field-embed.ts +90 -16
- package/src/slice-add-field-geo-point.ts +90 -16
- package/src/slice-add-field-group.ts +191 -0
- package/src/slice-add-field-image.ts +90 -16
- package/src/slice-add-field-key-text.ts +90 -16
- package/src/slice-add-field-link.ts +90 -16
- package/src/slice-add-field-number.ts +90 -16
- package/src/slice-add-field-rich-text.ts +90 -16
- package/src/slice-add-field-select.ts +91 -25
- package/src/slice-add-field-timestamp.ts +90 -16
- package/src/slice-add-field.ts +5 -0
- package/src/slice-create.ts +66 -5
- package/src/slice-set-screenshot.ts +235 -0
- package/src/slice-view.ts +3 -0
- package/src/slice.ts +5 -0
- 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";
|
|
@@ -40,6 +42,28 @@ EXAMPLES
|
|
|
40
42
|
prismic slice add-field rich-text blog post --multi "paragraph,strong,em,hyperlink" --allow-target-blank
|
|
41
43
|
`.trim();
|
|
42
44
|
|
|
45
|
+
function getDocsPath(framework: Framework): string {
|
|
46
|
+
switch (framework) {
|
|
47
|
+
case "next":
|
|
48
|
+
return "nextjs/with-cli";
|
|
49
|
+
case "nuxt":
|
|
50
|
+
return "nuxt/with-cli";
|
|
51
|
+
case "sveltekit":
|
|
52
|
+
return "sveltekit/with-cli";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getWriteComponentsAnchor(framework: Framework): string {
|
|
57
|
+
switch (framework) {
|
|
58
|
+
case "nuxt":
|
|
59
|
+
return "#write-vue-components";
|
|
60
|
+
case "sveltekit":
|
|
61
|
+
return "#write-svelte-components";
|
|
62
|
+
default:
|
|
63
|
+
return "#write-react-components";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
43
67
|
export async function sliceAddFieldRichText(): Promise<void> {
|
|
44
68
|
const {
|
|
45
69
|
values: {
|
|
@@ -87,6 +111,15 @@ export async function sliceAddFieldRichText(): Promise<void> {
|
|
|
87
111
|
return;
|
|
88
112
|
}
|
|
89
113
|
|
|
114
|
+
// Parse and validate field path
|
|
115
|
+
const fieldPath = parseFieldPath(fieldId);
|
|
116
|
+
const pathValidation = validateNestedFieldPath(fieldPath);
|
|
117
|
+
if (!pathValidation.ok) {
|
|
118
|
+
console.error(pathValidation.error);
|
|
119
|
+
process.exitCode = 1;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
90
123
|
// Find the slice model
|
|
91
124
|
const result = await findSliceModel(sliceId);
|
|
92
125
|
if (!result.ok) {
|
|
@@ -122,20 +155,11 @@ export async function sliceAddFieldRichText(): Promise<void> {
|
|
|
122
155
|
targetVariation.primary = {};
|
|
123
156
|
}
|
|
124
157
|
|
|
125
|
-
// Check if field already exists in any variation
|
|
126
|
-
for (const v of model.variations) {
|
|
127
|
-
if (v.primary?.[fieldId]) {
|
|
128
|
-
console.error(`Field "${fieldId}" already exists in variation "${v.id}"`);
|
|
129
|
-
process.exitCode = 1;
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
158
|
// Build field definition
|
|
135
159
|
const fieldDefinition: RichText = {
|
|
136
160
|
type: "StructuredText",
|
|
137
161
|
config: {
|
|
138
|
-
label: label ?? humanReadable(fieldId),
|
|
162
|
+
label: label ?? humanReadable(fieldPath.type === "nested" ? fieldPath.nestedFieldId : fieldId),
|
|
139
163
|
...(placeholder && { placeholder }),
|
|
140
164
|
...(single && { single }),
|
|
141
165
|
...(multi && { multi }),
|
|
@@ -143,8 +167,44 @@ export async function sliceAddFieldRichText(): Promise<void> {
|
|
|
143
167
|
},
|
|
144
168
|
};
|
|
145
169
|
|
|
146
|
-
// Add field to variation
|
|
147
|
-
|
|
170
|
+
// Add field to variation (with nested field support)
|
|
171
|
+
if (fieldPath.type === "nested") {
|
|
172
|
+
const groupResult = findGroupInVariation(targetVariation.primary, fieldPath.groupId, targetVariation.id);
|
|
173
|
+
if (!groupResult.ok) {
|
|
174
|
+
console.error(groupResult.error);
|
|
175
|
+
process.exitCode = 1;
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
// Check if nested field already exists
|
|
179
|
+
if (groupResult.group.config.fields[fieldPath.nestedFieldId]) {
|
|
180
|
+
console.error(
|
|
181
|
+
`Field "${fieldPath.nestedFieldId}" already exists in group "${fieldPath.groupId}"`,
|
|
182
|
+
);
|
|
183
|
+
process.exitCode = 1;
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
groupResult.group.config.fields[fieldPath.nestedFieldId] = fieldDefinition;
|
|
187
|
+
} else {
|
|
188
|
+
// Check if field already exists in any variation (at top level or in groups)
|
|
189
|
+
for (const v of model.variations) {
|
|
190
|
+
if (v.primary?.[fieldId]) {
|
|
191
|
+
console.error(`Field "${fieldId}" already exists in variation "${v.id}"`);
|
|
192
|
+
process.exitCode = 1;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
// Also check inside groups
|
|
196
|
+
for (const [groupFieldId, groupField] of Object.entries(v.primary ?? {})) {
|
|
197
|
+
if (isGroupField(groupField) && groupField.config.fields[fieldId]) {
|
|
198
|
+
console.error(
|
|
199
|
+
`Field "${fieldId}" already exists in group "${groupFieldId}" in variation "${v.id}"`,
|
|
200
|
+
);
|
|
201
|
+
process.exitCode = 1;
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
targetVariation.primary[fieldId] = fieldDefinition;
|
|
207
|
+
}
|
|
148
208
|
|
|
149
209
|
// Write updated model
|
|
150
210
|
try {
|
|
@@ -159,9 +219,15 @@ export async function sliceAddFieldRichText(): Promise<void> {
|
|
|
159
219
|
return;
|
|
160
220
|
}
|
|
161
221
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
222
|
+
if (fieldPath.type === "nested") {
|
|
223
|
+
console.info(
|
|
224
|
+
`Added field "${fieldPath.nestedFieldId}" (StructuredText) to group "${fieldPath.groupId}" in ${sliceId}`,
|
|
225
|
+
);
|
|
226
|
+
} else {
|
|
227
|
+
console.info(
|
|
228
|
+
`Added field "${fieldId}" (StructuredText) to "${targetVariation.id}" variation in ${sliceId}`,
|
|
229
|
+
);
|
|
230
|
+
}
|
|
165
231
|
|
|
166
232
|
try {
|
|
167
233
|
await buildTypes({ output: types });
|
|
@@ -172,5 +238,13 @@ export async function sliceAddFieldRichText(): Promise<void> {
|
|
|
172
238
|
|
|
173
239
|
console.info();
|
|
174
240
|
console.info("Next: Add more fields with `prismic slice add-field`");
|
|
175
|
-
|
|
241
|
+
|
|
242
|
+
const frameworkInfo = await detectFrameworkInfo();
|
|
243
|
+
if (frameworkInfo?.framework) {
|
|
244
|
+
const docsPath = getDocsPath(frameworkInfo.framework);
|
|
245
|
+
const anchor = getWriteComponentsAnchor(frameworkInfo.framework);
|
|
246
|
+
console.info(
|
|
247
|
+
` Run \`prismic docs ${docsPath}${anchor}\` to learn how to implement the slice's component`,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
176
250
|
}
|
|
@@ -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";
|
|
@@ -33,17 +35,31 @@ EXAMPLES
|
|
|
33
35
|
prismic slice add-field select product size --option "small" --option "medium" --option "large" --label "Size"
|
|
34
36
|
`.trim();
|
|
35
37
|
|
|
38
|
+
function getDocsPath(framework: Framework): string {
|
|
39
|
+
switch (framework) {
|
|
40
|
+
case "next":
|
|
41
|
+
return "nextjs/with-cli";
|
|
42
|
+
case "nuxt":
|
|
43
|
+
return "nuxt/with-cli";
|
|
44
|
+
case "sveltekit":
|
|
45
|
+
return "sveltekit/with-cli";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getWriteComponentsAnchor(framework: Framework): string {
|
|
50
|
+
switch (framework) {
|
|
51
|
+
case "nuxt":
|
|
52
|
+
return "#write-vue-components";
|
|
53
|
+
case "sveltekit":
|
|
54
|
+
return "#write-svelte-components";
|
|
55
|
+
default:
|
|
56
|
+
return "#write-react-components";
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
36
60
|
export async function sliceAddFieldSelect(): Promise<void> {
|
|
37
61
|
const {
|
|
38
|
-
values: {
|
|
39
|
-
help,
|
|
40
|
-
variation,
|
|
41
|
-
label,
|
|
42
|
-
placeholder,
|
|
43
|
-
option,
|
|
44
|
-
default: defaultValue,
|
|
45
|
-
types,
|
|
46
|
-
},
|
|
62
|
+
values: { help, variation, label, placeholder, option, default: defaultValue, types },
|
|
47
63
|
positionals: [sliceId, fieldId],
|
|
48
64
|
} = parseArgs({
|
|
49
65
|
args: process.argv.slice(5), // skip: node, script, "slice", "add-field", "select"
|
|
@@ -78,6 +94,15 @@ export async function sliceAddFieldSelect(): Promise<void> {
|
|
|
78
94
|
return;
|
|
79
95
|
}
|
|
80
96
|
|
|
97
|
+
// Parse and validate field path
|
|
98
|
+
const fieldPath = parseFieldPath(fieldId);
|
|
99
|
+
const pathValidation = validateNestedFieldPath(fieldPath);
|
|
100
|
+
if (!pathValidation.ok) {
|
|
101
|
+
console.error(pathValidation.error);
|
|
102
|
+
process.exitCode = 1;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
81
106
|
// Find the slice model
|
|
82
107
|
const result = await findSliceModel(sliceId);
|
|
83
108
|
if (!result.ok) {
|
|
@@ -113,28 +138,55 @@ export async function sliceAddFieldSelect(): Promise<void> {
|
|
|
113
138
|
targetVariation.primary = {};
|
|
114
139
|
}
|
|
115
140
|
|
|
116
|
-
// Check if field already exists in any variation
|
|
117
|
-
for (const v of model.variations) {
|
|
118
|
-
if (v.primary?.[fieldId]) {
|
|
119
|
-
console.error(`Field "${fieldId}" already exists in variation "${v.id}"`);
|
|
120
|
-
process.exitCode = 1;
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
141
|
// Build field definition
|
|
126
142
|
const fieldDefinition: Select = {
|
|
127
143
|
type: "Select",
|
|
128
144
|
config: {
|
|
129
|
-
label: label ?? humanReadable(fieldId),
|
|
145
|
+
label: label ?? humanReadable(fieldPath.type === "nested" ? fieldPath.nestedFieldId : fieldId),
|
|
130
146
|
...(placeholder && { placeholder }),
|
|
131
147
|
...(option && option.length > 0 && { options: option }),
|
|
132
148
|
...(defaultValue && { default_value: defaultValue }),
|
|
133
149
|
},
|
|
134
150
|
};
|
|
135
151
|
|
|
136
|
-
// Add field to variation
|
|
137
|
-
|
|
152
|
+
// Add field to variation (with nested field support)
|
|
153
|
+
if (fieldPath.type === "nested") {
|
|
154
|
+
const groupResult = findGroupInVariation(targetVariation.primary, fieldPath.groupId, targetVariation.id);
|
|
155
|
+
if (!groupResult.ok) {
|
|
156
|
+
console.error(groupResult.error);
|
|
157
|
+
process.exitCode = 1;
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
// Check if nested field already exists
|
|
161
|
+
if (groupResult.group.config.fields[fieldPath.nestedFieldId]) {
|
|
162
|
+
console.error(
|
|
163
|
+
`Field "${fieldPath.nestedFieldId}" already exists in group "${fieldPath.groupId}"`,
|
|
164
|
+
);
|
|
165
|
+
process.exitCode = 1;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
groupResult.group.config.fields[fieldPath.nestedFieldId] = fieldDefinition;
|
|
169
|
+
} else {
|
|
170
|
+
// Check if field already exists in any variation (at top level or in groups)
|
|
171
|
+
for (const v of model.variations) {
|
|
172
|
+
if (v.primary?.[fieldId]) {
|
|
173
|
+
console.error(`Field "${fieldId}" already exists in variation "${v.id}"`);
|
|
174
|
+
process.exitCode = 1;
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
// Also check inside groups
|
|
178
|
+
for (const [groupFieldId, groupField] of Object.entries(v.primary ?? {})) {
|
|
179
|
+
if (isGroupField(groupField) && groupField.config.fields[fieldId]) {
|
|
180
|
+
console.error(
|
|
181
|
+
`Field "${fieldId}" already exists in group "${groupFieldId}" in variation "${v.id}"`,
|
|
182
|
+
);
|
|
183
|
+
process.exitCode = 1;
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
targetVariation.primary[fieldId] = fieldDefinition;
|
|
189
|
+
}
|
|
138
190
|
|
|
139
191
|
// Write updated model
|
|
140
192
|
try {
|
|
@@ -149,9 +201,15 @@ export async function sliceAddFieldSelect(): Promise<void> {
|
|
|
149
201
|
return;
|
|
150
202
|
}
|
|
151
203
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
204
|
+
if (fieldPath.type === "nested") {
|
|
205
|
+
console.info(
|
|
206
|
+
`Added field "${fieldPath.nestedFieldId}" (Select) to group "${fieldPath.groupId}" in ${sliceId}`,
|
|
207
|
+
);
|
|
208
|
+
} else {
|
|
209
|
+
console.info(
|
|
210
|
+
`Added field "${fieldId}" (Select) to "${targetVariation.id}" variation in ${sliceId}`,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
155
213
|
|
|
156
214
|
try {
|
|
157
215
|
await buildTypes({ output: types });
|
|
@@ -162,5 +220,13 @@ export async function sliceAddFieldSelect(): Promise<void> {
|
|
|
162
220
|
|
|
163
221
|
console.info();
|
|
164
222
|
console.info("Next: Add more fields with `prismic slice add-field`");
|
|
165
|
-
|
|
223
|
+
|
|
224
|
+
const frameworkInfo = await detectFrameworkInfo();
|
|
225
|
+
if (frameworkInfo?.framework) {
|
|
226
|
+
const docsPath = getDocsPath(frameworkInfo.framework);
|
|
227
|
+
const anchor = getWriteComponentsAnchor(frameworkInfo.framework);
|
|
228
|
+
console.info(
|
|
229
|
+
` Run \`prismic docs ${docsPath}${anchor}\` to learn how to implement the slice's component`,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
166
232
|
}
|
|
@@ -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 timestamp schedule meeting_time --variation "detailed"
|
|
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 sliceAddFieldTimestamp(): Promise<void> {
|
|
35
59
|
const {
|
|
36
60
|
values: { help, variation, label, placeholder, types },
|
|
@@ -66,6 +90,15 @@ export async function sliceAddFieldTimestamp(): 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 sliceAddFieldTimestamp(): 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: Timestamp = {
|
|
115
139
|
type: "Timestamp",
|
|
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
|
-
|
|
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 sliceAddFieldTimestamp(): Promise<void> {
|
|
|
135
195
|
return;
|
|
136
196
|
}
|
|
137
197
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
198
|
+
if (fieldPath.type === "nested") {
|
|
199
|
+
console.info(
|
|
200
|
+
`Added field "${fieldPath.nestedFieldId}" (Timestamp) to group "${fieldPath.groupId}" in ${sliceId}`,
|
|
201
|
+
);
|
|
202
|
+
} else {
|
|
203
|
+
console.info(
|
|
204
|
+
`Added field "${fieldId}" (Timestamp) 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 sliceAddFieldTimestamp(): Promise<void> {
|
|
|
148
214
|
|
|
149
215
|
console.info();
|
|
150
216
|
console.info("Next: Add more fields with `prismic slice add-field`");
|
|
151
|
-
|
|
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
|
}
|
package/src/slice-add-field.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { sliceAddFieldColor } from "./slice-add-field-color";
|
|
|
5
5
|
import { sliceAddFieldDate } from "./slice-add-field-date";
|
|
6
6
|
import { sliceAddFieldEmbed } from "./slice-add-field-embed";
|
|
7
7
|
import { sliceAddFieldGeoPoint } from "./slice-add-field-geo-point";
|
|
8
|
+
import { sliceAddFieldGroup } from "./slice-add-field-group";
|
|
8
9
|
import { sliceAddFieldImage } from "./slice-add-field-image";
|
|
9
10
|
import { sliceAddFieldKeyText } from "./slice-add-field-key-text";
|
|
10
11
|
import { sliceAddFieldLink } from "./slice-add-field-link";
|
|
@@ -25,6 +26,7 @@ FIELD TYPES
|
|
|
25
26
|
date Date picker
|
|
26
27
|
embed Embed (oEmbed)
|
|
27
28
|
geo-point Geographic coordinates
|
|
29
|
+
group Repeatable group of fields
|
|
28
30
|
image Image
|
|
29
31
|
key-text Single-line text
|
|
30
32
|
link Any link type
|
|
@@ -74,6 +76,9 @@ export async function sliceAddField(): Promise<void> {
|
|
|
74
76
|
case "geo-point":
|
|
75
77
|
await sliceAddFieldGeoPoint();
|
|
76
78
|
break;
|
|
79
|
+
case "group":
|
|
80
|
+
await sliceAddFieldGroup();
|
|
81
|
+
break;
|
|
77
82
|
case "image":
|
|
78
83
|
await sliceAddFieldImage();
|
|
79
84
|
break;
|
package/src/slice-create.ts
CHANGED
|
@@ -5,8 +5,11 @@ import { parseArgs } from "node:util";
|
|
|
5
5
|
import * as v from "valibot";
|
|
6
6
|
|
|
7
7
|
import { buildTypes } from "./codegen-types";
|
|
8
|
+
import { isAuthenticated } from "./lib/auth";
|
|
9
|
+
import { safeGetRepositoryFromConfig } from "./lib/config";
|
|
8
10
|
import { exists, findUpward } from "./lib/file";
|
|
9
11
|
import { stringify } from "./lib/json";
|
|
12
|
+
import { uploadScreenshot } from "./slice-set-screenshot";
|
|
10
13
|
|
|
11
14
|
const HELP = `
|
|
12
15
|
Create a new slice in a Prismic project.
|
|
@@ -18,9 +21,11 @@ ARGUMENTS
|
|
|
18
21
|
id Slice identifier (required)
|
|
19
22
|
|
|
20
23
|
FLAGS
|
|
21
|
-
-n, --name string
|
|
22
|
-
--
|
|
23
|
-
-
|
|
24
|
+
-n, --name string Display name for the slice
|
|
25
|
+
--screenshot string Path to screenshot image for default variation
|
|
26
|
+
-r, --repo string Repository name (required for screenshot)
|
|
27
|
+
--types string Output file for generated types (default: "prismicio-types.d.ts")
|
|
28
|
+
-h, --help Show help for command
|
|
24
29
|
|
|
25
30
|
LEARN MORE
|
|
26
31
|
Use \`prismic slice <command> --help\` for more information about a command.
|
|
@@ -28,12 +33,14 @@ LEARN MORE
|
|
|
28
33
|
|
|
29
34
|
export async function sliceCreate(): Promise<void> {
|
|
30
35
|
const {
|
|
31
|
-
values: { help, name, types },
|
|
36
|
+
values: { help, name, types, screenshot, repo: repoFlag },
|
|
32
37
|
positionals: [id],
|
|
33
38
|
} = parseArgs({
|
|
34
39
|
args: process.argv.slice(4), // skip: node, script, "slice", "create"
|
|
35
40
|
options: {
|
|
36
41
|
name: { type: "string", short: "n" },
|
|
42
|
+
screenshot: { type: "string" },
|
|
43
|
+
repo: { type: "string", short: "r" },
|
|
37
44
|
types: { type: "string" },
|
|
38
45
|
help: { type: "boolean", short: "h" },
|
|
39
46
|
},
|
|
@@ -51,6 +58,60 @@ export async function sliceCreate(): Promise<void> {
|
|
|
51
58
|
return;
|
|
52
59
|
}
|
|
53
60
|
|
|
61
|
+
// Handle screenshot upload if provided
|
|
62
|
+
let screenshotUrl: string | undefined;
|
|
63
|
+
if (screenshot) {
|
|
64
|
+
// Check authentication
|
|
65
|
+
const authenticated = await isAuthenticated();
|
|
66
|
+
if (!authenticated) {
|
|
67
|
+
console.error("You must be logged in to upload a screenshot.");
|
|
68
|
+
console.error("Run `prismic login` to authenticate.");
|
|
69
|
+
process.exitCode = 1;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Resolve repository
|
|
74
|
+
const repo = repoFlag ?? (await safeGetRepositoryFromConfig());
|
|
75
|
+
if (!repo) {
|
|
76
|
+
console.error("Could not determine repository for screenshot upload.");
|
|
77
|
+
console.error("Use --repo flag or run from a directory with prismic.config.json");
|
|
78
|
+
process.exitCode = 1;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Read and upload the screenshot
|
|
83
|
+
let imageData: Buffer;
|
|
84
|
+
try {
|
|
85
|
+
imageData = await readFile(screenshot);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
if (error instanceof Error) {
|
|
88
|
+
console.error(`Failed to read screenshot file: ${error.message}`);
|
|
89
|
+
} else {
|
|
90
|
+
console.error("Failed to read screenshot file");
|
|
91
|
+
}
|
|
92
|
+
process.exitCode = 1;
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
screenshotUrl = await uploadScreenshot({
|
|
98
|
+
data: imageData,
|
|
99
|
+
repo,
|
|
100
|
+
sliceId: id,
|
|
101
|
+
variationId: "default",
|
|
102
|
+
filename: screenshot,
|
|
103
|
+
});
|
|
104
|
+
} catch (error) {
|
|
105
|
+
if (error instanceof Error) {
|
|
106
|
+
console.error(`Failed to upload screenshot: ${error.message}`);
|
|
107
|
+
} else {
|
|
108
|
+
console.error("Failed to upload screenshot");
|
|
109
|
+
}
|
|
110
|
+
process.exitCode = 1;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
54
115
|
const model: SharedSlice = {
|
|
55
116
|
id,
|
|
56
117
|
type: "SharedSlice",
|
|
@@ -61,7 +122,7 @@ export async function sliceCreate(): Promise<void> {
|
|
|
61
122
|
id: "default",
|
|
62
123
|
name: "Default",
|
|
63
124
|
description: "Default",
|
|
64
|
-
imageUrl: "",
|
|
125
|
+
imageUrl: screenshotUrl ?? "",
|
|
65
126
|
docURL: "",
|
|
66
127
|
version: "initial",
|
|
67
128
|
primary: {},
|