@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
|
@@ -6,6 +6,8 @@ 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";
|
|
10
|
+
import { type Framework, detectFrameworkInfo } from "./lib/framework";
|
|
9
11
|
import { stringify } from "./lib/json";
|
|
10
12
|
import { humanReadable } from "./lib/string";
|
|
11
13
|
|
|
@@ -40,6 +42,17 @@ const CustomTypeSchema = v.object({
|
|
|
40
42
|
json: v.record(v.string(), v.record(v.string(), v.unknown())),
|
|
41
43
|
});
|
|
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
|
+
|
|
43
56
|
export async function pageTypeAddFieldGeoPoint(): Promise<void> {
|
|
44
57
|
const {
|
|
45
58
|
values: { help, tab, label, types },
|
|
@@ -74,6 +87,15 @@ export async function pageTypeAddFieldGeoPoint(): Promise<void> {
|
|
|
74
87
|
return;
|
|
75
88
|
}
|
|
76
89
|
|
|
90
|
+
// Parse and validate field path
|
|
91
|
+
const fieldPath = parseFieldPath(fieldId);
|
|
92
|
+
const pathValidation = validateNestedFieldPath(fieldPath);
|
|
93
|
+
if (!pathValidation.ok) {
|
|
94
|
+
console.error(pathValidation.error);
|
|
95
|
+
process.exitCode = 1;
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
77
99
|
// Find the page type file
|
|
78
100
|
const projectRoot = await findUpward("package.json");
|
|
79
101
|
if (!projectRoot) {
|
|
@@ -120,25 +142,45 @@ export async function pageTypeAddFieldGeoPoint(): Promise<void> {
|
|
|
120
142
|
model.json[targetTab] = {};
|
|
121
143
|
}
|
|
122
144
|
|
|
123
|
-
// Check if field already exists in any tab
|
|
124
|
-
for (const [tabName, tabFields] of Object.entries(model.json)) {
|
|
125
|
-
if (tabFields[fieldId]) {
|
|
126
|
-
console.error(`Field "${fieldId}" already exists in tab "${tabName}"`);
|
|
127
|
-
process.exitCode = 1;
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
145
|
// Build field definition
|
|
133
146
|
const fieldDefinition: GeoPoint = {
|
|
134
147
|
type: "GeoPoint",
|
|
135
148
|
config: {
|
|
136
|
-
label: label ?? humanReadable(fieldId),
|
|
149
|
+
label: label ?? humanReadable(fieldPath.type === "nested" ? fieldPath.nestedFieldId : fieldId),
|
|
137
150
|
},
|
|
138
151
|
};
|
|
139
152
|
|
|
140
153
|
// Add field to model
|
|
141
|
-
|
|
154
|
+
if (fieldPath.type === "nested") {
|
|
155
|
+
const groupResult = findGroupInTab(model.json[targetTab], fieldPath.groupId, targetTab);
|
|
156
|
+
if (!groupResult.ok) {
|
|
157
|
+
console.error(groupResult.error);
|
|
158
|
+
process.exitCode = 1;
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (groupResult.group.config.fields[fieldPath.nestedFieldId]) {
|
|
162
|
+
console.error(`Field "${fieldPath.nestedFieldId}" already exists in group "${fieldPath.groupId}"`);
|
|
163
|
+
process.exitCode = 1;
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
groupResult.group.config.fields[fieldPath.nestedFieldId] = fieldDefinition;
|
|
167
|
+
} else {
|
|
168
|
+
for (const [tabName, tabFields] of Object.entries(model.json)) {
|
|
169
|
+
if (tabFields[fieldId]) {
|
|
170
|
+
console.error(`Field "${fieldId}" already exists in tab "${tabName}"`);
|
|
171
|
+
process.exitCode = 1;
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
for (const [groupFieldId, groupField] of Object.entries(tabFields)) {
|
|
175
|
+
if (isGroupField(groupField) && groupField.config.fields[fieldId]) {
|
|
176
|
+
console.error(`Field "${fieldId}" already exists in group "${groupFieldId}" in tab "${tabName}"`);
|
|
177
|
+
process.exitCode = 1;
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
model.json[targetTab][fieldId] = fieldDefinition;
|
|
183
|
+
}
|
|
142
184
|
|
|
143
185
|
// Write updated model
|
|
144
186
|
try {
|
|
@@ -153,7 +195,11 @@ export async function pageTypeAddFieldGeoPoint(): Promise<void> {
|
|
|
153
195
|
return;
|
|
154
196
|
}
|
|
155
197
|
|
|
156
|
-
|
|
198
|
+
if (fieldPath.type === "nested") {
|
|
199
|
+
console.info(`Added field "${fieldPath.nestedFieldId}" (GeoPoint) to group "${fieldPath.groupId}" in ${typeId}`);
|
|
200
|
+
} else {
|
|
201
|
+
console.info(`Added field "${fieldId}" (GeoPoint) to "${targetTab}" tab in ${typeId}`);
|
|
202
|
+
}
|
|
157
203
|
|
|
158
204
|
try {
|
|
159
205
|
await buildTypes({ output: types });
|
|
@@ -164,5 +210,12 @@ export async function pageTypeAddFieldGeoPoint(): Promise<void> {
|
|
|
164
210
|
|
|
165
211
|
console.info();
|
|
166
212
|
console.info("Next: Add more fields with `prismic page-type add-field`");
|
|
167
|
-
|
|
213
|
+
|
|
214
|
+
const frameworkInfo = await detectFrameworkInfo();
|
|
215
|
+
if (frameworkInfo?.framework) {
|
|
216
|
+
const docsPath = getDocsPath(frameworkInfo.framework);
|
|
217
|
+
console.info(
|
|
218
|
+
` Run \`prismic docs ${docsPath}#write-page-components\` to learn how to implement a page file`,
|
|
219
|
+
);
|
|
220
|
+
}
|
|
168
221
|
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import type { CustomType, Group } from "@prismicio/types-internal/lib/customtypes";
|
|
2
|
+
|
|
3
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
import { parseArgs } from "node:util";
|
|
5
|
+
import * as v from "valibot";
|
|
6
|
+
|
|
7
|
+
import { buildTypes } from "./codegen-types";
|
|
8
|
+
import { findUpward } from "./lib/file";
|
|
9
|
+
import { type Framework, detectFrameworkInfo } from "./lib/framework";
|
|
10
|
+
import { stringify } from "./lib/json";
|
|
11
|
+
import { humanReadable } from "./lib/string";
|
|
12
|
+
|
|
13
|
+
const HELP = `
|
|
14
|
+
Add a group field to an existing page type.
|
|
15
|
+
|
|
16
|
+
USAGE
|
|
17
|
+
prismic page-type add-field group <type-id> <field-id> [flags]
|
|
18
|
+
|
|
19
|
+
ARGUMENTS
|
|
20
|
+
type-id Page type identifier (required)
|
|
21
|
+
field-id Field identifier (required)
|
|
22
|
+
|
|
23
|
+
FLAGS
|
|
24
|
+
-t, --tab string Target tab (default: first existing tab, or "Main")
|
|
25
|
+
-l, --label string Display label for the field (inferred from field-id if omitted)
|
|
26
|
+
--non-repeatable Make this a non-repeating group (default: repeatable)
|
|
27
|
+
--types string Output file for generated types (default: "prismicio-types.d.ts")
|
|
28
|
+
-h, --help Show help for command
|
|
29
|
+
|
|
30
|
+
EXAMPLES
|
|
31
|
+
prismic page-type add-field group homepage buttons
|
|
32
|
+
prismic page-type add-field group article authors --non-repeatable
|
|
33
|
+
prismic page-type add-field group product variants --tab "Content"
|
|
34
|
+
`.trim();
|
|
35
|
+
|
|
36
|
+
const CustomTypeSchema = v.object({
|
|
37
|
+
id: v.string(),
|
|
38
|
+
label: v.string(),
|
|
39
|
+
repeatable: v.boolean(),
|
|
40
|
+
status: v.boolean(),
|
|
41
|
+
format: v.string(),
|
|
42
|
+
json: v.record(v.string(), v.record(v.string(), v.unknown())),
|
|
43
|
+
});
|
|
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
|
+
export async function pageTypeAddFieldGroup(): Promise<void> {
|
|
57
|
+
const {
|
|
58
|
+
values: { help, tab, label, "non-repeatable": nonRepeatable, types },
|
|
59
|
+
positionals: [typeId, fieldId],
|
|
60
|
+
} = parseArgs({
|
|
61
|
+
args: process.argv.slice(5), // skip: node, script, "page-type", "add-field", "group"
|
|
62
|
+
options: {
|
|
63
|
+
tab: { type: "string", short: "t" },
|
|
64
|
+
label: { type: "string", short: "l" },
|
|
65
|
+
"non-repeatable": { type: "boolean" },
|
|
66
|
+
types: { type: "string" },
|
|
67
|
+
help: { type: "boolean", short: "h" },
|
|
68
|
+
},
|
|
69
|
+
allowPositionals: true,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (help) {
|
|
73
|
+
console.info(HELP);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!typeId) {
|
|
78
|
+
console.error("Missing required argument: type-id\n");
|
|
79
|
+
console.error("Usage: prismic page-type add-field group <type-id> <field-id>");
|
|
80
|
+
process.exitCode = 1;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!fieldId) {
|
|
85
|
+
console.error("Missing required argument: field-id\n");
|
|
86
|
+
console.error("Usage: prismic page-type add-field group <type-id> <field-id>");
|
|
87
|
+
process.exitCode = 1;
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Groups cannot be nested
|
|
92
|
+
if (fieldId.includes(".")) {
|
|
93
|
+
console.error("Groups cannot be nested inside other groups");
|
|
94
|
+
process.exitCode = 1;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Find the page type file
|
|
99
|
+
const projectRoot = await findUpward("package.json");
|
|
100
|
+
if (!projectRoot) {
|
|
101
|
+
console.error("Could not find project root (no package.json found)");
|
|
102
|
+
process.exitCode = 1;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const modelPath = new URL(`customtypes/${typeId}/index.json`, projectRoot);
|
|
107
|
+
|
|
108
|
+
// Read and parse the model
|
|
109
|
+
let model: CustomType;
|
|
110
|
+
try {
|
|
111
|
+
const contents = await readFile(modelPath, "utf8");
|
|
112
|
+
const result = v.safeParse(CustomTypeSchema, JSON.parse(contents));
|
|
113
|
+
if (!result.success) {
|
|
114
|
+
console.error(`Invalid page type model: ${modelPath.href}`);
|
|
115
|
+
process.exitCode = 1;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
model = result.output as CustomType;
|
|
119
|
+
} catch (error) {
|
|
120
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
121
|
+
console.error(`Page type not found: ${typeId}\n`);
|
|
122
|
+
console.error(`Create it first with: prismic page-type create ${typeId}`);
|
|
123
|
+
process.exitCode = 1;
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (error instanceof Error) {
|
|
127
|
+
console.error(`Failed to read page type: ${error.message}`);
|
|
128
|
+
} else {
|
|
129
|
+
console.error("Failed to read page type");
|
|
130
|
+
}
|
|
131
|
+
process.exitCode = 1;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Determine target tab
|
|
136
|
+
const existingTabs = Object.keys(model.json);
|
|
137
|
+
const targetTab = tab ?? existingTabs[0] ?? "Main";
|
|
138
|
+
|
|
139
|
+
// Initialize tab if it doesn't exist
|
|
140
|
+
if (!model.json[targetTab]) {
|
|
141
|
+
model.json[targetTab] = {};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Check if field already exists in any tab
|
|
145
|
+
for (const [tabName, tabFields] of Object.entries(model.json)) {
|
|
146
|
+
if (tabFields[fieldId]) {
|
|
147
|
+
console.error(`Field "${fieldId}" already exists in tab "${tabName}"`);
|
|
148
|
+
process.exitCode = 1;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Build field definition
|
|
154
|
+
const fieldDefinition: Group = {
|
|
155
|
+
type: "Group",
|
|
156
|
+
config: {
|
|
157
|
+
label: label ?? humanReadable(fieldId),
|
|
158
|
+
repeat: !nonRepeatable,
|
|
159
|
+
fields: {},
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// Add field to model
|
|
164
|
+
model.json[targetTab][fieldId] = fieldDefinition;
|
|
165
|
+
|
|
166
|
+
// Write updated model
|
|
167
|
+
try {
|
|
168
|
+
await writeFile(modelPath, stringify(model));
|
|
169
|
+
} catch (error) {
|
|
170
|
+
if (error instanceof Error) {
|
|
171
|
+
console.error(`Failed to update page type: ${error.message}`);
|
|
172
|
+
} else {
|
|
173
|
+
console.error("Failed to update page type");
|
|
174
|
+
}
|
|
175
|
+
process.exitCode = 1;
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
console.info(`Added field "${fieldId}" (Group) to "${targetTab}" tab in ${typeId}`);
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
await buildTypes({ output: types });
|
|
183
|
+
console.info(`Updated types in ${types ?? "prismicio-types.d.ts"}`);
|
|
184
|
+
} catch (error) {
|
|
185
|
+
console.warn(`Could not generate types: ${error instanceof Error ? error.message : error}`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
console.info();
|
|
189
|
+
console.info(`Next: Add fields to the group with \`prismic page-type add-field <type> ${typeId} ${fieldId}.<field-id>\``);
|
|
190
|
+
|
|
191
|
+
const frameworkInfo = await detectFrameworkInfo();
|
|
192
|
+
if (frameworkInfo?.framework) {
|
|
193
|
+
const docsPath = getDocsPath(frameworkInfo.framework);
|
|
194
|
+
console.info(
|
|
195
|
+
` Run \`prismic docs ${docsPath}#write-page-components\` to learn how to implement a page file`,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
@@ -6,6 +6,8 @@ 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";
|
|
10
|
+
import { type Framework, detectFrameworkInfo } from "./lib/framework";
|
|
9
11
|
import { stringify } from "./lib/json";
|
|
10
12
|
import { humanReadable } from "./lib/string";
|
|
11
13
|
|
|
@@ -41,6 +43,17 @@ const CustomTypeSchema = v.object({
|
|
|
41
43
|
json: v.record(v.string(), v.record(v.string(), v.unknown())),
|
|
42
44
|
});
|
|
43
45
|
|
|
46
|
+
function getDocsPath(framework: Framework): string {
|
|
47
|
+
switch (framework) {
|
|
48
|
+
case "next":
|
|
49
|
+
return "nextjs/with-cli";
|
|
50
|
+
case "nuxt":
|
|
51
|
+
return "nuxt/with-cli";
|
|
52
|
+
case "sveltekit":
|
|
53
|
+
return "sveltekit/with-cli";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
44
57
|
export async function pageTypeAddFieldImage(): Promise<void> {
|
|
45
58
|
const {
|
|
46
59
|
values: { help, tab, label, placeholder, types },
|
|
@@ -76,6 +89,15 @@ export async function pageTypeAddFieldImage(): Promise<void> {
|
|
|
76
89
|
return;
|
|
77
90
|
}
|
|
78
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
|
+
|
|
79
101
|
// Find the page type file
|
|
80
102
|
const projectRoot = await findUpward("package.json");
|
|
81
103
|
if (!projectRoot) {
|
|
@@ -122,26 +144,46 @@ export async function pageTypeAddFieldImage(): Promise<void> {
|
|
|
122
144
|
model.json[targetTab] = {};
|
|
123
145
|
}
|
|
124
146
|
|
|
125
|
-
// Check if field already exists in any tab
|
|
126
|
-
for (const [tabName, tabFields] of Object.entries(model.json)) {
|
|
127
|
-
if (tabFields[fieldId]) {
|
|
128
|
-
console.error(`Field "${fieldId}" already exists in tab "${tabName}"`);
|
|
129
|
-
process.exitCode = 1;
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
147
|
// Build field definition
|
|
135
148
|
const fieldDefinition: Image = {
|
|
136
149
|
type: "Image",
|
|
137
150
|
config: {
|
|
138
|
-
label: label ?? humanReadable(fieldId),
|
|
151
|
+
label: label ?? humanReadable(fieldPath.type === "nested" ? fieldPath.nestedFieldId : fieldId),
|
|
139
152
|
...(placeholder && { placeholder }),
|
|
140
153
|
},
|
|
141
154
|
};
|
|
142
155
|
|
|
143
156
|
// Add field to model
|
|
144
|
-
|
|
157
|
+
if (fieldPath.type === "nested") {
|
|
158
|
+
const groupResult = findGroupInTab(model.json[targetTab], fieldPath.groupId, targetTab);
|
|
159
|
+
if (!groupResult.ok) {
|
|
160
|
+
console.error(groupResult.error);
|
|
161
|
+
process.exitCode = 1;
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (groupResult.group.config.fields[fieldPath.nestedFieldId]) {
|
|
165
|
+
console.error(`Field "${fieldPath.nestedFieldId}" already exists in group "${fieldPath.groupId}"`);
|
|
166
|
+
process.exitCode = 1;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
groupResult.group.config.fields[fieldPath.nestedFieldId] = fieldDefinition;
|
|
170
|
+
} else {
|
|
171
|
+
for (const [tabName, tabFields] of Object.entries(model.json)) {
|
|
172
|
+
if (tabFields[fieldId]) {
|
|
173
|
+
console.error(`Field "${fieldId}" already exists in tab "${tabName}"`);
|
|
174
|
+
process.exitCode = 1;
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
for (const [groupFieldId, groupField] of Object.entries(tabFields)) {
|
|
178
|
+
if (isGroupField(groupField) && groupField.config.fields[fieldId]) {
|
|
179
|
+
console.error(`Field "${fieldId}" already exists in group "${groupFieldId}" in tab "${tabName}"`);
|
|
180
|
+
process.exitCode = 1;
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
model.json[targetTab][fieldId] = fieldDefinition;
|
|
186
|
+
}
|
|
145
187
|
|
|
146
188
|
// Write updated model
|
|
147
189
|
try {
|
|
@@ -156,7 +198,11 @@ export async function pageTypeAddFieldImage(): Promise<void> {
|
|
|
156
198
|
return;
|
|
157
199
|
}
|
|
158
200
|
|
|
159
|
-
|
|
201
|
+
if (fieldPath.type === "nested") {
|
|
202
|
+
console.info(`Added field "${fieldPath.nestedFieldId}" (Image) to group "${fieldPath.groupId}" in ${typeId}`);
|
|
203
|
+
} else {
|
|
204
|
+
console.info(`Added field "${fieldId}" (Image) to "${targetTab}" tab in ${typeId}`);
|
|
205
|
+
}
|
|
160
206
|
|
|
161
207
|
try {
|
|
162
208
|
await buildTypes({ output: types });
|
|
@@ -167,5 +213,12 @@ export async function pageTypeAddFieldImage(): Promise<void> {
|
|
|
167
213
|
|
|
168
214
|
console.info();
|
|
169
215
|
console.info("Next: Add more fields with `prismic page-type add-field`");
|
|
170
|
-
|
|
216
|
+
|
|
217
|
+
const frameworkInfo = await detectFrameworkInfo();
|
|
218
|
+
if (frameworkInfo?.framework) {
|
|
219
|
+
const docsPath = getDocsPath(frameworkInfo.framework);
|
|
220
|
+
console.info(
|
|
221
|
+
` Run \`prismic docs ${docsPath}#write-page-components\` to learn how to implement a page file`,
|
|
222
|
+
);
|
|
223
|
+
}
|
|
171
224
|
}
|
|
@@ -6,6 +6,8 @@ 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";
|
|
10
|
+
import { type Framework, detectFrameworkInfo } from "./lib/framework";
|
|
9
11
|
import { stringify } from "./lib/json";
|
|
10
12
|
import { humanReadable } from "./lib/string";
|
|
11
13
|
|
|
@@ -41,6 +43,17 @@ const CustomTypeSchema = v.object({
|
|
|
41
43
|
json: v.record(v.string(), v.record(v.string(), v.unknown())),
|
|
42
44
|
});
|
|
43
45
|
|
|
46
|
+
function getDocsPath(framework: Framework): string {
|
|
47
|
+
switch (framework) {
|
|
48
|
+
case "next":
|
|
49
|
+
return "nextjs/with-cli";
|
|
50
|
+
case "nuxt":
|
|
51
|
+
return "nuxt/with-cli";
|
|
52
|
+
case "sveltekit":
|
|
53
|
+
return "sveltekit/with-cli";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
44
57
|
export async function pageTypeAddFieldKeyText(): Promise<void> {
|
|
45
58
|
const {
|
|
46
59
|
values: { help, tab, label, placeholder, types },
|
|
@@ -76,6 +89,15 @@ export async function pageTypeAddFieldKeyText(): Promise<void> {
|
|
|
76
89
|
return;
|
|
77
90
|
}
|
|
78
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
|
+
|
|
79
101
|
// Find the page type file
|
|
80
102
|
const projectRoot = await findUpward("package.json");
|
|
81
103
|
if (!projectRoot) {
|
|
@@ -122,26 +144,46 @@ export async function pageTypeAddFieldKeyText(): Promise<void> {
|
|
|
122
144
|
model.json[targetTab] = {};
|
|
123
145
|
}
|
|
124
146
|
|
|
125
|
-
// Check if field already exists in any tab
|
|
126
|
-
for (const [tabName, tabFields] of Object.entries(model.json)) {
|
|
127
|
-
if (tabFields[fieldId]) {
|
|
128
|
-
console.error(`Field "${fieldId}" already exists in tab "${tabName}"`);
|
|
129
|
-
process.exitCode = 1;
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
147
|
// Build field definition
|
|
135
148
|
const fieldDefinition: Text = {
|
|
136
149
|
type: "Text",
|
|
137
150
|
config: {
|
|
138
|
-
label: label ?? humanReadable(fieldId),
|
|
151
|
+
label: label ?? humanReadable(fieldPath.type === "nested" ? fieldPath.nestedFieldId : fieldId),
|
|
139
152
|
...(placeholder && { placeholder }),
|
|
140
153
|
},
|
|
141
154
|
};
|
|
142
155
|
|
|
143
156
|
// Add field to model
|
|
144
|
-
|
|
157
|
+
if (fieldPath.type === "nested") {
|
|
158
|
+
const groupResult = findGroupInTab(model.json[targetTab], fieldPath.groupId, targetTab);
|
|
159
|
+
if (!groupResult.ok) {
|
|
160
|
+
console.error(groupResult.error);
|
|
161
|
+
process.exitCode = 1;
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (groupResult.group.config.fields[fieldPath.nestedFieldId]) {
|
|
165
|
+
console.error(`Field "${fieldPath.nestedFieldId}" already exists in group "${fieldPath.groupId}"`);
|
|
166
|
+
process.exitCode = 1;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
groupResult.group.config.fields[fieldPath.nestedFieldId] = fieldDefinition;
|
|
170
|
+
} else {
|
|
171
|
+
for (const [tabName, tabFields] of Object.entries(model.json)) {
|
|
172
|
+
if (tabFields[fieldId]) {
|
|
173
|
+
console.error(`Field "${fieldId}" already exists in tab "${tabName}"`);
|
|
174
|
+
process.exitCode = 1;
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
for (const [groupFieldId, groupField] of Object.entries(tabFields)) {
|
|
178
|
+
if (isGroupField(groupField) && groupField.config.fields[fieldId]) {
|
|
179
|
+
console.error(`Field "${fieldId}" already exists in group "${groupFieldId}" in tab "${tabName}"`);
|
|
180
|
+
process.exitCode = 1;
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
model.json[targetTab][fieldId] = fieldDefinition;
|
|
186
|
+
}
|
|
145
187
|
|
|
146
188
|
// Write updated model
|
|
147
189
|
try {
|
|
@@ -156,7 +198,11 @@ export async function pageTypeAddFieldKeyText(): Promise<void> {
|
|
|
156
198
|
return;
|
|
157
199
|
}
|
|
158
200
|
|
|
159
|
-
|
|
201
|
+
if (fieldPath.type === "nested") {
|
|
202
|
+
console.info(`Added field "${fieldPath.nestedFieldId}" (Text) to group "${fieldPath.groupId}" in ${typeId}`);
|
|
203
|
+
} else {
|
|
204
|
+
console.info(`Added field "${fieldId}" (Text) to "${targetTab}" tab in ${typeId}`);
|
|
205
|
+
}
|
|
160
206
|
|
|
161
207
|
try {
|
|
162
208
|
await buildTypes({ output: types });
|
|
@@ -167,5 +213,12 @@ export async function pageTypeAddFieldKeyText(): Promise<void> {
|
|
|
167
213
|
|
|
168
214
|
console.info();
|
|
169
215
|
console.info("Next: Add more fields with `prismic page-type add-field`");
|
|
170
|
-
|
|
216
|
+
|
|
217
|
+
const frameworkInfo = await detectFrameworkInfo();
|
|
218
|
+
if (frameworkInfo?.framework) {
|
|
219
|
+
const docsPath = getDocsPath(frameworkInfo.framework);
|
|
220
|
+
console.info(
|
|
221
|
+
` Run \`prismic docs ${docsPath}#write-page-components\` to learn how to implement a page file`,
|
|
222
|
+
);
|
|
223
|
+
}
|
|
171
224
|
}
|
|
@@ -6,6 +6,8 @@ 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";
|
|
10
|
+
import { type Framework, detectFrameworkInfo } from "./lib/framework";
|
|
9
11
|
import { stringify } from "./lib/json";
|
|
10
12
|
import { humanReadable } from "./lib/string";
|
|
11
13
|
|
|
@@ -46,6 +48,17 @@ const CustomTypeSchema = v.object({
|
|
|
46
48
|
json: v.record(v.string(), v.record(v.string(), v.unknown())),
|
|
47
49
|
});
|
|
48
50
|
|
|
51
|
+
function getDocsPath(framework: Framework): string {
|
|
52
|
+
switch (framework) {
|
|
53
|
+
case "next":
|
|
54
|
+
return "nextjs/with-cli";
|
|
55
|
+
case "nuxt":
|
|
56
|
+
return "nuxt/with-cli";
|
|
57
|
+
case "sveltekit":
|
|
58
|
+
return "sveltekit/with-cli";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
49
62
|
export async function pageTypeAddFieldLink(): Promise<void> {
|
|
50
63
|
const {
|
|
51
64
|
values: {
|
|
@@ -95,6 +108,15 @@ export async function pageTypeAddFieldLink(): Promise<void> {
|
|
|
95
108
|
return;
|
|
96
109
|
}
|
|
97
110
|
|
|
111
|
+
// Parse and validate field path
|
|
112
|
+
const fieldPath = parseFieldPath(fieldId);
|
|
113
|
+
const pathValidation = validateNestedFieldPath(fieldPath);
|
|
114
|
+
if (!pathValidation.ok) {
|
|
115
|
+
console.error(pathValidation.error);
|
|
116
|
+
process.exitCode = 1;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
98
120
|
// Find the page type file
|
|
99
121
|
const projectRoot = await findUpward("package.json");
|
|
100
122
|
if (!projectRoot) {
|
|
@@ -141,20 +163,11 @@ export async function pageTypeAddFieldLink(): Promise<void> {
|
|
|
141
163
|
model.json[targetTab] = {};
|
|
142
164
|
}
|
|
143
165
|
|
|
144
|
-
// Check if field already exists in any tab
|
|
145
|
-
for (const [tabName, tabFields] of Object.entries(model.json)) {
|
|
146
|
-
if (tabFields[fieldId]) {
|
|
147
|
-
console.error(`Field "${fieldId}" already exists in tab "${tabName}"`);
|
|
148
|
-
process.exitCode = 1;
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
166
|
// Build field definition
|
|
154
167
|
const fieldDefinition: Link = {
|
|
155
168
|
type: "Link",
|
|
156
169
|
config: {
|
|
157
|
-
label: label ?? humanReadable(fieldId),
|
|
170
|
+
label: label ?? humanReadable(fieldPath.type === "nested" ? fieldPath.nestedFieldId : fieldId),
|
|
158
171
|
...(placeholder && { placeholder }),
|
|
159
172
|
...(variation && variation.length > 0 && { variants: variation }),
|
|
160
173
|
...(allowText && { allowText: true }),
|
|
@@ -164,7 +177,36 @@ export async function pageTypeAddFieldLink(): Promise<void> {
|
|
|
164
177
|
};
|
|
165
178
|
|
|
166
179
|
// Add field to model
|
|
167
|
-
|
|
180
|
+
if (fieldPath.type === "nested") {
|
|
181
|
+
const groupResult = findGroupInTab(model.json[targetTab], fieldPath.groupId, targetTab);
|
|
182
|
+
if (!groupResult.ok) {
|
|
183
|
+
console.error(groupResult.error);
|
|
184
|
+
process.exitCode = 1;
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (groupResult.group.config.fields[fieldPath.nestedFieldId]) {
|
|
188
|
+
console.error(`Field "${fieldPath.nestedFieldId}" already exists in group "${fieldPath.groupId}"`);
|
|
189
|
+
process.exitCode = 1;
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
groupResult.group.config.fields[fieldPath.nestedFieldId] = fieldDefinition;
|
|
193
|
+
} else {
|
|
194
|
+
for (const [tabName, tabFields] of Object.entries(model.json)) {
|
|
195
|
+
if (tabFields[fieldId]) {
|
|
196
|
+
console.error(`Field "${fieldId}" already exists in tab "${tabName}"`);
|
|
197
|
+
process.exitCode = 1;
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
for (const [groupFieldId, groupField] of Object.entries(tabFields)) {
|
|
201
|
+
if (isGroupField(groupField) && groupField.config.fields[fieldId]) {
|
|
202
|
+
console.error(`Field "${fieldId}" already exists in group "${groupFieldId}" in tab "${tabName}"`);
|
|
203
|
+
process.exitCode = 1;
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
model.json[targetTab][fieldId] = fieldDefinition;
|
|
209
|
+
}
|
|
168
210
|
|
|
169
211
|
// Write updated model
|
|
170
212
|
try {
|
|
@@ -179,7 +221,11 @@ export async function pageTypeAddFieldLink(): Promise<void> {
|
|
|
179
221
|
return;
|
|
180
222
|
}
|
|
181
223
|
|
|
182
|
-
|
|
224
|
+
if (fieldPath.type === "nested") {
|
|
225
|
+
console.info(`Added field "${fieldPath.nestedFieldId}" (Link) to group "${fieldPath.groupId}" in ${typeId}`);
|
|
226
|
+
} else {
|
|
227
|
+
console.info(`Added field "${fieldId}" (Link) to "${targetTab}" tab in ${typeId}`);
|
|
228
|
+
}
|
|
183
229
|
|
|
184
230
|
try {
|
|
185
231
|
await buildTypes({ output: types });
|
|
@@ -190,5 +236,12 @@ export async function pageTypeAddFieldLink(): Promise<void> {
|
|
|
190
236
|
|
|
191
237
|
console.info();
|
|
192
238
|
console.info("Next: Add more fields with `prismic page-type add-field`");
|
|
193
|
-
|
|
239
|
+
|
|
240
|
+
const frameworkInfo = await detectFrameworkInfo();
|
|
241
|
+
if (frameworkInfo?.framework) {
|
|
242
|
+
const docsPath = getDocsPath(frameworkInfo.framework);
|
|
243
|
+
console.info(
|
|
244
|
+
` Run \`prismic docs ${docsPath}#write-page-components\` to learn how to implement a page file`,
|
|
245
|
+
);
|
|
246
|
+
}
|
|
194
247
|
}
|