|
1 | | -import fs from 'node:fs/promises' |
2 | | -import path from 'node:path' |
3 | | -import { fileURLToPath } from 'node:url' |
4 | | -import nextEnv from '@next/env' |
5 | | -import { getPayload } from 'payload' |
6 | | -import type { Payload } from 'payload' |
7 | | - |
8 | | -type ImportPage = { |
9 | | - id?: number | string |
10 | | - slug?: string |
11 | | - title?: unknown |
12 | | - layout?: unknown |
13 | | - [key: string]: unknown |
14 | | -} |
15 | | - |
16 | | -type ImportLocale = 'en' | 'de' |
| 1 | +import fs from "node:fs/promises" |
| 2 | +import path from "node:path" |
| 3 | +import { fileURLToPath } from "node:url" |
| 4 | +import nextEnv from "@next/env" |
| 5 | +import { getPayload } from "payload" |
| 6 | +import type { Payload } from "payload" |
| 7 | +import { importPages, normalizeImportLocale, normalizePageImportDocs } from "../src/lib/importPages" |
17 | 8 |
|
18 | 9 | const filename = fileURLToPath(import.meta.url) |
19 | 10 | const dirname = path.dirname(filename) |
20 | 11 |
|
21 | 12 | const args = process.argv.slice(2) |
22 | | -const positional = args.filter((arg) => !arg.startsWith('--')) |
23 | | -const inputPath = path.resolve(process.cwd(), positional[0] ?? 'pages.json') |
24 | | -const localeArg = args.find((arg) => arg.startsWith('--locale=')) |
25 | | -const localeInput = localeArg?.split('=')[1] || process.env.PAYLOAD_IMPORT_LOCALE || 'en' |
26 | | -const dryRun = args.includes('--dry-run') |
27 | | - |
28 | | -const systemFields = new Set(['id', 'createdAt', 'updatedAt']) |
29 | | -const supportedLocales = new Set(['en', 'de']) |
30 | | - |
31 | | -if (!supportedLocales.has(localeInput)) { |
32 | | - throw new Error(`Unsupported locale "${localeInput}". Expected "en" or "de".`) |
33 | | -} |
34 | | - |
35 | | -const locale = localeInput as ImportLocale |
| 13 | +const positional = args.filter((arg) => !arg.startsWith("--")) |
| 14 | +const inputPath = path.resolve(process.cwd(), positional[0] ?? "pages.json") |
| 15 | +const localeArg = args.find((arg) => arg.startsWith("--locale=")) |
| 16 | +const locale = normalizeImportLocale(localeArg?.split("=")[1] || process.env.PAYLOAD_IMPORT_LOCALE || "en") |
| 17 | +const dryRun = args.includes("--dry-run") |
36 | 18 |
|
37 | 19 | const toErrorDetails = (error: unknown) => { |
38 | 20 | if (error instanceof Error) { |
39 | 21 | return { |
40 | 22 | message: error.message, |
41 | 23 | name: error.name, |
42 | 24 | stack: error.stack, |
43 | | - ...(typeof error === 'object' ? error : {}), |
| 25 | + ...(typeof error === "object" ? error : {}), |
44 | 26 | } |
45 | 27 | } |
46 | 28 |
|
47 | 29 | return error |
48 | 30 | } |
49 | 31 |
|
50 | | -const normalizeDocs = (raw: unknown): ImportPage[] => { |
51 | | - if (Array.isArray(raw)) { |
52 | | - return raw as ImportPage[] |
53 | | - } |
54 | | - |
55 | | - if ( |
56 | | - raw && |
57 | | - typeof raw === 'object' && |
58 | | - 'docs' in raw && |
59 | | - Array.isArray((raw as { docs?: unknown }).docs) |
60 | | - ) { |
61 | | - return (raw as { docs: ImportPage[] }).docs |
62 | | - } |
63 | | - |
64 | | - throw new Error('Expected pages.json to contain an array or an object with a docs array.') |
65 | | -} |
66 | | - |
67 | | -const removeSystemFields = (doc: ImportPage) => { |
68 | | - return Object.fromEntries(Object.entries(doc).filter(([key]) => !systemFields.has(key))) |
69 | | -} |
70 | | - |
71 | | -const isRecord = (value: unknown): value is Record<string, unknown> => { |
72 | | - return Boolean(value) && typeof value === 'object' && !Array.isArray(value) |
73 | | -} |
74 | | - |
75 | | -const isPayloadRelationshipObject = (value: Record<string, unknown>) => { |
76 | | - return ( |
77 | | - 'id' in value && |
78 | | - !('blockType' in value) && |
79 | | - ( |
80 | | - 'relationTo' in value || |
81 | | - 'filename' in value || |
82 | | - 'mimeType' in value || |
83 | | - 'filesize' in value |
84 | | - ) |
85 | | - ) |
86 | | -} |
87 | | - |
88 | | -const isLocalizedValue = (value: Record<string, unknown>) => { |
89 | | - const keys = Object.keys(value) |
90 | | - |
91 | | - return keys.length > 0 && keys.every((key) => supportedLocales.has(key)) |
92 | | -} |
93 | | - |
94 | | -const normalizeRelationships = (value: unknown): unknown => { |
95 | | - if (Array.isArray(value)) { |
96 | | - return value.map((item) => normalizeRelationships(item)) |
97 | | - } |
98 | | - |
99 | | - if (!isRecord(value)) { |
100 | | - return value |
101 | | - } |
102 | | - |
103 | | - if (isPayloadRelationshipObject(value)) { |
104 | | - return value.id |
105 | | - } |
106 | | - |
107 | | - if (isLocalizedValue(value)) { |
108 | | - return normalizeRelationships(value[locale] ?? value.en ?? value.de) |
109 | | - } |
110 | | - |
111 | | - return Object.fromEntries( |
112 | | - Object.entries(value).map(([key, nestedValue]) => [key, normalizeRelationships(nestedValue)]), |
113 | | - ) |
114 | | -} |
115 | | - |
116 | 32 | const main = async () => { |
117 | 33 | nextEnv.loadEnvConfig(process.cwd()) |
118 | 34 |
|
119 | | - const { default: config } = await import('../src/payload.config') |
120 | | - const raw = await fs.readFile(inputPath, 'utf8') |
121 | | - const docs = normalizeDocs(JSON.parse(raw)) |
| 35 | + const { default: config } = await import("../src/payload.config") |
| 36 | + const raw = await fs.readFile(inputPath, "utf8") |
| 37 | + const docs = normalizePageImportDocs(JSON.parse(raw)) |
122 | 38 | let payload: Payload | undefined |
123 | | - const results: Array<{ id?: number | string; slug?: string; status: string; error?: unknown }> = [] |
124 | 39 |
|
125 | 40 | try { |
126 | 41 | payload = await getPayload({ config }) |
| 42 | + const result = await importPages({ |
| 43 | + docs, |
| 44 | + dryRun, |
| 45 | + locale, |
| 46 | + payload, |
| 47 | + }) |
| 48 | + |
| 49 | + console.log( |
| 50 | + JSON.stringify( |
| 51 | + { |
| 52 | + file: path.relative(path.resolve(dirname, ".."), inputPath), |
| 53 | + ...result, |
| 54 | + }, |
| 55 | + null, |
| 56 | + 2, |
| 57 | + ), |
| 58 | + ) |
127 | 59 |
|
128 | | - for (const doc of docs) { |
129 | | - if (!doc.slug || typeof doc.slug !== 'string') { |
130 | | - results.push({ |
131 | | - id: doc.id, |
132 | | - status: 'rejected', |
133 | | - error: 'Missing required string field: slug', |
134 | | - }) |
135 | | - continue |
136 | | - } |
137 | | - |
138 | | - const data = normalizeRelationships(removeSystemFields(doc)) as any |
139 | | - |
140 | | - try { |
141 | | - const existing = await payload.find({ |
142 | | - collection: 'pages', |
143 | | - depth: 0, |
144 | | - limit: 1, |
145 | | - locale, |
146 | | - overrideAccess: true, |
147 | | - where: { |
148 | | - slug: { |
149 | | - equals: doc.slug, |
150 | | - }, |
151 | | - }, |
152 | | - }) |
153 | | - |
154 | | - if (dryRun) { |
155 | | - results.push({ |
156 | | - id: existing.docs[0]?.id ?? doc.id, |
157 | | - slug: doc.slug, |
158 | | - status: existing.totalDocs > 0 ? 'would-update' : 'would-create', |
159 | | - }) |
160 | | - continue |
161 | | - } |
162 | | - |
163 | | - if (existing.docs[0]) { |
164 | | - const updated = await payload.update({ |
165 | | - id: existing.docs[0].id, |
166 | | - collection: 'pages', |
167 | | - data, |
168 | | - locale, |
169 | | - overrideAccess: true, |
170 | | - }) |
171 | | - |
172 | | - results.push({ id: updated.id, slug: doc.slug, status: 'updated' }) |
173 | | - } else { |
174 | | - const created = await payload.create({ |
175 | | - collection: 'pages', |
176 | | - data, |
177 | | - locale, |
178 | | - overrideAccess: true, |
179 | | - }) |
180 | | - |
181 | | - results.push({ id: created.id, slug: doc.slug, status: 'created' }) |
182 | | - } |
183 | | - } catch (error) { |
184 | | - results.push({ |
185 | | - id: doc.id, |
186 | | - slug: doc.slug, |
187 | | - status: 'rejected', |
188 | | - error: toErrorDetails(error), |
189 | | - }) |
190 | | - } |
| 60 | + if (result.rejected > 0) { |
| 61 | + process.exitCode = 1 |
191 | 62 | } |
192 | 63 | } finally { |
193 | 64 | await payload?.db?.destroy?.() |
194 | 65 | } |
195 | | - |
196 | | - const rejected = results.filter((result) => result.status === 'rejected') |
197 | | - |
198 | | - console.log( |
199 | | - JSON.stringify( |
200 | | - { |
201 | | - file: path.relative(path.resolve(dirname, '..'), inputPath), |
202 | | - locale, |
203 | | - dryRun, |
204 | | - total: results.length, |
205 | | - rejected: rejected.length, |
206 | | - results, |
207 | | - }, |
208 | | - null, |
209 | | - 2, |
210 | | - ), |
211 | | - ) |
212 | | - |
213 | | - if (rejected.length > 0) { |
214 | | - process.exitCode = 1 |
215 | | - } |
216 | 66 | } |
217 | 67 |
|
218 | 68 | main().catch((error) => { |
|
0 commit comments