@angeloashmore/prismic-cli-poc 0.0.0-canary.2ff9563
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/LICENSE +202 -0
- package/README.md +98 -0
- package/dist/index.mjs +1996 -0
- package/package.json +52 -0
- package/src/custom-type-add-field-boolean.ts +171 -0
- package/src/custom-type-add-field-color.ts +158 -0
- package/src/custom-type-add-field-date.ts +161 -0
- package/src/custom-type-add-field-embed.ts +158 -0
- package/src/custom-type-add-field-geo-point.ts +155 -0
- package/src/custom-type-add-field-image.ts +158 -0
- package/src/custom-type-add-field-key-text.ts +158 -0
- package/src/custom-type-add-field-link.ts +180 -0
- package/src/custom-type-add-field-number.ts +190 -0
- package/src/custom-type-add-field-rich-text.ts +181 -0
- package/src/custom-type-add-field-select.ts +164 -0
- package/src/custom-type-add-field-timestamp.ts +161 -0
- package/src/custom-type-add-field-uid.ts +158 -0
- package/src/custom-type-add-field.ts +111 -0
- package/src/custom-type-connect-slice.ts +221 -0
- package/src/custom-type-create.ts +92 -0
- package/src/custom-type-disconnect-slice.ts +179 -0
- package/src/custom-type-list.ts +110 -0
- package/src/custom-type-remove-field.ts +161 -0
- package/src/custom-type-remove.ts +126 -0
- package/src/custom-type-set-name.ts +128 -0
- package/src/custom-type-view.ts +118 -0
- package/src/custom-type.ts +85 -0
- package/src/index.ts +100 -0
- package/src/init.ts +62 -0
- package/src/lib/auth.ts +60 -0
- package/src/lib/config.ts +111 -0
- package/src/lib/file.ts +49 -0
- package/src/lib/json.ts +3 -0
- package/src/lib/request.ts +116 -0
- package/src/lib/slice.ts +112 -0
- package/src/lib/url.ts +25 -0
- package/src/locale-add.ts +116 -0
- package/src/locale-list.ts +107 -0
- package/src/locale-remove.ts +88 -0
- package/src/locale-set-default.ts +131 -0
- package/src/locale.ts +60 -0
- package/src/login.ts +143 -0
- package/src/logout.ts +36 -0
- package/src/page-type-add-field-boolean.ts +171 -0
- package/src/page-type-add-field-color.ts +158 -0
- package/src/page-type-add-field-date.ts +161 -0
- package/src/page-type-add-field-embed.ts +158 -0
- package/src/page-type-add-field-geo-point.ts +155 -0
- package/src/page-type-add-field-image.ts +158 -0
- package/src/page-type-add-field-key-text.ts +158 -0
- package/src/page-type-add-field-link.ts +180 -0
- package/src/page-type-add-field-number.ts +190 -0
- package/src/page-type-add-field-rich-text.ts +181 -0
- package/src/page-type-add-field-select.ts +164 -0
- package/src/page-type-add-field-timestamp.ts +161 -0
- package/src/page-type-add-field-uid.ts +158 -0
- package/src/page-type-add-field.ts +111 -0
- package/src/page-type-connect-slice.ts +221 -0
- package/src/page-type-create.ts +93 -0
- package/src/page-type-disconnect-slice.ts +179 -0
- package/src/page-type-list.ts +109 -0
- package/src/page-type-remove-field.ts +161 -0
- package/src/page-type-remove.ts +126 -0
- package/src/page-type-set-name.ts +128 -0
- package/src/page-type-set-repeatable.ts +137 -0
- package/src/page-type-view.ts +118 -0
- package/src/page-type.ts +90 -0
- package/src/preview-add.ts +126 -0
- package/src/preview-list.ts +106 -0
- package/src/preview-remove.ts +109 -0
- package/src/preview-set-name.ts +137 -0
- package/src/preview.ts +60 -0
- package/src/repo-create.ts +136 -0
- package/src/repo-list.ts +100 -0
- package/src/repo-set-name.ts +102 -0
- package/src/repo-view.ts +113 -0
- package/src/repo.ts +60 -0
- package/src/slice-add-field-boolean.ts +150 -0
- package/src/slice-add-field-color.ts +137 -0
- package/src/slice-add-field-date.ts +137 -0
- package/src/slice-add-field-embed.ts +137 -0
- package/src/slice-add-field-geo-point.ts +134 -0
- package/src/slice-add-field-image.ts +134 -0
- package/src/slice-add-field-key-text.ts +137 -0
- package/src/slice-add-field-link.ts +155 -0
- package/src/slice-add-field-number.ts +137 -0
- package/src/slice-add-field-rich-text.ts +160 -0
- package/src/slice-add-field-select.ts +143 -0
- package/src/slice-add-field-timestamp.ts +137 -0
- package/src/slice-add-field.ts +106 -0
- package/src/slice-add-variation.ts +137 -0
- package/src/slice-create.ts +129 -0
- package/src/slice-list-variations.ts +67 -0
- package/src/slice-list.ts +88 -0
- package/src/slice-remove-field.ts +117 -0
- package/src/slice-remove-variation.ts +108 -0
- package/src/slice-remove.ts +81 -0
- package/src/slice-rename.ts +112 -0
- package/src/slice-view.ts +77 -0
- package/src/slice.ts +90 -0
- package/src/sync.ts +309 -0
- package/src/token-create.ts +185 -0
- package/src/token-delete.ts +161 -0
- package/src/token-list.ts +212 -0
- package/src/token-set-name.ts +165 -0
- package/src/token.ts +60 -0
- package/src/webhook-add-header.ts +118 -0
- package/src/webhook-create.ts +152 -0
- package/src/webhook-disable.ts +109 -0
- package/src/webhook-enable.ts +132 -0
- package/src/webhook-list.ts +93 -0
- package/src/webhook-remove-header.ts +117 -0
- package/src/webhook-remove.ts +106 -0
- package/src/webhook-set-triggers.ts +148 -0
- package/src/webhook-status.ts +90 -0
- package/src/webhook-test.ts +106 -0
- package/src/webhook-view.ts +147 -0
- package/src/webhook.ts +95 -0
- package/src/whoami.ts +62 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import type { CustomType, UID } 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 { findUpward } from "./lib/file";
|
|
8
|
+
import { stringify } from "./lib/json";
|
|
9
|
+
|
|
10
|
+
const HELP = `
|
|
11
|
+
Add a UID (unique identifier) field to an existing custom type.
|
|
12
|
+
|
|
13
|
+
USAGE
|
|
14
|
+
prismic custom-type add-field uid <type-id> <field-id> [flags]
|
|
15
|
+
|
|
16
|
+
ARGUMENTS
|
|
17
|
+
type-id Custom type identifier (required)
|
|
18
|
+
field-id Field identifier (required)
|
|
19
|
+
|
|
20
|
+
FLAGS
|
|
21
|
+
-t, --tab string Target tab (default: first existing tab, or "Main")
|
|
22
|
+
-l, --label string Display label for the field
|
|
23
|
+
-p, --placeholder string Placeholder text
|
|
24
|
+
-h, --help Show help for command
|
|
25
|
+
|
|
26
|
+
EXAMPLES
|
|
27
|
+
prismic custom-type add-field uid page uid
|
|
28
|
+
prismic custom-type add-field uid article slug --label "URL Slug"
|
|
29
|
+
prismic custom-type add-field uid product sku --placeholder "Enter unique SKU"
|
|
30
|
+
`.trim();
|
|
31
|
+
|
|
32
|
+
const CustomTypeSchema = v.object({
|
|
33
|
+
id: v.string(),
|
|
34
|
+
label: v.string(),
|
|
35
|
+
repeatable: v.boolean(),
|
|
36
|
+
status: v.boolean(),
|
|
37
|
+
format: v.string(),
|
|
38
|
+
json: v.record(v.string(), v.record(v.string(), v.unknown())),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export async function customTypeAddFieldUid(): Promise<void> {
|
|
42
|
+
const {
|
|
43
|
+
values: { help, tab, label, placeholder },
|
|
44
|
+
positionals: [typeId, fieldId],
|
|
45
|
+
} = parseArgs({
|
|
46
|
+
args: process.argv.slice(5), // skip: node, script, "custom-type", "add-field", "uid"
|
|
47
|
+
options: {
|
|
48
|
+
tab: { type: "string", short: "t" },
|
|
49
|
+
label: { type: "string", short: "l" },
|
|
50
|
+
placeholder: { type: "string", short: "p" },
|
|
51
|
+
help: { type: "boolean", short: "h" },
|
|
52
|
+
},
|
|
53
|
+
allowPositionals: true,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (help) {
|
|
57
|
+
console.info(HELP);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!typeId) {
|
|
62
|
+
console.error("Missing required argument: type-id\n");
|
|
63
|
+
console.error("Usage: prismic custom-type add-field uid <type-id> <field-id>");
|
|
64
|
+
process.exitCode = 1;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!fieldId) {
|
|
69
|
+
console.error("Missing required argument: field-id\n");
|
|
70
|
+
console.error("Usage: prismic custom-type add-field uid <type-id> <field-id>");
|
|
71
|
+
process.exitCode = 1;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Find the custom type file
|
|
76
|
+
const projectRoot = await findUpward("package.json");
|
|
77
|
+
if (!projectRoot) {
|
|
78
|
+
console.error("Could not find project root (no package.json found)");
|
|
79
|
+
process.exitCode = 1;
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const modelPath = new URL(`customtypes/${typeId}/index.json`, projectRoot);
|
|
84
|
+
|
|
85
|
+
// Read and parse the model
|
|
86
|
+
let model: CustomType;
|
|
87
|
+
try {
|
|
88
|
+
const contents = await readFile(modelPath, "utf8");
|
|
89
|
+
const result = v.safeParse(CustomTypeSchema, JSON.parse(contents));
|
|
90
|
+
if (!result.success) {
|
|
91
|
+
console.error(`Invalid custom type model: ${modelPath.href}`);
|
|
92
|
+
process.exitCode = 1;
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
model = result.output as CustomType;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
98
|
+
console.error(`Custom type not found: ${typeId}\n`);
|
|
99
|
+
console.error(`Create it first with: prismic custom-type create ${typeId}`);
|
|
100
|
+
process.exitCode = 1;
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (error instanceof Error) {
|
|
104
|
+
console.error(`Failed to read custom type: ${error.message}`);
|
|
105
|
+
} else {
|
|
106
|
+
console.error("Failed to read custom type");
|
|
107
|
+
}
|
|
108
|
+
process.exitCode = 1;
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Determine target tab
|
|
113
|
+
const existingTabs = Object.keys(model.json);
|
|
114
|
+
const targetTab = tab ?? existingTabs[0] ?? "Main";
|
|
115
|
+
|
|
116
|
+
// Initialize tab if it doesn't exist
|
|
117
|
+
if (!model.json[targetTab]) {
|
|
118
|
+
model.json[targetTab] = {};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Check if field already exists in any tab
|
|
122
|
+
for (const [tabName, tabFields] of Object.entries(model.json)) {
|
|
123
|
+
if (tabFields[fieldId]) {
|
|
124
|
+
console.error(`Field "${fieldId}" already exists in tab "${tabName}"`);
|
|
125
|
+
process.exitCode = 1;
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Build field definition
|
|
131
|
+
const fieldDefinition: UID = {
|
|
132
|
+
type: "UID",
|
|
133
|
+
config: {
|
|
134
|
+
...(label && { label }),
|
|
135
|
+
...(placeholder && { placeholder }),
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// Add field to model
|
|
140
|
+
model.json[targetTab][fieldId] = fieldDefinition;
|
|
141
|
+
|
|
142
|
+
// Write updated model
|
|
143
|
+
try {
|
|
144
|
+
await writeFile(modelPath, stringify(model));
|
|
145
|
+
} catch (error) {
|
|
146
|
+
if (error instanceof Error) {
|
|
147
|
+
console.error(`Failed to update custom type: ${error.message}`);
|
|
148
|
+
} else {
|
|
149
|
+
console.error("Failed to update custom type");
|
|
150
|
+
}
|
|
151
|
+
process.exitCode = 1;
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
console.info(
|
|
156
|
+
`Added field "${fieldId}" (UID) to "${targetTab}" tab in ${typeId}`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
|
|
3
|
+
import { customTypeAddFieldBoolean } from "./custom-type-add-field-boolean";
|
|
4
|
+
import { customTypeAddFieldColor } from "./custom-type-add-field-color";
|
|
5
|
+
import { customTypeAddFieldDate } from "./custom-type-add-field-date";
|
|
6
|
+
import { customTypeAddFieldEmbed } from "./custom-type-add-field-embed";
|
|
7
|
+
import { customTypeAddFieldGeoPoint } from "./custom-type-add-field-geo-point";
|
|
8
|
+
import { customTypeAddFieldImage } from "./custom-type-add-field-image";
|
|
9
|
+
import { customTypeAddFieldKeyText } from "./custom-type-add-field-key-text";
|
|
10
|
+
import { customTypeAddFieldLink } from "./custom-type-add-field-link";
|
|
11
|
+
import { customTypeAddFieldNumber } from "./custom-type-add-field-number";
|
|
12
|
+
import { customTypeAddFieldRichText } from "./custom-type-add-field-rich-text";
|
|
13
|
+
import { customTypeAddFieldSelect } from "./custom-type-add-field-select";
|
|
14
|
+
import { customTypeAddFieldTimestamp } from "./custom-type-add-field-timestamp";
|
|
15
|
+
import { customTypeAddFieldUid } from "./custom-type-add-field-uid";
|
|
16
|
+
|
|
17
|
+
const HELP = `
|
|
18
|
+
Add a field to an existing custom type.
|
|
19
|
+
|
|
20
|
+
USAGE
|
|
21
|
+
prismic custom-type add-field <field-type> <type-id> <field-id> [flags]
|
|
22
|
+
|
|
23
|
+
FIELD TYPES
|
|
24
|
+
boolean Boolean toggle
|
|
25
|
+
color Color picker
|
|
26
|
+
date Date picker
|
|
27
|
+
embed Embed (oEmbed)
|
|
28
|
+
geo-point Geographic coordinates
|
|
29
|
+
image Image
|
|
30
|
+
key-text Single-line text
|
|
31
|
+
link Any link type
|
|
32
|
+
number Number
|
|
33
|
+
rich-text Rich text editor
|
|
34
|
+
select Dropdown select
|
|
35
|
+
timestamp Date and time
|
|
36
|
+
uid Unique identifier
|
|
37
|
+
|
|
38
|
+
FLAGS
|
|
39
|
+
-h, --help Show help for command
|
|
40
|
+
|
|
41
|
+
LEARN MORE
|
|
42
|
+
Use \`prismic custom-type add-field <field-type> --help\` for more information.
|
|
43
|
+
|
|
44
|
+
EXAMPLES
|
|
45
|
+
prismic custom-type add-field key-text homepage meta_title --tab "SEO"
|
|
46
|
+
prismic custom-type add-field link homepage button --allow-text
|
|
47
|
+
prismic custom-type add-field rich-text homepage body --multi "paragraph,heading2,strong,em"
|
|
48
|
+
prismic custom-type add-field select homepage layout --option "full" --option "sidebar"
|
|
49
|
+
`.trim();
|
|
50
|
+
|
|
51
|
+
export async function customTypeAddField(): Promise<void> {
|
|
52
|
+
const {
|
|
53
|
+
positionals: [fieldType],
|
|
54
|
+
} = parseArgs({
|
|
55
|
+
args: process.argv.slice(4), // skip: node, script, "custom-type", "add-field"
|
|
56
|
+
options: {
|
|
57
|
+
help: { type: "boolean", short: "h" },
|
|
58
|
+
},
|
|
59
|
+
allowPositionals: true,
|
|
60
|
+
strict: false,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
switch (fieldType) {
|
|
64
|
+
case "boolean":
|
|
65
|
+
await customTypeAddFieldBoolean();
|
|
66
|
+
break;
|
|
67
|
+
case "color":
|
|
68
|
+
await customTypeAddFieldColor();
|
|
69
|
+
break;
|
|
70
|
+
case "date":
|
|
71
|
+
await customTypeAddFieldDate();
|
|
72
|
+
break;
|
|
73
|
+
case "embed":
|
|
74
|
+
await customTypeAddFieldEmbed();
|
|
75
|
+
break;
|
|
76
|
+
case "geo-point":
|
|
77
|
+
await customTypeAddFieldGeoPoint();
|
|
78
|
+
break;
|
|
79
|
+
case "image":
|
|
80
|
+
await customTypeAddFieldImage();
|
|
81
|
+
break;
|
|
82
|
+
case "key-text":
|
|
83
|
+
await customTypeAddFieldKeyText();
|
|
84
|
+
break;
|
|
85
|
+
case "link":
|
|
86
|
+
await customTypeAddFieldLink();
|
|
87
|
+
break;
|
|
88
|
+
case "number":
|
|
89
|
+
await customTypeAddFieldNumber();
|
|
90
|
+
break;
|
|
91
|
+
case "rich-text":
|
|
92
|
+
await customTypeAddFieldRichText();
|
|
93
|
+
break;
|
|
94
|
+
case "select":
|
|
95
|
+
await customTypeAddFieldSelect();
|
|
96
|
+
break;
|
|
97
|
+
case "timestamp":
|
|
98
|
+
await customTypeAddFieldTimestamp();
|
|
99
|
+
break;
|
|
100
|
+
case "uid":
|
|
101
|
+
await customTypeAddFieldUid();
|
|
102
|
+
break;
|
|
103
|
+
default: {
|
|
104
|
+
if (fieldType) {
|
|
105
|
+
console.error(`Unknown field type: ${fieldType}\n`);
|
|
106
|
+
process.exitCode = 1;
|
|
107
|
+
}
|
|
108
|
+
console.info(HELP);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CustomType,
|
|
3
|
+
DynamicSlices,
|
|
4
|
+
SharedSliceRef,
|
|
5
|
+
} from "@prismicio/types-internal/lib/customtypes";
|
|
6
|
+
|
|
7
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
8
|
+
import { parseArgs } from "node:util";
|
|
9
|
+
import * as v from "valibot";
|
|
10
|
+
|
|
11
|
+
import { findUpward } from "./lib/file";
|
|
12
|
+
import { stringify } from "./lib/json";
|
|
13
|
+
import { findSliceModel } from "./lib/slice";
|
|
14
|
+
|
|
15
|
+
const HELP = `
|
|
16
|
+
Connect a shared slice to a custom type's slice zone.
|
|
17
|
+
|
|
18
|
+
USAGE
|
|
19
|
+
prismic custom-type connect-slice <type-id> <slice-id> [flags]
|
|
20
|
+
|
|
21
|
+
ARGUMENTS
|
|
22
|
+
type-id Custom type identifier (required)
|
|
23
|
+
slice-id Slice identifier (required)
|
|
24
|
+
|
|
25
|
+
FLAGS
|
|
26
|
+
-z, --slice-zone string Target slice zone field ID (default: "slices")
|
|
27
|
+
-h, --help Show help for command
|
|
28
|
+
|
|
29
|
+
EXAMPLES
|
|
30
|
+
prismic custom-type connect-slice homepage CallToAction
|
|
31
|
+
prismic custom-type connect-slice homepage CallToAction --slice-zone slices
|
|
32
|
+
prismic custom-type connect-slice article HeroSection -z body
|
|
33
|
+
`.trim();
|
|
34
|
+
|
|
35
|
+
const CustomTypeSchema = v.object({
|
|
36
|
+
id: v.string(),
|
|
37
|
+
label: v.string(),
|
|
38
|
+
repeatable: v.boolean(),
|
|
39
|
+
status: v.boolean(),
|
|
40
|
+
format: v.string(),
|
|
41
|
+
json: v.record(v.string(), v.record(v.string(), v.unknown())),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export async function customTypeConnectSlice(): Promise<void> {
|
|
45
|
+
const {
|
|
46
|
+
values: { help, "slice-zone": sliceZoneId },
|
|
47
|
+
positionals: [typeId, sliceId],
|
|
48
|
+
} = parseArgs({
|
|
49
|
+
args: process.argv.slice(4), // skip: node, script, "custom-type", "connect-slice"
|
|
50
|
+
options: {
|
|
51
|
+
"slice-zone": { type: "string", short: "z" },
|
|
52
|
+
help: { type: "boolean", short: "h" },
|
|
53
|
+
},
|
|
54
|
+
allowPositionals: true,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (help) {
|
|
58
|
+
console.info(HELP);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!typeId) {
|
|
63
|
+
console.error("Missing required argument: type-id\n");
|
|
64
|
+
console.error(
|
|
65
|
+
"Usage: prismic custom-type connect-slice <type-id> <slice-id>",
|
|
66
|
+
);
|
|
67
|
+
process.exitCode = 1;
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!sliceId) {
|
|
72
|
+
console.error("Missing required argument: slice-id\n");
|
|
73
|
+
console.error(
|
|
74
|
+
"Usage: prismic custom-type connect-slice <type-id> <slice-id>",
|
|
75
|
+
);
|
|
76
|
+
process.exitCode = 1;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Verify the slice exists
|
|
81
|
+
const sliceResult = await findSliceModel(sliceId);
|
|
82
|
+
if (!sliceResult.ok) {
|
|
83
|
+
console.error(sliceResult.error);
|
|
84
|
+
process.exitCode = 1;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Find the custom type file
|
|
89
|
+
const projectRoot = await findUpward("package.json");
|
|
90
|
+
if (!projectRoot) {
|
|
91
|
+
console.error("Could not find project root (no package.json found)");
|
|
92
|
+
process.exitCode = 1;
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const modelPath = new URL(`customtypes/${typeId}/index.json`, projectRoot);
|
|
97
|
+
|
|
98
|
+
// Read and parse the model
|
|
99
|
+
let model: CustomType;
|
|
100
|
+
try {
|
|
101
|
+
const contents = await readFile(modelPath, "utf8");
|
|
102
|
+
const result = v.safeParse(CustomTypeSchema, JSON.parse(contents));
|
|
103
|
+
if (!result.success) {
|
|
104
|
+
console.error(`Invalid custom type model: ${modelPath.href}`);
|
|
105
|
+
process.exitCode = 1;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
model = result.output as CustomType;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
if (
|
|
111
|
+
error instanceof Error &&
|
|
112
|
+
"code" in error &&
|
|
113
|
+
error.code === "ENOENT"
|
|
114
|
+
) {
|
|
115
|
+
console.error(`Custom type not found: ${typeId}\n`);
|
|
116
|
+
console.error(
|
|
117
|
+
`Create it first with: prismic custom-type create ${typeId}`,
|
|
118
|
+
);
|
|
119
|
+
process.exitCode = 1;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (error instanceof Error) {
|
|
123
|
+
console.error(`Failed to read custom type: ${error.message}`);
|
|
124
|
+
} else {
|
|
125
|
+
console.error("Failed to read custom type");
|
|
126
|
+
}
|
|
127
|
+
process.exitCode = 1;
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const targetSliceZoneId = sliceZoneId ?? "slices";
|
|
132
|
+
|
|
133
|
+
// Find existing slice zone or create a new one
|
|
134
|
+
let sliceZone: DynamicSlices | undefined;
|
|
135
|
+
let sliceZoneFieldId: string | undefined;
|
|
136
|
+
|
|
137
|
+
// Search all tabs for a Slices field matching the target ID
|
|
138
|
+
for (const [, tabFields] of Object.entries(model.json)) {
|
|
139
|
+
for (const [fieldId, field] of Object.entries(tabFields)) {
|
|
140
|
+
if (
|
|
141
|
+
(field as { type?: string }).type === "Slices" &&
|
|
142
|
+
fieldId === targetSliceZoneId
|
|
143
|
+
) {
|
|
144
|
+
sliceZone = field as DynamicSlices;
|
|
145
|
+
sliceZoneFieldId = fieldId;
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (sliceZone) break;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Handle slice zone not found
|
|
153
|
+
if (!sliceZone) {
|
|
154
|
+
if (sliceZoneId) {
|
|
155
|
+
// User specified a slice zone that doesn't exist
|
|
156
|
+
console.error(
|
|
157
|
+
`Slice zone "${sliceZoneId}" not found in custom type "${typeId}"`,
|
|
158
|
+
);
|
|
159
|
+
process.exitCode = 1;
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Create a new slice zone in the first tab
|
|
164
|
+
const existingTabs = Object.keys(model.json);
|
|
165
|
+
const targetTab = existingTabs[0] ?? "Main";
|
|
166
|
+
|
|
167
|
+
// Initialize tab if it doesn't exist
|
|
168
|
+
if (!model.json[targetTab]) {
|
|
169
|
+
model.json[targetTab] = {};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const newSliceZone: DynamicSlices = {
|
|
173
|
+
type: "Slices",
|
|
174
|
+
fieldset: "Slice Zone",
|
|
175
|
+
config: {
|
|
176
|
+
choices: {},
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
model.json[targetTab][targetSliceZoneId] = newSliceZone;
|
|
181
|
+
sliceZone = newSliceZone;
|
|
182
|
+
sliceZoneFieldId = targetSliceZoneId;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Ensure config and choices exist
|
|
186
|
+
if (!sliceZone.config) {
|
|
187
|
+
sliceZone.config = { choices: {} };
|
|
188
|
+
}
|
|
189
|
+
if (!sliceZone.config.choices) {
|
|
190
|
+
sliceZone.config.choices = {};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Check if slice is already connected
|
|
194
|
+
if (sliceId in sliceZone.config.choices) {
|
|
195
|
+
console.info(
|
|
196
|
+
`Slice "${sliceId}" is already connected to slice zone "${sliceZoneFieldId}" in ${typeId}`,
|
|
197
|
+
);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Add the slice reference
|
|
202
|
+
const sliceRef: SharedSliceRef = { type: "SharedSlice" };
|
|
203
|
+
sliceZone.config.choices[sliceId] = sliceRef;
|
|
204
|
+
|
|
205
|
+
// Write updated model
|
|
206
|
+
try {
|
|
207
|
+
await writeFile(modelPath, stringify(model));
|
|
208
|
+
} catch (error) {
|
|
209
|
+
if (error instanceof Error) {
|
|
210
|
+
console.error(`Failed to update custom type: ${error.message}`);
|
|
211
|
+
} else {
|
|
212
|
+
console.error("Failed to update custom type");
|
|
213
|
+
}
|
|
214
|
+
process.exitCode = 1;
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
console.info(
|
|
219
|
+
`Connected slice "${sliceId}" to slice zone "${sliceZoneFieldId}" in ${typeId}`,
|
|
220
|
+
);
|
|
221
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { CustomType } from "@prismicio/types-internal/lib/customtypes";
|
|
2
|
+
|
|
3
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
4
|
+
import { parseArgs } from "node:util";
|
|
5
|
+
|
|
6
|
+
import { findUpward } from "./lib/file";
|
|
7
|
+
import { stringify } from "./lib/json";
|
|
8
|
+
|
|
9
|
+
const HELP = `
|
|
10
|
+
Create a new custom type in a Prismic repository.
|
|
11
|
+
|
|
12
|
+
USAGE
|
|
13
|
+
prismic custom-type create <id> [flags]
|
|
14
|
+
|
|
15
|
+
ARGUMENTS
|
|
16
|
+
id Custom type identifier (required)
|
|
17
|
+
|
|
18
|
+
FLAGS
|
|
19
|
+
-n, --name string Display name for the custom type
|
|
20
|
+
--single Create as a singleton (non-repeatable) type
|
|
21
|
+
-h, --help Show help for command
|
|
22
|
+
|
|
23
|
+
LEARN MORE
|
|
24
|
+
Use \`prismic custom-type <command> --help\` for more information about a command.
|
|
25
|
+
`.trim();
|
|
26
|
+
|
|
27
|
+
export async function customTypeCreate(): Promise<void> {
|
|
28
|
+
const {
|
|
29
|
+
values: { help, name, single },
|
|
30
|
+
positionals: [id],
|
|
31
|
+
} = parseArgs({
|
|
32
|
+
args: process.argv.slice(4), // skip: node, script, "custom-type", "create"
|
|
33
|
+
options: {
|
|
34
|
+
name: { type: "string", short: "n" },
|
|
35
|
+
single: { type: "boolean" },
|
|
36
|
+
help: { type: "boolean", short: "h" },
|
|
37
|
+
},
|
|
38
|
+
allowPositionals: true,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (help) {
|
|
42
|
+
console.info(HELP);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!id) {
|
|
47
|
+
console.error("Missing required argument: id");
|
|
48
|
+
process.exitCode = 1;
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const model = {
|
|
53
|
+
id,
|
|
54
|
+
label: name ?? pascalCase(id),
|
|
55
|
+
repeatable: !single,
|
|
56
|
+
status: true,
|
|
57
|
+
format: "custom",
|
|
58
|
+
json: {
|
|
59
|
+
Main: {},
|
|
60
|
+
},
|
|
61
|
+
} satisfies CustomType;
|
|
62
|
+
|
|
63
|
+
const projectRoot = await findUpward("package.json");
|
|
64
|
+
if (!projectRoot) {
|
|
65
|
+
console.error("Could not find project root (no package.json found)");
|
|
66
|
+
process.exitCode = 1;
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const customTypesDirectory = new URL("customtypes/", projectRoot);
|
|
71
|
+
const typeDirectory = new URL(id + "/", customTypesDirectory);
|
|
72
|
+
const modelPath = new URL("index.json", typeDirectory);
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
await mkdir(new URL(".", modelPath), { recursive: true });
|
|
76
|
+
await writeFile(modelPath, stringify(model));
|
|
77
|
+
} catch (error) {
|
|
78
|
+
if (error instanceof Error) {
|
|
79
|
+
console.error(`Failed to create custom type: ${error.message}`);
|
|
80
|
+
} else {
|
|
81
|
+
console.error(`Failed to create custom type`);
|
|
82
|
+
}
|
|
83
|
+
process.exitCode = 1;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.info(`Created custom type at ${modelPath.href}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function pascalCase(input: string): string {
|
|
91
|
+
return input.toLowerCase().replace(/(^|[-_\s]+)(.)?/g, (_, __, c) => c?.toUpperCase() ?? "");
|
|
92
|
+
}
|