@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.
Files changed (119) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +98 -0
  3. package/dist/index.mjs +1996 -0
  4. package/package.json +52 -0
  5. package/src/custom-type-add-field-boolean.ts +171 -0
  6. package/src/custom-type-add-field-color.ts +158 -0
  7. package/src/custom-type-add-field-date.ts +161 -0
  8. package/src/custom-type-add-field-embed.ts +158 -0
  9. package/src/custom-type-add-field-geo-point.ts +155 -0
  10. package/src/custom-type-add-field-image.ts +158 -0
  11. package/src/custom-type-add-field-key-text.ts +158 -0
  12. package/src/custom-type-add-field-link.ts +180 -0
  13. package/src/custom-type-add-field-number.ts +190 -0
  14. package/src/custom-type-add-field-rich-text.ts +181 -0
  15. package/src/custom-type-add-field-select.ts +164 -0
  16. package/src/custom-type-add-field-timestamp.ts +161 -0
  17. package/src/custom-type-add-field-uid.ts +158 -0
  18. package/src/custom-type-add-field.ts +111 -0
  19. package/src/custom-type-connect-slice.ts +221 -0
  20. package/src/custom-type-create.ts +92 -0
  21. package/src/custom-type-disconnect-slice.ts +179 -0
  22. package/src/custom-type-list.ts +110 -0
  23. package/src/custom-type-remove-field.ts +161 -0
  24. package/src/custom-type-remove.ts +126 -0
  25. package/src/custom-type-set-name.ts +128 -0
  26. package/src/custom-type-view.ts +118 -0
  27. package/src/custom-type.ts +85 -0
  28. package/src/index.ts +100 -0
  29. package/src/init.ts +62 -0
  30. package/src/lib/auth.ts +60 -0
  31. package/src/lib/config.ts +111 -0
  32. package/src/lib/file.ts +49 -0
  33. package/src/lib/json.ts +3 -0
  34. package/src/lib/request.ts +116 -0
  35. package/src/lib/slice.ts +112 -0
  36. package/src/lib/url.ts +25 -0
  37. package/src/locale-add.ts +116 -0
  38. package/src/locale-list.ts +107 -0
  39. package/src/locale-remove.ts +88 -0
  40. package/src/locale-set-default.ts +131 -0
  41. package/src/locale.ts +60 -0
  42. package/src/login.ts +143 -0
  43. package/src/logout.ts +36 -0
  44. package/src/page-type-add-field-boolean.ts +171 -0
  45. package/src/page-type-add-field-color.ts +158 -0
  46. package/src/page-type-add-field-date.ts +161 -0
  47. package/src/page-type-add-field-embed.ts +158 -0
  48. package/src/page-type-add-field-geo-point.ts +155 -0
  49. package/src/page-type-add-field-image.ts +158 -0
  50. package/src/page-type-add-field-key-text.ts +158 -0
  51. package/src/page-type-add-field-link.ts +180 -0
  52. package/src/page-type-add-field-number.ts +190 -0
  53. package/src/page-type-add-field-rich-text.ts +181 -0
  54. package/src/page-type-add-field-select.ts +164 -0
  55. package/src/page-type-add-field-timestamp.ts +161 -0
  56. package/src/page-type-add-field-uid.ts +158 -0
  57. package/src/page-type-add-field.ts +111 -0
  58. package/src/page-type-connect-slice.ts +221 -0
  59. package/src/page-type-create.ts +93 -0
  60. package/src/page-type-disconnect-slice.ts +179 -0
  61. package/src/page-type-list.ts +109 -0
  62. package/src/page-type-remove-field.ts +161 -0
  63. package/src/page-type-remove.ts +126 -0
  64. package/src/page-type-set-name.ts +128 -0
  65. package/src/page-type-set-repeatable.ts +137 -0
  66. package/src/page-type-view.ts +118 -0
  67. package/src/page-type.ts +90 -0
  68. package/src/preview-add.ts +126 -0
  69. package/src/preview-list.ts +106 -0
  70. package/src/preview-remove.ts +109 -0
  71. package/src/preview-set-name.ts +137 -0
  72. package/src/preview.ts +60 -0
  73. package/src/repo-create.ts +136 -0
  74. package/src/repo-list.ts +100 -0
  75. package/src/repo-set-name.ts +102 -0
  76. package/src/repo-view.ts +113 -0
  77. package/src/repo.ts +60 -0
  78. package/src/slice-add-field-boolean.ts +150 -0
  79. package/src/slice-add-field-color.ts +137 -0
  80. package/src/slice-add-field-date.ts +137 -0
  81. package/src/slice-add-field-embed.ts +137 -0
  82. package/src/slice-add-field-geo-point.ts +134 -0
  83. package/src/slice-add-field-image.ts +134 -0
  84. package/src/slice-add-field-key-text.ts +137 -0
  85. package/src/slice-add-field-link.ts +155 -0
  86. package/src/slice-add-field-number.ts +137 -0
  87. package/src/slice-add-field-rich-text.ts +160 -0
  88. package/src/slice-add-field-select.ts +143 -0
  89. package/src/slice-add-field-timestamp.ts +137 -0
  90. package/src/slice-add-field.ts +106 -0
  91. package/src/slice-add-variation.ts +137 -0
  92. package/src/slice-create.ts +129 -0
  93. package/src/slice-list-variations.ts +67 -0
  94. package/src/slice-list.ts +88 -0
  95. package/src/slice-remove-field.ts +117 -0
  96. package/src/slice-remove-variation.ts +108 -0
  97. package/src/slice-remove.ts +81 -0
  98. package/src/slice-rename.ts +112 -0
  99. package/src/slice-view.ts +77 -0
  100. package/src/slice.ts +90 -0
  101. package/src/sync.ts +309 -0
  102. package/src/token-create.ts +185 -0
  103. package/src/token-delete.ts +161 -0
  104. package/src/token-list.ts +212 -0
  105. package/src/token-set-name.ts +165 -0
  106. package/src/token.ts +60 -0
  107. package/src/webhook-add-header.ts +118 -0
  108. package/src/webhook-create.ts +152 -0
  109. package/src/webhook-disable.ts +109 -0
  110. package/src/webhook-enable.ts +132 -0
  111. package/src/webhook-list.ts +93 -0
  112. package/src/webhook-remove-header.ts +117 -0
  113. package/src/webhook-remove.ts +106 -0
  114. package/src/webhook-set-triggers.ts +148 -0
  115. package/src/webhook-status.ts +90 -0
  116. package/src/webhook-test.ts +106 -0
  117. package/src/webhook-view.ts +147 -0
  118. package/src/webhook.ts +95 -0
  119. package/src/whoami.ts +62 -0
@@ -0,0 +1,93 @@
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 page type in a Prismic repository.
11
+
12
+ USAGE
13
+ prismic page-type create <id> [flags]
14
+
15
+ ARGUMENTS
16
+ id Page type identifier (required)
17
+
18
+ FLAGS
19
+ -n, --name string Display name for the page type
20
+ --single Create as a singleton (non-repeatable) type
21
+ -h, --help Show help for command
22
+
23
+ LEARN MORE
24
+ Use \`prismic page-type <command> --help\` for more information about a command.
25
+ `.trim();
26
+
27
+ export async function pageTypeCreate(): Promise<void> {
28
+ const {
29
+ values: { help, name, single },
30
+ positionals: [id],
31
+ } = parseArgs({
32
+ args: process.argv.slice(4), // skip: node, script, "page-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: "page",
58
+ json: {
59
+ Main: {},
60
+ "SEO & Metadata": {},
61
+ },
62
+ } satisfies CustomType;
63
+
64
+ const projectRoot = await findUpward("package.json");
65
+ if (!projectRoot) {
66
+ console.error("Could not find project root (no package.json found)");
67
+ process.exitCode = 1;
68
+ return;
69
+ }
70
+
71
+ const customTypesDirectory = new URL("customtypes/", projectRoot);
72
+ const typeDirectory = new URL(id + "/", customTypesDirectory);
73
+ const modelPath = new URL("index.json", typeDirectory);
74
+
75
+ try {
76
+ await mkdir(new URL(".", modelPath), { recursive: true });
77
+ await writeFile(modelPath, stringify(model));
78
+ } catch (error) {
79
+ if (error instanceof Error) {
80
+ console.error(`Failed to create page type: ${error.message}`);
81
+ } else {
82
+ console.error(`Failed to create page type`);
83
+ }
84
+ process.exitCode = 1;
85
+ return;
86
+ }
87
+
88
+ console.info(`Created page type at ${modelPath.href}`);
89
+ }
90
+
91
+ export function pascalCase(input: string): string {
92
+ return input.toLowerCase().replace(/(^|[-_\s]+)(.)?/g, (_, __, c) => c?.toUpperCase() ?? "");
93
+ }
@@ -0,0 +1,179 @@
1
+ import type {
2
+ CustomType,
3
+ DynamicSlices,
4
+ } from "@prismicio/types-internal/lib/customtypes";
5
+
6
+ import { readFile, writeFile } from "node:fs/promises";
7
+ import { parseArgs } from "node:util";
8
+ import * as v from "valibot";
9
+
10
+ import { findUpward } from "./lib/file";
11
+ import { stringify } from "./lib/json";
12
+
13
+ const HELP = `
14
+ Disconnect a shared slice from a page type's slice zone.
15
+
16
+ USAGE
17
+ prismic page-type disconnect-slice <type-id> <slice-id> [flags]
18
+
19
+ ARGUMENTS
20
+ type-id Page type identifier (required)
21
+ slice-id Slice identifier (required)
22
+
23
+ FLAGS
24
+ -z, --slice-zone string Target slice zone field ID (default: "slices")
25
+ -h, --help Show help for command
26
+
27
+ EXAMPLES
28
+ prismic page-type disconnect-slice homepage CallToAction
29
+ prismic page-type disconnect-slice homepage CallToAction --slice-zone slices
30
+ prismic page-type disconnect-slice article HeroSection -z body
31
+ `.trim();
32
+
33
+ const CustomTypeSchema = v.object({
34
+ id: v.string(),
35
+ label: v.string(),
36
+ repeatable: v.boolean(),
37
+ status: v.boolean(),
38
+ format: v.string(),
39
+ json: v.record(v.string(), v.record(v.string(), v.unknown())),
40
+ });
41
+
42
+ export async function pageTypeDisconnectSlice(): Promise<void> {
43
+ const {
44
+ values: { help, "slice-zone": sliceZoneId },
45
+ positionals: [typeId, sliceId],
46
+ } = parseArgs({
47
+ args: process.argv.slice(4), // skip: node, script, "page-type", "disconnect-slice"
48
+ options: {
49
+ "slice-zone": { type: "string", short: "z" },
50
+ help: { type: "boolean", short: "h" },
51
+ },
52
+ allowPositionals: true,
53
+ });
54
+
55
+ if (help) {
56
+ console.info(HELP);
57
+ return;
58
+ }
59
+
60
+ if (!typeId) {
61
+ console.error("Missing required argument: type-id\n");
62
+ console.error(
63
+ "Usage: prismic page-type disconnect-slice <type-id> <slice-id>",
64
+ );
65
+ process.exitCode = 1;
66
+ return;
67
+ }
68
+
69
+ if (!sliceId) {
70
+ console.error("Missing required argument: slice-id\n");
71
+ console.error(
72
+ "Usage: prismic page-type disconnect-slice <type-id> <slice-id>",
73
+ );
74
+ process.exitCode = 1;
75
+ return;
76
+ }
77
+
78
+ // Find the page type file
79
+ const projectRoot = await findUpward("package.json");
80
+ if (!projectRoot) {
81
+ console.error("Could not find project root (no package.json found)");
82
+ process.exitCode = 1;
83
+ return;
84
+ }
85
+
86
+ const modelPath = new URL(`customtypes/${typeId}/index.json`, projectRoot);
87
+
88
+ // Read and parse the model
89
+ let model: CustomType;
90
+ try {
91
+ const contents = await readFile(modelPath, "utf8");
92
+ const result = v.safeParse(CustomTypeSchema, JSON.parse(contents));
93
+ if (!result.success) {
94
+ console.error(`Invalid page type model: ${modelPath.href}`);
95
+ process.exitCode = 1;
96
+ return;
97
+ }
98
+ model = result.output as CustomType;
99
+ } catch (error) {
100
+ if (
101
+ error instanceof Error &&
102
+ "code" in error &&
103
+ error.code === "ENOENT"
104
+ ) {
105
+ console.error(`Page type not found: ${typeId}\n`);
106
+ console.error(
107
+ `Create it first with: prismic page-type create ${typeId}`,
108
+ );
109
+ process.exitCode = 1;
110
+ return;
111
+ }
112
+ if (error instanceof Error) {
113
+ console.error(`Failed to read page type: ${error.message}`);
114
+ } else {
115
+ console.error("Failed to read page type");
116
+ }
117
+ process.exitCode = 1;
118
+ return;
119
+ }
120
+
121
+ const targetSliceZoneId = sliceZoneId ?? "slices";
122
+
123
+ // Find existing slice zone
124
+ let sliceZone: DynamicSlices | undefined;
125
+ let sliceZoneFieldId: string | undefined;
126
+
127
+ // Search all tabs for a Slices field matching the target ID
128
+ for (const [, tabFields] of Object.entries(model.json)) {
129
+ for (const [fieldId, field] of Object.entries(tabFields)) {
130
+ if (
131
+ (field as { type?: string }).type === "Slices" &&
132
+ fieldId === targetSliceZoneId
133
+ ) {
134
+ sliceZone = field as DynamicSlices;
135
+ sliceZoneFieldId = fieldId;
136
+ break;
137
+ }
138
+ }
139
+ if (sliceZone) break;
140
+ }
141
+
142
+ // Slice zone must exist for disconnect
143
+ if (!sliceZone) {
144
+ console.error(
145
+ `Slice zone "${targetSliceZoneId}" not found in page type "${typeId}"`,
146
+ );
147
+ process.exitCode = 1;
148
+ return;
149
+ }
150
+
151
+ // Check if slice is connected
152
+ if (!sliceZone.config?.choices || !(sliceId in sliceZone.config.choices)) {
153
+ console.error(
154
+ `Slice "${sliceId}" is not connected to slice zone "${sliceZoneFieldId}" in ${typeId}`,
155
+ );
156
+ process.exitCode = 1;
157
+ return;
158
+ }
159
+
160
+ // Remove the slice reference
161
+ delete sliceZone.config.choices[sliceId];
162
+
163
+ // Write updated model
164
+ try {
165
+ await writeFile(modelPath, stringify(model));
166
+ } catch (error) {
167
+ if (error instanceof Error) {
168
+ console.error(`Failed to update page type: ${error.message}`);
169
+ } else {
170
+ console.error("Failed to update page type");
171
+ }
172
+ process.exitCode = 1;
173
+ return;
174
+ }
175
+
176
+ console.info(
177
+ `Disconnected slice "${sliceId}" from slice zone "${sliceZoneFieldId}" in ${typeId}`,
178
+ );
179
+ }
@@ -0,0 +1,109 @@
1
+ import { readdir, readFile } from "node:fs/promises";
2
+ import { parseArgs } from "node:util";
3
+ import * as v from "valibot";
4
+
5
+ import { findUpward } from "./lib/file";
6
+
7
+ const HELP = `
8
+ List all page types in a Prismic project.
9
+
10
+ USAGE
11
+ prismic page-type list [flags]
12
+
13
+ FLAGS
14
+ --json Output as JSON
15
+ -h, --help Show help for command
16
+
17
+ EXAMPLES
18
+ prismic page-type list
19
+ prismic page-type list --json
20
+ `.trim();
21
+
22
+ const CustomTypeSchema = v.object({
23
+ id: v.string(),
24
+ label: v.string(),
25
+ repeatable: v.boolean(),
26
+ status: v.boolean(),
27
+ format: v.optional(v.string()),
28
+ json: v.record(v.string(), v.record(v.string(), v.unknown())),
29
+ });
30
+
31
+ export async function pageTypeList(): Promise<void> {
32
+ const {
33
+ values: { help, json },
34
+ } = parseArgs({
35
+ args: process.argv.slice(4), // skip: node, script, "page-type", "list"
36
+ options: {
37
+ json: { type: "boolean" },
38
+ help: { type: "boolean", short: "h" },
39
+ },
40
+ allowPositionals: true,
41
+ });
42
+
43
+ if (help) {
44
+ console.info(HELP);
45
+ return;
46
+ }
47
+
48
+ const projectRoot = await findUpward("package.json");
49
+ if (!projectRoot) {
50
+ console.error("Could not find project root (no package.json found)");
51
+ process.exitCode = 1;
52
+ return;
53
+ }
54
+
55
+ const customTypesDirectory = new URL("customtypes/", projectRoot);
56
+
57
+ let entries: string[];
58
+ try {
59
+ entries = (await readdir(customTypesDirectory, {
60
+ withFileTypes: false,
61
+ })) as unknown as string[];
62
+ } catch {
63
+ if (json) {
64
+ console.info(JSON.stringify([]));
65
+ } else {
66
+ console.info("No page types found.");
67
+ }
68
+ return;
69
+ }
70
+
71
+ const pageTypes: { id: string; label: string; repeatable: boolean }[] = [];
72
+
73
+ for (const entry of entries) {
74
+ const modelPath = new URL(`${entry}/index.json`, customTypesDirectory);
75
+ try {
76
+ const contents = await readFile(modelPath, "utf8");
77
+ const parsed = JSON.parse(contents);
78
+ const result = v.safeParse(CustomTypeSchema, parsed);
79
+ if (result.success && result.output.format === "page") {
80
+ pageTypes.push({
81
+ id: result.output.id,
82
+ label: result.output.label,
83
+ repeatable: result.output.repeatable,
84
+ });
85
+ }
86
+ } catch {
87
+ // Skip directories without valid index.json
88
+ }
89
+ }
90
+
91
+ if (pageTypes.length === 0) {
92
+ if (json) {
93
+ console.info(JSON.stringify([]));
94
+ } else {
95
+ console.info("No page types found.");
96
+ }
97
+ return;
98
+ }
99
+
100
+ if (json) {
101
+ console.info(JSON.stringify(pageTypes, null, 2));
102
+ } else {
103
+ console.info("ID\tLABEL\tTYPE");
104
+ for (const pageType of pageTypes) {
105
+ const typeLabel = pageType.repeatable ? "repeatable" : "singleton";
106
+ console.info(`${pageType.id}\t${pageType.label}\t${typeLabel}`);
107
+ }
108
+ }
109
+ }
@@ -0,0 +1,161 @@
1
+ import type { CustomType } 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
+ Remove a field from a page type.
12
+
13
+ USAGE
14
+ prismic page-type remove-field <type-id> <field-id> [flags]
15
+
16
+ ARGUMENTS
17
+ type-id Page type identifier (required)
18
+ field-id Field identifier (required)
19
+
20
+ FLAGS
21
+ --tab string Specific tab (searches all tabs if not specified)
22
+ -h, --help Show help for command
23
+
24
+ EXAMPLES
25
+ prismic page-type remove-field homepage title
26
+ prismic page-type remove-field homepage meta_title --tab "SEO & Metadata"
27
+ `.trim();
28
+
29
+ const CustomTypeSchema = v.object({
30
+ id: v.string(),
31
+ label: v.string(),
32
+ repeatable: v.boolean(),
33
+ status: v.boolean(),
34
+ format: v.optional(v.string()),
35
+ json: v.record(v.string(), v.record(v.string(), v.unknown())),
36
+ });
37
+
38
+ export async function pageTypeRemoveField(): Promise<void> {
39
+ const {
40
+ values: { help, tab },
41
+ positionals: [typeId, fieldId],
42
+ } = parseArgs({
43
+ args: process.argv.slice(4), // skip: node, script, "page-type", "remove-field"
44
+ options: {
45
+ tab: { type: "string" },
46
+ help: { type: "boolean", short: "h" },
47
+ },
48
+ allowPositionals: true,
49
+ });
50
+
51
+ if (help) {
52
+ console.info(HELP);
53
+ return;
54
+ }
55
+
56
+ if (!typeId) {
57
+ console.error("Missing required argument: type-id\n");
58
+ console.error("Usage: prismic page-type remove-field <type-id> <field-id>");
59
+ process.exitCode = 1;
60
+ return;
61
+ }
62
+
63
+ if (!fieldId) {
64
+ console.error("Missing required argument: field-id\n");
65
+ console.error("Usage: prismic page-type remove-field <type-id> <field-id>");
66
+ process.exitCode = 1;
67
+ return;
68
+ }
69
+
70
+ const projectRoot = await findUpward("package.json");
71
+ if (!projectRoot) {
72
+ console.error("Could not find project root (no package.json found)");
73
+ process.exitCode = 1;
74
+ return;
75
+ }
76
+
77
+ const modelPath = new URL(`customtypes/${typeId}/index.json`, projectRoot);
78
+
79
+ // Read and parse the model
80
+ let model: CustomType;
81
+ try {
82
+ const contents = await readFile(modelPath, "utf8");
83
+ const result = v.safeParse(CustomTypeSchema, JSON.parse(contents));
84
+ if (!result.success) {
85
+ console.error(`Invalid page type model: ${modelPath.href}`);
86
+ process.exitCode = 1;
87
+ return;
88
+ }
89
+ model = result.output as CustomType;
90
+ } catch (error) {
91
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
92
+ console.error(`Page type not found: ${typeId}\n`);
93
+ console.error(`Create it first with: prismic page-type create ${typeId}`);
94
+ process.exitCode = 1;
95
+ return;
96
+ }
97
+ if (error instanceof Error) {
98
+ console.error(`Failed to read page type: ${error.message}`);
99
+ } else {
100
+ console.error("Failed to read page type");
101
+ }
102
+ process.exitCode = 1;
103
+ return;
104
+ }
105
+
106
+ // Check if this is actually a page type
107
+ if (model.format !== "page") {
108
+ console.error(`"${typeId}" is not a page type (format: ${model.format ?? "custom"})`);
109
+ process.exitCode = 1;
110
+ return;
111
+ }
112
+
113
+ // Find and remove the field
114
+ let foundTab: string | undefined;
115
+
116
+ if (tab) {
117
+ // Look in specific tab
118
+ if (!model.json[tab]) {
119
+ console.error(`Tab "${tab}" not found in page type "${typeId}"`);
120
+ console.error(`Available tabs: ${Object.keys(model.json).join(", ")}`);
121
+ process.exitCode = 1;
122
+ return;
123
+ }
124
+ if (!(fieldId in model.json[tab])) {
125
+ console.error(`Field "${fieldId}" not found in tab "${tab}"`);
126
+ process.exitCode = 1;
127
+ return;
128
+ }
129
+ delete model.json[tab][fieldId];
130
+ foundTab = tab;
131
+ } else {
132
+ // Search all tabs
133
+ for (const [tabName, tabFields] of Object.entries(model.json)) {
134
+ if (fieldId in tabFields) {
135
+ delete tabFields[fieldId];
136
+ foundTab = tabName;
137
+ break;
138
+ }
139
+ }
140
+ if (!foundTab) {
141
+ console.error(`Field "${fieldId}" not found in any tab of page type "${typeId}"`);
142
+ process.exitCode = 1;
143
+ return;
144
+ }
145
+ }
146
+
147
+ // Write updated model
148
+ try {
149
+ await writeFile(modelPath, stringify(model));
150
+ } catch (error) {
151
+ if (error instanceof Error) {
152
+ console.error(`Failed to update page type: ${error.message}`);
153
+ } else {
154
+ console.error("Failed to update page type");
155
+ }
156
+ process.exitCode = 1;
157
+ return;
158
+ }
159
+
160
+ console.info(`Removed field "${fieldId}" from tab "${foundTab}" in page type "${typeId}"`);
161
+ }
@@ -0,0 +1,126 @@
1
+ import type { CustomType } from "@prismicio/types-internal/lib/customtypes";
2
+
3
+ import { readFile, rm } from "node:fs/promises";
4
+ import { parseArgs } from "node:util";
5
+ import * as v from "valibot";
6
+
7
+ import { findUpward } from "./lib/file";
8
+
9
+ const HELP = `
10
+ Remove a page type from the project.
11
+
12
+ USAGE
13
+ prismic page-type remove <type-id> [flags]
14
+
15
+ ARGUMENTS
16
+ type-id Page type identifier (required)
17
+
18
+ FLAGS
19
+ -y Confirm removal
20
+ -h, --help Show help for command
21
+
22
+ EXAMPLES
23
+ prismic page-type remove homepage
24
+ prismic page-type remove homepage -y
25
+ `.trim();
26
+
27
+ const CustomTypeSchema = v.object({
28
+ id: v.string(),
29
+ label: v.string(),
30
+ repeatable: v.boolean(),
31
+ status: v.boolean(),
32
+ format: v.optional(v.string()),
33
+ json: v.record(v.string(), v.record(v.string(), v.unknown())),
34
+ });
35
+
36
+ export async function pageTypeRemove(): Promise<void> {
37
+ const {
38
+ values: { help, y },
39
+ positionals: [typeId],
40
+ } = parseArgs({
41
+ args: process.argv.slice(4), // skip: node, script, "page-type", "remove"
42
+ options: {
43
+ y: { type: "boolean", short: "y" },
44
+ help: { type: "boolean", short: "h" },
45
+ },
46
+ allowPositionals: true,
47
+ });
48
+
49
+ if (help) {
50
+ console.info(HELP);
51
+ return;
52
+ }
53
+
54
+ if (!typeId) {
55
+ console.error("Missing required argument: type-id\n");
56
+ console.error("Usage: prismic page-type remove <type-id>");
57
+ process.exitCode = 1;
58
+ return;
59
+ }
60
+
61
+ const projectRoot = await findUpward("package.json");
62
+ if (!projectRoot) {
63
+ console.error("Could not find project root (no package.json found)");
64
+ process.exitCode = 1;
65
+ return;
66
+ }
67
+
68
+ const typeDirectory = new URL(`customtypes/${typeId}/`, projectRoot);
69
+ const modelPath = new URL("index.json", typeDirectory);
70
+
71
+ // Verify the page type exists and is actually a page type
72
+ let model: CustomType;
73
+ try {
74
+ const contents = await readFile(modelPath, "utf8");
75
+ const result = v.safeParse(CustomTypeSchema, JSON.parse(contents));
76
+ if (!result.success) {
77
+ console.error(`Invalid page type model: ${modelPath.href}`);
78
+ process.exitCode = 1;
79
+ return;
80
+ }
81
+ model = result.output as CustomType;
82
+ } catch (error) {
83
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
84
+ console.error(`Page type not found: ${typeId}`);
85
+ process.exitCode = 1;
86
+ return;
87
+ }
88
+ if (error instanceof Error) {
89
+ console.error(`Failed to read page type: ${error.message}`);
90
+ } else {
91
+ console.error("Failed to read page type");
92
+ }
93
+ process.exitCode = 1;
94
+ return;
95
+ }
96
+
97
+ // Check if this is actually a page type
98
+ if (model.format !== "page") {
99
+ console.error(`"${typeId}" is not a page type (format: ${model.format ?? "custom"})`);
100
+ process.exitCode = 1;
101
+ return;
102
+ }
103
+
104
+ // Require -y flag to confirm deletion
105
+ if (!y) {
106
+ console.error(`Refusing to remove page type "${typeId}" (this will delete the entire directory).`);
107
+ console.error("Re-run with -y to confirm.");
108
+ process.exitCode = 1;
109
+ return;
110
+ }
111
+
112
+ // Delete the page type directory
113
+ try {
114
+ await rm(typeDirectory, { recursive: true });
115
+ } catch (error) {
116
+ if (error instanceof Error) {
117
+ console.error(`Failed to remove page type: ${error.message}`);
118
+ } else {
119
+ console.error("Failed to remove page type");
120
+ }
121
+ process.exitCode = 1;
122
+ return;
123
+ }
124
+
125
+ console.info(`Removed page type "${typeId}"`);
126
+ }