prismic 0.0.0-pr.28.59bf330

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 (158) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +69 -0
  3. package/dist/builders-hKD4IrLX-DsO7BUQw.mjs +97 -0
  4. package/dist/dist-B11B2hHn.mjs +1 -0
  5. package/dist/dist-DT8CtumB.mjs +1 -0
  6. package/dist/framework-CfjEoVk0.mjs +17 -0
  7. package/dist/index.mjs +2537 -0
  8. package/dist/nextjs-9z7YrSnS.mjs +312 -0
  9. package/dist/nuxt-KoJ61G2q.mjs +59 -0
  10. package/dist/sveltekit-DjXKCG78.mjs +226 -0
  11. package/package.json +58 -0
  12. package/src/codegen-types.ts +82 -0
  13. package/src/codegen.ts +45 -0
  14. package/src/custom-type-add-field-boolean.ts +185 -0
  15. package/src/custom-type-add-field-color.ts +168 -0
  16. package/src/custom-type-add-field-date.ts +171 -0
  17. package/src/custom-type-add-field-embed.ts +168 -0
  18. package/src/custom-type-add-field-geo-point.ts +165 -0
  19. package/src/custom-type-add-field-group.ts +142 -0
  20. package/src/custom-type-add-field-image.ts +168 -0
  21. package/src/custom-type-add-field-key-text.ts +168 -0
  22. package/src/custom-type-add-field-link.ts +191 -0
  23. package/src/custom-type-add-field-number.ts +200 -0
  24. package/src/custom-type-add-field-rich-text.ts +192 -0
  25. package/src/custom-type-add-field-select.ts +174 -0
  26. package/src/custom-type-add-field-timestamp.ts +171 -0
  27. package/src/custom-type-add-field-uid.ts +151 -0
  28. package/src/custom-type-add-field.ts +116 -0
  29. package/src/custom-type-connect-slice.ts +178 -0
  30. package/src/custom-type-create.ts +98 -0
  31. package/src/custom-type-disconnect-slice.ts +134 -0
  32. package/src/custom-type-list.ts +110 -0
  33. package/src/custom-type-remove-field.ts +135 -0
  34. package/src/custom-type-remove.ts +103 -0
  35. package/src/custom-type-set-name.ts +102 -0
  36. package/src/custom-type-view.ts +118 -0
  37. package/src/custom-type.ts +85 -0
  38. package/src/docs-fetch.ts +146 -0
  39. package/src/docs-list.ts +131 -0
  40. package/src/docs.ts +54 -0
  41. package/src/env.d.ts +12 -0
  42. package/src/framework/index.ts +399 -0
  43. package/src/framework/nextjs.templates.ts +426 -0
  44. package/src/framework/nextjs.ts +216 -0
  45. package/src/framework/nuxt.templates.ts +74 -0
  46. package/src/framework/nuxt.ts +250 -0
  47. package/src/framework/sveltekit.templates.ts +278 -0
  48. package/src/framework/sveltekit.ts +241 -0
  49. package/src/index.ts +155 -0
  50. package/src/init.ts +173 -0
  51. package/src/lib/auth.ts +200 -0
  52. package/src/lib/browser.ts +11 -0
  53. package/src/lib/config.ts +111 -0
  54. package/src/lib/custom-types-api.ts +385 -0
  55. package/src/lib/field-path.ts +81 -0
  56. package/src/lib/file.ts +49 -0
  57. package/src/lib/json.ts +3 -0
  58. package/src/lib/packageJson.ts +35 -0
  59. package/src/lib/profile.ts +39 -0
  60. package/src/lib/request.ts +116 -0
  61. package/src/lib/segment.ts +145 -0
  62. package/src/lib/sentry.ts +63 -0
  63. package/src/lib/string.ts +10 -0
  64. package/src/lib/url.ts +31 -0
  65. package/src/locale-add.ts +116 -0
  66. package/src/locale-list.ts +107 -0
  67. package/src/locale-remove.ts +88 -0
  68. package/src/locale-set-default.ts +131 -0
  69. package/src/locale.ts +60 -0
  70. package/src/login.ts +45 -0
  71. package/src/logout.ts +36 -0
  72. package/src/page-type-add-field-boolean.ts +179 -0
  73. package/src/page-type-add-field-color.ts +165 -0
  74. package/src/page-type-add-field-date.ts +168 -0
  75. package/src/page-type-add-field-embed.ts +165 -0
  76. package/src/page-type-add-field-geo-point.ts +162 -0
  77. package/src/page-type-add-field-group.ts +139 -0
  78. package/src/page-type-add-field-image.ts +165 -0
  79. package/src/page-type-add-field-key-text.ts +165 -0
  80. package/src/page-type-add-field-link.ts +188 -0
  81. package/src/page-type-add-field-number.ts +197 -0
  82. package/src/page-type-add-field-rich-text.ts +189 -0
  83. package/src/page-type-add-field-select.ts +171 -0
  84. package/src/page-type-add-field-timestamp.ts +168 -0
  85. package/src/page-type-add-field-uid.ts +148 -0
  86. package/src/page-type-add-field.ts +116 -0
  87. package/src/page-type-connect-slice.ts +178 -0
  88. package/src/page-type-create.ts +128 -0
  89. package/src/page-type-disconnect-slice.ts +134 -0
  90. package/src/page-type-list.ts +109 -0
  91. package/src/page-type-remove-field.ts +135 -0
  92. package/src/page-type-remove.ts +103 -0
  93. package/src/page-type-set-name.ts +102 -0
  94. package/src/page-type-set-repeatable.ts +111 -0
  95. package/src/page-type-view.ts +118 -0
  96. package/src/page-type.ts +90 -0
  97. package/src/preview-add.ts +126 -0
  98. package/src/preview-get-simulator.ts +104 -0
  99. package/src/preview-list.ts +106 -0
  100. package/src/preview-remove-simulator.ts +80 -0
  101. package/src/preview-remove.ts +109 -0
  102. package/src/preview-set-name.ts +137 -0
  103. package/src/preview-set-simulator.ts +116 -0
  104. package/src/preview.ts +75 -0
  105. package/src/pull.ts +236 -0
  106. package/src/push.ts +409 -0
  107. package/src/repo-create.ts +175 -0
  108. package/src/repo-get-access.ts +86 -0
  109. package/src/repo-list.ts +100 -0
  110. package/src/repo-set-access.ts +100 -0
  111. package/src/repo-set-name.ts +102 -0
  112. package/src/repo-view.ts +113 -0
  113. package/src/repo.ts +70 -0
  114. package/src/slice-add-field-boolean.ts +219 -0
  115. package/src/slice-add-field-color.ts +205 -0
  116. package/src/slice-add-field-date.ts +205 -0
  117. package/src/slice-add-field-embed.ts +205 -0
  118. package/src/slice-add-field-geo-point.ts +202 -0
  119. package/src/slice-add-field-group.ts +170 -0
  120. package/src/slice-add-field-image.ts +202 -0
  121. package/src/slice-add-field-key-text.ts +205 -0
  122. package/src/slice-add-field-link.ts +224 -0
  123. package/src/slice-add-field-number.ts +205 -0
  124. package/src/slice-add-field-rich-text.ts +229 -0
  125. package/src/slice-add-field-select.ts +211 -0
  126. package/src/slice-add-field-timestamp.ts +205 -0
  127. package/src/slice-add-field.ts +111 -0
  128. package/src/slice-add-variation.ts +142 -0
  129. package/src/slice-create.ts +164 -0
  130. package/src/slice-list-variations.ts +71 -0
  131. package/src/slice-list.ts +60 -0
  132. package/src/slice-remove-field.ts +125 -0
  133. package/src/slice-remove-variation.ts +113 -0
  134. package/src/slice-remove.ts +92 -0
  135. package/src/slice-rename.ts +104 -0
  136. package/src/slice-set-screenshot.ts +239 -0
  137. package/src/slice-view.ts +83 -0
  138. package/src/slice.ts +95 -0
  139. package/src/status.ts +834 -0
  140. package/src/sync.ts +259 -0
  141. package/src/token-create.ts +203 -0
  142. package/src/token-delete.ts +182 -0
  143. package/src/token-list.ts +223 -0
  144. package/src/token-set-name.ts +193 -0
  145. package/src/token.ts +60 -0
  146. package/src/webhook-add-header.ts +118 -0
  147. package/src/webhook-create.ts +152 -0
  148. package/src/webhook-disable.ts +109 -0
  149. package/src/webhook-enable.ts +132 -0
  150. package/src/webhook-list.ts +93 -0
  151. package/src/webhook-remove-header.ts +117 -0
  152. package/src/webhook-remove.ts +106 -0
  153. package/src/webhook-set-triggers.ts +148 -0
  154. package/src/webhook-status.ts +90 -0
  155. package/src/webhook-test.ts +106 -0
  156. package/src/webhook-view.ts +147 -0
  157. package/src/webhook.ts +95 -0
  158. package/src/whoami.ts +62 -0
@@ -0,0 +1,131 @@
1
+ import { parseArgs } from "node:util";
2
+
3
+ const HELP = `
4
+ List documentation pages from Prismic's docs site.
5
+
6
+ USAGE
7
+ prismic docs list [flags]
8
+
9
+ FLAGS
10
+ -h, --help Show help for command
11
+
12
+ EXAMPLES
13
+ prismic docs list
14
+ `.trim();
15
+
16
+ const ROOT_SITEMAP_URL = "https://prismic.io/docs/sitemap.xml";
17
+
18
+ function decodeXmlEntities(input: string): string {
19
+ return input
20
+ .replaceAll("&", "&")
21
+ .replaceAll("&lt;", "<")
22
+ .replaceAll("&gt;", ">")
23
+ .replaceAll("&quot;", '"')
24
+ .replaceAll("&apos;", "'");
25
+ }
26
+
27
+ function extractLocEntries(xml: string): string[] {
28
+ const locPattern = /<loc>(.*?)<\/loc>/g;
29
+ const entries: string[] = [];
30
+ let match = locPattern.exec(xml);
31
+
32
+ while (match) {
33
+ entries.push(decodeXmlEntities(match[1]).trim());
34
+ match = locPattern.exec(xml);
35
+ }
36
+
37
+ return entries.filter(Boolean);
38
+ }
39
+
40
+ async function fetchXml(url: string): Promise<{ ok: true; xml: string } | { ok: false; error: string }> {
41
+ try {
42
+ const response = await fetch(url);
43
+ if (!response.ok) {
44
+ return {
45
+ ok: false,
46
+ error: `Failed to fetch sitemap: ${response.status} (${url})`,
47
+ };
48
+ }
49
+
50
+ return {
51
+ ok: true,
52
+ xml: await response.text(),
53
+ };
54
+ } catch (error) {
55
+ const message = error instanceof Error ? error.message : String(error);
56
+ return { ok: false, error: `Network error while fetching sitemap ${url}: ${message}` };
57
+ }
58
+ }
59
+
60
+ function toDocsPath(urlString: string): string | null {
61
+ try {
62
+ const url = new URL(urlString);
63
+ if (url.hostname !== "prismic.io") {
64
+ return null;
65
+ }
66
+
67
+ if (!url.pathname.startsWith("/docs/")) {
68
+ return null;
69
+ }
70
+
71
+ return url.pathname.replace(/^\/docs\//, "").replace(/^\/+|\/+$/g, "");
72
+ } catch {
73
+ return null;
74
+ }
75
+ }
76
+
77
+ export async function docsList(): Promise<void> {
78
+ const {
79
+ values: { help },
80
+ } = parseArgs({
81
+ args: process.argv.slice(4),
82
+ options: {
83
+ help: { type: "boolean", short: "h" },
84
+ },
85
+ allowPositionals: true,
86
+ });
87
+
88
+ if (help) {
89
+ console.info(HELP);
90
+ return;
91
+ }
92
+
93
+ const rootResult = await fetchXml(ROOT_SITEMAP_URL);
94
+ if (!rootResult.ok) {
95
+ console.error(rootResult.error);
96
+ process.exitCode = 1;
97
+ return;
98
+ }
99
+
100
+ const nestedSitemapUrls = extractLocEntries(rootResult.xml);
101
+ if (nestedSitemapUrls.length === 0) {
102
+ console.error(`No nested sitemaps found in ${ROOT_SITEMAP_URL}`);
103
+ process.exitCode = 1;
104
+ return;
105
+ }
106
+
107
+ const nestedResults = await Promise.all(nestedSitemapUrls.map((url) => fetchXml(url)));
108
+ const failedFetch = nestedResults.find((result) => !result.ok);
109
+ if (failedFetch && !failedFetch.ok) {
110
+ console.error(failedFetch.error);
111
+ process.exitCode = 1;
112
+ return;
113
+ }
114
+
115
+ const paths = new Set<string>();
116
+
117
+ for (const nestedResult of nestedResults) {
118
+ if (!nestedResult.ok) continue;
119
+
120
+ for (const url of extractLocEntries(nestedResult.xml)) {
121
+ const path = toDocsPath(url);
122
+ if (path) {
123
+ paths.add(path);
124
+ }
125
+ }
126
+ }
127
+
128
+ for (const path of [...paths].sort((a, b) => a.localeCompare(b))) {
129
+ console.info(path);
130
+ }
131
+ }
package/src/docs.ts ADDED
@@ -0,0 +1,54 @@
1
+ import { parseArgs } from "node:util";
2
+ import { docsFetch } from "./docs-fetch";
3
+ import { docsList } from "./docs-list";
4
+
5
+ const HELP = `
6
+ Fetch and list documentation from Prismic's docs site.
7
+
8
+ USAGE
9
+ prismic docs <command> [flags]
10
+
11
+ COMMANDS
12
+ fetch Fetch and display a documentation page
13
+ list List documentation pages
14
+
15
+ FLAGS
16
+ -h, --help Show help for command
17
+
18
+ EXAMPLES
19
+ prismic docs fetch nextjs
20
+ prismic docs fetch nextjs#set-up-a-prismic-client
21
+ prismic docs list
22
+
23
+ LEARN MORE
24
+ Use \`prismic docs <command> --help\` for more information about a command.
25
+ `.trim();
26
+
27
+ export async function docs(): Promise<void> {
28
+ const {
29
+ positionals: [subcommand],
30
+ } = parseArgs({
31
+ args: process.argv.slice(3),
32
+ options: {
33
+ help: { type: "boolean", short: "h" },
34
+ },
35
+ allowPositionals: true,
36
+ strict: false,
37
+ });
38
+
39
+ switch (subcommand) {
40
+ case "fetch":
41
+ await docsFetch();
42
+ break;
43
+ case "list":
44
+ await docsList();
45
+ break;
46
+ default: {
47
+ if (subcommand) {
48
+ console.error(`Unknown docs subcommand: ${subcommand}\n`);
49
+ process.exitCode = 1;
50
+ }
51
+ console.info(HELP);
52
+ }
53
+ }
54
+ }
package/src/env.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ interface ImportMetaEnv {
2
+ readonly MODE: string;
3
+ readonly DEV: boolean;
4
+ readonly PROD: boolean;
5
+ readonly PRISMIC_SENTRY_DSN: string | undefined;
6
+ readonly PRISMIC_SENTRY_ENVIRONMENT: string | undefined;
7
+ readonly PRISMIC_SENTRY_ENABLED: string | undefined;
8
+ }
9
+
10
+ interface ImportMeta {
11
+ readonly env: ImportMetaEnv;
12
+ }
@@ -0,0 +1,399 @@
1
+ import type { CustomType, SharedSlice } from "@prismicio/types-internal/lib/customtypes";
2
+
3
+ import { pascalCase } from "change-case";
4
+ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
5
+ import { relative } from "node:path";
6
+ import { fileURLToPath, pathToFileURL } from "node:url";
7
+ import { glob } from "tinyglobby";
8
+ import * as v from "valibot";
9
+
10
+ import { readConfig } from "../lib/config";
11
+ import { exists, findUpward } from "../lib/file";
12
+ import { stringify } from "../lib/json";
13
+ import { addDependencies } from "../lib/packageJson";
14
+ import { dedent } from "../lib/string";
15
+ import { appendTrailingSlash } from "../lib/url";
16
+
17
+ export abstract class FrameworkAdapter {
18
+ abstract readonly id: Framework;
19
+
20
+ abstract getDependencies(): Promise<Record<string, string>>;
21
+
22
+ abstract getClientFilePath(): Promise<string | null>;
23
+
24
+ abstract getSlicesDirectoryPath(): Promise<string>;
25
+
26
+ abstract getSliceComponentExtensions(): string[];
27
+
28
+ abstract getRoutePath(route: string): Promise<{ path: string; extensions: string[] } | null>;
29
+
30
+ abstract createSliceComponent(
31
+ model: SharedSlice,
32
+ sliceDirectory: URL,
33
+ ): Promise<{ componentPath: URL }>;
34
+
35
+ abstract getSliceImportPath(relativeDirectory: string): string;
36
+
37
+ abstract getDefaultSliceLibraryPath(projectRoot: URL): Promise<URL>;
38
+
39
+ async initProject(): Promise<void> {
40
+ const deps = await this.getDependencies();
41
+ await addDependencies(deps);
42
+ const libraries = await this.#getSliceLibraries();
43
+ for (const library of libraries) {
44
+ await this.#updateSliceLibraryIndexFile(library);
45
+ }
46
+ }
47
+
48
+ async createSlice(
49
+ model: SharedSlice,
50
+ library: URL,
51
+ ): Promise<{ modelPath: URL; componentPath: URL; indexPath: URL }> {
52
+ const { modelPath } = await this.#writeSliceModel(model, library);
53
+ const sliceDirectory = await this.#getSliceDirectory(model.name, library);
54
+ const { componentPath } = await this.createSliceComponent(model, sliceDirectory);
55
+ const { indexPath } = await this.#updateSliceLibraryIndexFile(library);
56
+ return { modelPath, componentPath, indexPath };
57
+ }
58
+
59
+ async readSlice(sliceId: string): Promise<SharedSlice> {
60
+ const slice = await this.#findSlice(sliceId);
61
+ return slice.model;
62
+ }
63
+
64
+ async updateSlice(model: SharedSlice): Promise<{ modelPath: URL; indexPath: URL }> {
65
+ const existingSlice = await this.#findSlice(model.id);
66
+ const { modelPath } = await this.#writeSliceModel(model, existingSlice.library);
67
+ const { indexPath } = await this.#updateSliceLibraryIndexFile(existingSlice.library);
68
+ return { modelPath, indexPath };
69
+ }
70
+
71
+ async renameSlice(model: SharedSlice): Promise<{ modelPath: URL; indexPath: URL }> {
72
+ const existingSlice = await this.#findSlice(model.id);
73
+ const newSliceDirectory = await this.#getSliceDirectory(model.name, existingSlice.library);
74
+ await rename(existingSlice.directory, newSliceDirectory);
75
+ const { modelPath } = await this.#writeSliceModel(model, existingSlice.library);
76
+ const { indexPath } = await this.#updateSliceLibraryIndexFile(existingSlice.library);
77
+ return { modelPath, indexPath };
78
+ }
79
+
80
+ async deleteSlice(sliceId: string): Promise<{ sliceDirectory: URL; indexPath: URL }> {
81
+ const slice = await this.#findSlice(sliceId);
82
+ await rm(slice.directory, { recursive: true });
83
+ const { indexPath } = await this.#updateSliceLibraryIndexFile(slice.library);
84
+ return { sliceDirectory: slice.directory, indexPath };
85
+ }
86
+
87
+ async getSlices(library?: URL): Promise<{ library: URL; directory: URL; model: SharedSlice }[]> {
88
+ const libraryDirs = library ? [library] : await this.#getSliceLibraries();
89
+ const allSlices: {
90
+ library: URL;
91
+ directory: URL;
92
+ model: SharedSlice;
93
+ }[] = [];
94
+
95
+ for (const libraryDir of libraryDirs) {
96
+ const modelGlob = new URL("*/model.json", libraryDir);
97
+ const sliceModelPaths = Array.from(
98
+ await glob(fileURLToPath(modelGlob), { absolute: true }),
99
+ (path) => pathToFileURL(path),
100
+ );
101
+ const slices = await Promise.all(
102
+ sliceModelPaths.map(async (sliceModelPath) => {
103
+ const directory = new URL(".", sliceModelPath);
104
+ const model = await readFile(sliceModelPath, "utf8");
105
+ return {
106
+ library: libraryDir,
107
+ directory,
108
+ model: JSON.parse(model),
109
+ };
110
+ }),
111
+ );
112
+ allSlices.push(...slices);
113
+ }
114
+
115
+ return allSlices;
116
+ }
117
+
118
+ async getDefaultSliceLibrary(): Promise<URL> {
119
+ const dirs = await this.#getSliceLibraries();
120
+ return dirs[0];
121
+ }
122
+
123
+ async createCustomType(model: CustomType): Promise<{ modelPath: URL }> {
124
+ const { modelPath } = await this.#writeCustomType(model);
125
+ return { modelPath };
126
+ }
127
+
128
+ async readCustomType(customTypeId: string): Promise<CustomType> {
129
+ const customTypeDirectory = await this.#getCustomTypeDirectory(customTypeId);
130
+ const modelPath = new URL("index.json", customTypeDirectory);
131
+ const model = await readFile(modelPath, "utf8");
132
+ const json = JSON.parse(model);
133
+ return json;
134
+ }
135
+
136
+ async updateCustomType(model: CustomType): Promise<{ modelPath: URL }> {
137
+ const { modelPath } = await this.#writeCustomType(model);
138
+ return { modelPath };
139
+ }
140
+
141
+ async renameCustomType(model: CustomType): Promise<{ modelPath: URL }> {
142
+ const existingCustomTypeDirectory = await this.#getCustomTypeDirectory(model.id);
143
+ const newCustomTypeDirectory = await this.#getCustomTypeDirectory(model.id);
144
+ await rename(existingCustomTypeDirectory, newCustomTypeDirectory);
145
+ const { modelPath } = await this.#writeCustomType(model);
146
+ return { modelPath };
147
+ }
148
+
149
+ async deleteCustomType(customTypeId: string): Promise<{ customTypeDirectory: URL }> {
150
+ const customTypeDirectory = await this.#getCustomTypeDirectory(customTypeId);
151
+ await rm(customTypeDirectory, { recursive: true });
152
+ return { customTypeDirectory };
153
+ }
154
+
155
+ async getCustomTypes(): Promise<{ directory: URL; model: CustomType }[]> {
156
+ const customTypesDirectory = await this.#getCustomTypesDirectory();
157
+ const modelGlob = new URL("*/index.json", customTypesDirectory);
158
+ const customTypeModelPaths = Array.from(
159
+ await glob(fileURLToPath(modelGlob), { absolute: true }),
160
+ (path) => pathToFileURL(path),
161
+ );
162
+ const customTypes = await Promise.all(
163
+ customTypeModelPaths.map(async (customTypeModelPath) => {
164
+ const directory = new URL(".", customTypeModelPath);
165
+ const model = await readFile(customTypeModelPath, "utf8");
166
+ return {
167
+ directory,
168
+ model: JSON.parse(model),
169
+ };
170
+ }),
171
+ );
172
+ return customTypes;
173
+ }
174
+
175
+ async getProjectRoot(): Promise<URL> {
176
+ const packageJsonPath = await findUpward("package.json");
177
+ if (!packageJsonPath) {
178
+ throw new Error("No package.json found");
179
+ }
180
+ const projectRoot = new URL("./", packageJsonPath);
181
+ return projectRoot;
182
+ }
183
+
184
+ protected async checkIsTypeScriptProject(): Promise<boolean> {
185
+ const projectRoot = await this.getProjectRoot();
186
+ const tsconfigPath = new URL("tsconfig.json", projectRoot);
187
+ const isTypeScriptProject = await exists(tsconfigPath);
188
+ return isTypeScriptProject;
189
+ }
190
+
191
+ protected async getJsFileExtension(): Promise<string> {
192
+ const isTypeScriptProject = await this.checkIsTypeScriptProject();
193
+ const jsFileExtension = isTypeScriptProject ? "ts" : "js";
194
+ return jsFileExtension;
195
+ }
196
+
197
+ async #findSlice(sliceId: string): Promise<{ library: URL; directory: URL; model: SharedSlice }> {
198
+ const slices = await this.getSlices();
199
+ const slice = slices.find((slice) => slice.model.id === sliceId);
200
+ if (!slice) throw new Error(`No slice found with ID: ${sliceId}`);
201
+ return slice;
202
+ }
203
+
204
+ async #writeSliceModel(model: SharedSlice, library: URL): Promise<{ modelPath: URL }> {
205
+ const sliceDirectory = await this.#getSliceDirectory(model.name, library);
206
+ await mkdir(sliceDirectory, { recursive: true });
207
+ const modelPath = new URL("model.json", sliceDirectory);
208
+ const formattedModel = this.#formatModel(model);
209
+ await writeFile(modelPath, formattedModel);
210
+ return { modelPath };
211
+ }
212
+
213
+ async #getSliceDirectory(sliceName: string, library: URL): Promise<URL> {
214
+ const sliceDirectoryName = pascalCase(sliceName);
215
+ const sliceDirectory = appendTrailingSlash(new URL(sliceDirectoryName, library));
216
+ return sliceDirectory;
217
+ }
218
+
219
+ async #getSliceLibraries(): Promise<URL[]> {
220
+ const projectRoot = await this.getProjectRoot();
221
+ const configResult = await readConfig();
222
+ const sliceLibraries = configResult.ok ? configResult.config.libraries : undefined;
223
+
224
+ if (sliceLibraries?.length) {
225
+ return sliceLibraries.map((sliceLibrary) => {
226
+ const withoutLeadingSlash = sliceLibrary.replace(/^\//, "");
227
+ return appendTrailingSlash(new URL(withoutLeadingSlash, projectRoot));
228
+ });
229
+ }
230
+
231
+ return [await this.getDefaultSliceLibraryPath(projectRoot)];
232
+ }
233
+
234
+ async #updateSliceLibraryIndexFile(library: URL): Promise<{ indexPath: URL }> {
235
+ const slices = await this.getSlices(library);
236
+ const contents = await this.generateSliceLibraryIndexContents(slices);
237
+ const extension = await this.getJsFileExtension();
238
+ const filename = `index.${extension}`;
239
+ const indexPath = new URL(filename, library);
240
+ await writeFile(indexPath, contents);
241
+ return { indexPath };
242
+ }
243
+
244
+ protected async generateSliceLibraryIndexContents(
245
+ slices: { library: URL; directory: URL; model: SharedSlice }[],
246
+ ): Promise<string> {
247
+ const imports = slices.map((slice) => {
248
+ const componentName = pascalCase(slice.model.name);
249
+ const relativeDirectory = relative(
250
+ fileURLToPath(slice.library),
251
+ fileURLToPath(slice.directory),
252
+ );
253
+ return `import ${componentName} from "${this.getSliceImportPath(relativeDirectory)}";`;
254
+ });
255
+ const componentLines = slices.map((slice) => {
256
+ const componentName = pascalCase(slice.model.name);
257
+ return `${slice.model.id}: ${componentName}`;
258
+ });
259
+ return dedent`
260
+ // Code generated by Prismic. DO NOT EDIT.
261
+
262
+ ${imports.join("\n")}
263
+
264
+ export const components = {
265
+ ${componentLines.join(",\n")}
266
+ };
267
+ `;
268
+ }
269
+
270
+ async #writeCustomType(model: CustomType): Promise<{ modelPath: URL }> {
271
+ const customTypeDirectory = await this.#getCustomTypeDirectory(model.id);
272
+ await mkdir(customTypeDirectory, { recursive: true });
273
+ const modelPath = new URL("index.json", customTypeDirectory);
274
+ const formattedModel = this.#formatModel(model);
275
+ await writeFile(modelPath, formattedModel);
276
+ return { modelPath };
277
+ }
278
+
279
+ async #getCustomTypeDirectory(customTypeId: string): Promise<URL> {
280
+ const customTypesDirectory = await this.#getCustomTypesDirectory();
281
+ const customTypeDirectoryName = customTypeId;
282
+ const customTypeDirectory = appendTrailingSlash(
283
+ new URL(customTypeDirectoryName, customTypesDirectory),
284
+ );
285
+ return customTypeDirectory;
286
+ }
287
+
288
+ async #getCustomTypesDirectory(): Promise<URL> {
289
+ const projectRoot = await this.getProjectRoot();
290
+ const customTypesDirectory = new URL(`customtypes/`, projectRoot);
291
+ return customTypesDirectory;
292
+ }
293
+
294
+ #formatModel(model: CustomType | SharedSlice): string {
295
+ const formattedModel = stringify(model);
296
+ return formattedModel;
297
+ }
298
+ }
299
+
300
+ export function getDocsPath(framework: Framework): string {
301
+ switch (framework) {
302
+ case "next":
303
+ return "nextjs/with-cli";
304
+ case "nuxt":
305
+ return "nuxt/with-cli";
306
+ case "sveltekit":
307
+ return "sveltekit/with-cli";
308
+ }
309
+ }
310
+
311
+ export function getWriteComponentsAnchor(framework: Framework): string {
312
+ switch (framework) {
313
+ case "nuxt":
314
+ return "#write-vue-components";
315
+ case "sveltekit":
316
+ return "#write-svelte-components";
317
+ default:
318
+ return "#write-react-components";
319
+ }
320
+ }
321
+
322
+ export function getClientSetupAnchor(framework: Framework): string {
323
+ switch (framework) {
324
+ case "nuxt":
325
+ return "#configure-the-modules-prismic-client";
326
+ default:
327
+ return "#set-up-a-prismic-client";
328
+ }
329
+ }
330
+
331
+ export function getPreviewSetupAnchor(framework: Framework): string {
332
+ switch (framework) {
333
+ case "next":
334
+ return "#set-up-previews-in-next-js";
335
+ case "sveltekit":
336
+ return "#set-up-previews-in-sveltekit";
337
+ default:
338
+ return "";
339
+ }
340
+ }
341
+
342
+ const PackageJsonSchema = v.object({
343
+ dependencies: v.optional(v.record(v.string(), v.string())),
344
+ devDependencies: v.optional(v.record(v.string(), v.string())),
345
+ });
346
+
347
+ export type Framework = "next" | "nuxt" | "sveltekit";
348
+
349
+ export async function requireFramework(): Promise<FrameworkAdapter | undefined> {
350
+ const framework = await getFramework();
351
+ if (!framework) {
352
+ console.error("No supported framework found (Next.js, Nuxt, or SvelteKit required)");
353
+ console.error("Ensure your project has the framework listed as a dependency in package.json");
354
+ process.exitCode = 1;
355
+ return undefined;
356
+ }
357
+ return framework;
358
+ }
359
+
360
+ export async function getFramework(): Promise<FrameworkAdapter | undefined> {
361
+ const id = await detectFramework();
362
+ switch (id) {
363
+ case "next": {
364
+ const { NextJsFramework } = await import("./nextjs");
365
+ return new NextJsFramework();
366
+ }
367
+ case "nuxt": {
368
+ const { NuxtFramework } = await import("./nuxt");
369
+ return new NuxtFramework();
370
+ }
371
+ case "sveltekit": {
372
+ const { SvelteKitFramework } = await import("./sveltekit");
373
+ return new SvelteKitFramework();
374
+ }
375
+ default:
376
+ return undefined;
377
+ }
378
+ }
379
+
380
+ async function detectFramework(): Promise<Framework | undefined> {
381
+ const packageJsonPath = await findUpward("package.json");
382
+ if (!packageJsonPath) return undefined;
383
+
384
+ try {
385
+ const contents = await readFile(packageJsonPath, "utf8");
386
+ const { dependencies = {}, devDependencies = {} } = v.parse(
387
+ PackageJsonSchema,
388
+ JSON.parse(contents),
389
+ );
390
+ const allDeps = { ...dependencies, ...devDependencies };
391
+ if ("next" in allDeps) return "next";
392
+ if ("nuxt" in allDeps) return "nuxt";
393
+ if ("@sveltejs/kit" in allDeps) return "sveltekit";
394
+ } catch {
395
+ // Continue with undefined
396
+ }
397
+
398
+ return undefined;
399
+ }