@angeloashmore/prismic-cli-poc 0.0.0-canary.2ff9563 → 0.0.0-canary.d96e685
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 +108 -131
- package/package.json +1 -1
- package/src/index.ts +0 -5
- package/src/sync.ts +0 -309
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -11,7 +11,6 @@ import { pageType } from "./page-type";
|
|
|
11
11
|
import { preview } from "./preview";
|
|
12
12
|
import { repo } from "./repo";
|
|
13
13
|
import { slice } from "./slice";
|
|
14
|
-
import { sync } from "./sync";
|
|
15
14
|
import { token } from "./token";
|
|
16
15
|
import { webhook } from "./webhook";
|
|
17
16
|
import { whoami } from "./whoami";
|
|
@@ -32,7 +31,6 @@ COMMANDS
|
|
|
32
31
|
page-type Manage page types in a repository
|
|
33
32
|
custom-type Manage custom types in a repository
|
|
34
33
|
slice Manage slices in a project
|
|
35
|
-
sync Sync types and slices from Prismic
|
|
36
34
|
preview Manage preview configurations
|
|
37
35
|
token Manage API tokens in a repository
|
|
38
36
|
webhook Manage webhooks in a repository
|
|
@@ -78,9 +76,6 @@ switch (positionals[0]) {
|
|
|
78
76
|
case "slice":
|
|
79
77
|
await slice();
|
|
80
78
|
break;
|
|
81
|
-
case "sync":
|
|
82
|
-
await sync();
|
|
83
|
-
break;
|
|
84
79
|
case "preview":
|
|
85
80
|
await preview();
|
|
86
81
|
break;
|
package/src/sync.ts
DELETED
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
import type { CustomType, SharedSlice } from "@prismicio/types-internal/lib/customtypes";
|
|
2
|
-
|
|
3
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
4
|
-
import { parseArgs } from "node:util";
|
|
5
|
-
import * as v from "valibot";
|
|
6
|
-
|
|
7
|
-
import { isAuthenticated, readHost, readToken } from "./lib/auth";
|
|
8
|
-
import { safeGetRepositoryFromConfig } from "./lib/config";
|
|
9
|
-
import { findUpward } from "./lib/file";
|
|
10
|
-
import { stringify } from "./lib/json";
|
|
11
|
-
import { getSlicesDirectory, pascalCase, SharedSliceSchema } from "./lib/slice";
|
|
12
|
-
|
|
13
|
-
const HELP = `
|
|
14
|
-
Sync custom types and slices from Prismic to local files.
|
|
15
|
-
|
|
16
|
-
By default, this command reads the repository from prismic.config.json at the
|
|
17
|
-
project root.
|
|
18
|
-
|
|
19
|
-
USAGE
|
|
20
|
-
prismic sync [flags]
|
|
21
|
-
|
|
22
|
-
FLAGS
|
|
23
|
-
-r, --repo string Repository domain
|
|
24
|
-
--dry-run Show what would be synced without writing files
|
|
25
|
-
--types-only Only sync custom types
|
|
26
|
-
--slices-only Only sync slices
|
|
27
|
-
--json Output as JSON
|
|
28
|
-
-h, --help Show help for command
|
|
29
|
-
|
|
30
|
-
EXAMPLES
|
|
31
|
-
prismic sync
|
|
32
|
-
prismic sync --repo my-repo
|
|
33
|
-
prismic sync --dry-run
|
|
34
|
-
prismic sync --types-only
|
|
35
|
-
`.trim();
|
|
36
|
-
|
|
37
|
-
const CustomTypeSchema = v.object({
|
|
38
|
-
id: v.string(),
|
|
39
|
-
label: v.optional(v.string()),
|
|
40
|
-
repeatable: v.boolean(),
|
|
41
|
-
status: v.boolean(),
|
|
42
|
-
format: v.optional(v.string()),
|
|
43
|
-
json: v.record(v.string(), v.unknown()),
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
export async function sync(): Promise<void> {
|
|
47
|
-
const {
|
|
48
|
-
values: { help, repo = await safeGetRepositoryFromConfig(), "dry-run": dryRun, "types-only": typesOnly, "slices-only": slicesOnly, json },
|
|
49
|
-
} = parseArgs({
|
|
50
|
-
args: process.argv.slice(3), // skip: node, script, "sync"
|
|
51
|
-
options: {
|
|
52
|
-
repo: { type: "string", short: "r" },
|
|
53
|
-
"dry-run": { type: "boolean" },
|
|
54
|
-
"types-only": { type: "boolean" },
|
|
55
|
-
"slices-only": { type: "boolean" },
|
|
56
|
-
json: { type: "boolean" },
|
|
57
|
-
help: { type: "boolean", short: "h" },
|
|
58
|
-
},
|
|
59
|
-
allowPositionals: false,
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
if (help) {
|
|
63
|
-
console.info(HELP);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (!repo) {
|
|
68
|
-
console.error("Missing prismic.config.json or --repo option");
|
|
69
|
-
process.exitCode = 1;
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Check authentication
|
|
74
|
-
if (!(await isAuthenticated())) {
|
|
75
|
-
console.error("Not logged in. Run `prismic login` first.");
|
|
76
|
-
process.exitCode = 1;
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (!json) {
|
|
81
|
-
console.info(`Syncing from repository: ${repo}\n`);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Fetch remote data in parallel
|
|
85
|
-
const shouldFetchTypes = !slicesOnly;
|
|
86
|
-
const shouldFetchSlices = !typesOnly;
|
|
87
|
-
|
|
88
|
-
const [customTypesResult, slicesResult] = await Promise.all([
|
|
89
|
-
shouldFetchTypes ? fetchRemoteCustomTypes(repo) : Promise.resolve({ ok: true, value: [] } as const),
|
|
90
|
-
shouldFetchSlices ? fetchRemoteSlices(repo) : Promise.resolve({ ok: true, value: [] } as const),
|
|
91
|
-
]);
|
|
92
|
-
|
|
93
|
-
if (!customTypesResult.ok) {
|
|
94
|
-
console.error(`Failed to fetch custom types: ${customTypesResult.error}`);
|
|
95
|
-
process.exitCode = 1;
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (!slicesResult.ok) {
|
|
100
|
-
console.error(`Failed to fetch slices: ${slicesResult.error}`);
|
|
101
|
-
process.exitCode = 1;
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const customTypes = customTypesResult.value;
|
|
106
|
-
const slices = slicesResult.value;
|
|
107
|
-
|
|
108
|
-
if (!json) {
|
|
109
|
-
if (shouldFetchTypes) {
|
|
110
|
-
console.info(`Fetching custom types... ${customTypes.length} types`);
|
|
111
|
-
}
|
|
112
|
-
if (shouldFetchSlices) {
|
|
113
|
-
console.info(`Fetching slices... ${slices.length} slices`);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Dry run - just show what would be synced
|
|
118
|
-
if (dryRun) {
|
|
119
|
-
if (json) {
|
|
120
|
-
console.info(stringify({ customTypes, slices }));
|
|
121
|
-
} else {
|
|
122
|
-
console.info("");
|
|
123
|
-
if (shouldFetchTypes && customTypes.length > 0) {
|
|
124
|
-
console.info("Would write custom types:");
|
|
125
|
-
for (const ct of customTypes) {
|
|
126
|
-
console.info(` customtypes/${ct.id}/index.json`);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
if (shouldFetchSlices && slices.length > 0) {
|
|
130
|
-
const slicesDir = await getSlicesDirectory();
|
|
131
|
-
const relativeSlicesDir = getRelativePath(slicesDir);
|
|
132
|
-
console.info("Would write slices:");
|
|
133
|
-
for (const slice of slices) {
|
|
134
|
-
console.info(` ${relativeSlicesDir}${pascalCase(slice.name)}/model.json`);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
console.info(`\nDry run complete: ${customTypes.length} custom types, ${slices.length} slices`);
|
|
138
|
-
}
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Find project root
|
|
143
|
-
const projectRoot = await findUpward("package.json");
|
|
144
|
-
if (!projectRoot) {
|
|
145
|
-
console.error("Could not find project root (no package.json found)");
|
|
146
|
-
process.exitCode = 1;
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
const projectDir = new URL(".", projectRoot);
|
|
150
|
-
|
|
151
|
-
const writtenTypes: string[] = [];
|
|
152
|
-
const writtenSlices: string[] = [];
|
|
153
|
-
|
|
154
|
-
// Write custom types
|
|
155
|
-
if (shouldFetchTypes && customTypes.length > 0) {
|
|
156
|
-
if (!json) {
|
|
157
|
-
console.info("\nWriting custom types:");
|
|
158
|
-
}
|
|
159
|
-
const customTypesDir = new URL("customtypes/", projectDir);
|
|
160
|
-
|
|
161
|
-
for (const ct of customTypes) {
|
|
162
|
-
const typeDir = new URL(`${ct.id}/`, customTypesDir);
|
|
163
|
-
const modelPath = new URL("index.json", typeDir);
|
|
164
|
-
|
|
165
|
-
try {
|
|
166
|
-
await mkdir(typeDir, { recursive: true });
|
|
167
|
-
await writeFile(modelPath, stringify(ct));
|
|
168
|
-
const relativePath = `customtypes/${ct.id}/index.json`;
|
|
169
|
-
writtenTypes.push(relativePath);
|
|
170
|
-
if (!json) {
|
|
171
|
-
console.info(` ${relativePath}`);
|
|
172
|
-
}
|
|
173
|
-
} catch (error) {
|
|
174
|
-
console.error(`Failed to write custom type ${ct.id}: ${error instanceof Error ? error.message : error}`);
|
|
175
|
-
process.exitCode = 1;
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Write slices
|
|
182
|
-
if (shouldFetchSlices && slices.length > 0) {
|
|
183
|
-
if (!json) {
|
|
184
|
-
console.info("\nWriting slices:");
|
|
185
|
-
}
|
|
186
|
-
const slicesDir = await getSlicesDirectory();
|
|
187
|
-
|
|
188
|
-
for (const slice of slices) {
|
|
189
|
-
const sliceDir = new URL(`${pascalCase(slice.name)}/`, slicesDir);
|
|
190
|
-
const modelPath = new URL("model.json", sliceDir);
|
|
191
|
-
|
|
192
|
-
try {
|
|
193
|
-
await mkdir(sliceDir, { recursive: true });
|
|
194
|
-
await writeFile(modelPath, stringify(slice));
|
|
195
|
-
const relativePath = `${getRelativePath(slicesDir)}${pascalCase(slice.name)}/model.json`;
|
|
196
|
-
writtenSlices.push(relativePath);
|
|
197
|
-
if (!json) {
|
|
198
|
-
console.info(` ${relativePath}`);
|
|
199
|
-
}
|
|
200
|
-
} catch (error) {
|
|
201
|
-
console.error(`Failed to write slice ${slice.name}: ${error instanceof Error ? error.message : error}`);
|
|
202
|
-
process.exitCode = 1;
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Output summary
|
|
209
|
-
if (json) {
|
|
210
|
-
console.info(stringify({ writtenTypes, writtenSlices }));
|
|
211
|
-
} else {
|
|
212
|
-
console.info(`\nSync complete: ${writtenTypes.length} custom types, ${writtenSlices.length} slices`);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
async function getCustomTypesApiUrl(): Promise<URL> {
|
|
217
|
-
const host = await readHost();
|
|
218
|
-
host.hostname = `customtypes.${host.hostname}`;
|
|
219
|
-
return host;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
type FetchResult<T> = { ok: true; value: T } | { ok: false; error: string };
|
|
223
|
-
|
|
224
|
-
async function fetchRemoteCustomTypes(repo: string): Promise<FetchResult<CustomType[]>> {
|
|
225
|
-
const token = await readToken();
|
|
226
|
-
if (!token) {
|
|
227
|
-
return { ok: false, error: "Not authenticated" };
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const baseUrl = await getCustomTypesApiUrl();
|
|
231
|
-
const url = new URL("customtypes", baseUrl);
|
|
232
|
-
|
|
233
|
-
try {
|
|
234
|
-
const response = await fetch(url, {
|
|
235
|
-
headers: {
|
|
236
|
-
Authorization: `Bearer ${token}`,
|
|
237
|
-
repository: repo,
|
|
238
|
-
},
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
if (!response.ok) {
|
|
242
|
-
if (response.status === 401) {
|
|
243
|
-
return { ok: false, error: "Unauthorized. Your session may have expired. Run `prismic login` again." };
|
|
244
|
-
}
|
|
245
|
-
if (response.status === 403) {
|
|
246
|
-
return { ok: false, error: `Access denied. You may not have access to repository "${repo}".` };
|
|
247
|
-
}
|
|
248
|
-
return { ok: false, error: `API error: ${response.status} ${response.statusText}` };
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const data = await response.json();
|
|
252
|
-
const result = v.safeParse(v.array(CustomTypeSchema), data);
|
|
253
|
-
if (!result.success) {
|
|
254
|
-
return { ok: false, error: "Invalid response from Custom Types API" };
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
return { ok: true, value: result.output as CustomType[] };
|
|
258
|
-
} catch (error) {
|
|
259
|
-
return { ok: false, error: `Network error: ${error instanceof Error ? error.message : error}` };
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
async function fetchRemoteSlices(repo: string): Promise<FetchResult<SharedSlice[]>> {
|
|
264
|
-
const token = await readToken();
|
|
265
|
-
if (!token) {
|
|
266
|
-
return { ok: false, error: "Not authenticated" };
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const baseUrl = await getCustomTypesApiUrl();
|
|
270
|
-
const url = new URL("slices", baseUrl);
|
|
271
|
-
|
|
272
|
-
try {
|
|
273
|
-
const response = await fetch(url, {
|
|
274
|
-
headers: {
|
|
275
|
-
Authorization: `Bearer ${token}`,
|
|
276
|
-
repository: repo,
|
|
277
|
-
},
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
if (!response.ok) {
|
|
281
|
-
if (response.status === 401) {
|
|
282
|
-
return { ok: false, error: "Unauthorized. Your session may have expired. Run `prismic login` again." };
|
|
283
|
-
}
|
|
284
|
-
if (response.status === 403) {
|
|
285
|
-
return { ok: false, error: `Access denied. You may not have access to repository "${repo}".` };
|
|
286
|
-
}
|
|
287
|
-
return { ok: false, error: `API error: ${response.status} ${response.statusText}` };
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const data = await response.json();
|
|
291
|
-
const result = v.safeParse(v.array(SharedSliceSchema), data);
|
|
292
|
-
if (!result.success) {
|
|
293
|
-
return { ok: false, error: "Invalid response from Custom Types API" };
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
return { ok: true, value: result.output as SharedSlice[] };
|
|
297
|
-
} catch (error) {
|
|
298
|
-
return { ok: false, error: `Network error: ${error instanceof Error ? error.message : error}` };
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
function getRelativePath(url: URL): string {
|
|
303
|
-
const cwd = process.cwd();
|
|
304
|
-
const path = url.pathname;
|
|
305
|
-
if (path.startsWith(cwd)) {
|
|
306
|
-
return path.slice(cwd.length + 1);
|
|
307
|
-
}
|
|
308
|
-
return path;
|
|
309
|
-
}
|