@artinstack/migrator 0.1.9 → 0.1.12

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 (34) hide show
  1. package/dist/{bundle-B3XS20r_.d.ts → bundle-CysqqLij.d.ts} +1 -1
  2. package/dist/chunk-J7EUSPEA.js +667 -0
  3. package/dist/chunk-J7EUSPEA.js.map +1 -0
  4. package/dist/{chunk-3YJFSTYR.js → chunk-MUFGDYGI.js} +2 -1
  5. package/dist/chunk-MUFGDYGI.js.map +1 -0
  6. package/dist/{chunk-KTQGOM45.js → chunk-PFUXPS7A.js} +1 -1
  7. package/dist/chunk-PFUXPS7A.js.map +1 -0
  8. package/dist/{chunk-FB3MMCHY.js → chunk-Q44KGFIH.js} +97 -531
  9. package/dist/chunk-Q44KGFIH.js.map +1 -0
  10. package/dist/chunk-QFJXNEXG.js +355 -0
  11. package/dist/chunk-QFJXNEXG.js.map +1 -0
  12. package/dist/{chunk-PPT5RIZ4.js → chunk-VRRYN6NS.js} +40 -236
  13. package/dist/chunk-VRRYN6NS.js.map +1 -0
  14. package/dist/{chunk-S4SUJT2D.js → chunk-WCAHVNWW.js} +98 -3
  15. package/dist/chunk-WCAHVNWW.js.map +1 -0
  16. package/dist/cli/index.js +12 -6
  17. package/dist/cli/index.js.map +1 -1
  18. package/dist/index.d.ts +4 -4
  19. package/dist/index.js +9 -6
  20. package/dist/normalizer/index.d.ts +10 -4
  21. package/dist/normalizer/index.js +2 -2
  22. package/dist/sinks/index.d.ts +13 -3
  23. package/dist/sinks/index.js +5 -3
  24. package/dist/transformers/index.d.ts +1 -1
  25. package/dist/transformers/index.js +3 -2
  26. package/dist/{types-Ce4r6zqt.d.ts → types-CLNmloya.d.ts} +15 -1
  27. package/package.json +1 -1
  28. package/dist/chunk-3YJFSTYR.js.map +0 -1
  29. package/dist/chunk-BONZ3U3I.js +0 -124
  30. package/dist/chunk-BONZ3U3I.js.map +0 -1
  31. package/dist/chunk-FB3MMCHY.js.map +0 -1
  32. package/dist/chunk-KTQGOM45.js.map +0 -1
  33. package/dist/chunk-PPT5RIZ4.js.map +0 -1
  34. package/dist/chunk-S4SUJT2D.js.map +0 -1
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  SquarespaceCollectionClient,
3
- WORDPRESS_BUILDER_REGISTRY,
4
- WORDPRESS_WIDGET_REGISTRY,
5
- WP_WIDGET_PLACEHOLDER,
6
3
  enumerateSquarespaceEntities,
7
4
  summarizeSquarespaceExport,
8
5
  validateSquarespaceExportFile
9
- } from "./chunk-PPT5RIZ4.js";
6
+ } from "./chunk-VRRYN6NS.js";
7
+ import {
8
+ flattenWordPressBuilders
9
+ } from "./chunk-J7EUSPEA.js";
10
10
  import {
11
11
  stampMigrationMediaRefs
12
- } from "./chunk-BONZ3U3I.js";
12
+ } from "./chunk-QFJXNEXG.js";
13
13
  import {
14
14
  linkToPath,
15
15
  sanitizeSlug
@@ -19,7 +19,6 @@ import {
19
19
  canonicalizeInlineAssetUrl,
20
20
  discoverContentAssetUrls,
21
21
  discoverContentAssets,
22
- normalizeAssetUrl,
23
22
  parseMigrationMediaRef,
24
23
  resolveFeaturedContentAssetUrl,
25
24
  rewriteOriginUrlsInText
@@ -29,523 +28,6 @@ import {
29
28
  import { readFile } from "fs/promises";
30
29
  import { basename } from "path";
31
30
  import { XMLParser } from "fast-xml-parser";
32
-
33
- // src/parsers/wordpress/builders/flatten.ts
34
- function escapeRegExp(value) {
35
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
36
- }
37
- function extractQuotedParam(params, name) {
38
- const pattern = new RegExp(`\\b${escapeRegExp(name)}\\s*=\\s*`, "i");
39
- const match = pattern.exec(params);
40
- if (!match) return void 0;
41
- let index = match.index + match[0].length;
42
- while (index < params.length && /\s/.test(params[index])) index += 1;
43
- const quote = params[index];
44
- if (quote !== '"' && quote !== "'") return void 0;
45
- index += 1;
46
- let value = "";
47
- while (index < params.length) {
48
- const char = params[index];
49
- if (char === "\\" && index + 1 < params.length) {
50
- value += params[index + 1];
51
- index += 2;
52
- continue;
53
- }
54
- if (char === quote) break;
55
- value += char;
56
- index += 1;
57
- }
58
- const trimmed = value.trim();
59
- return trimmed || void 0;
60
- }
61
- function escapeLayoutAttr(value) {
62
- return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
63
- }
64
- function parseFractionWidth(fraction) {
65
- if (!fraction?.trim()) return void 0;
66
- const trimmed = fraction.trim();
67
- const match = trimmed.match(/^(\d+)\s*\/\s*(\d+)$/);
68
- if (!match) return void 0;
69
- const numerator = Number(match[1]);
70
- const denominator = Number(match[2]);
71
- if (!Number.isFinite(numerator) || !Number.isFinite(denominator) || denominator <= 0) {
72
- return void 0;
73
- }
74
- const percent = numerator / denominator * 100;
75
- const rounded = Math.round(percent * 100) / 100;
76
- return `${rounded % 1 === 0 ? rounded.toFixed(0) : rounded}%`;
77
- }
78
- function parseRowLayoutCols(layout) {
79
- if (!layout?.trim()) return void 0;
80
- const parts = layout.split("+").map((part) => part.trim()).filter(Boolean);
81
- return parts.length > 1 ? parts.length : void 0;
82
- }
83
- function openSectionDiv(params, bgParamName) {
84
- const attrs = ['data-layout="section"'];
85
- const bgImage = extractQuotedParam(params, bgParamName ?? "bg_image");
86
- if (bgImage?.startsWith("http")) {
87
- attrs.push(`data-bg-image="${escapeLayoutAttr(bgImage)}"`);
88
- }
89
- return `<div ${attrs.join(" ")}>`;
90
- }
91
- function openRowDiv(params, colsParamName) {
92
- const attrs = ['data-layout="row"'];
93
- const cols = parseRowLayoutCols(extractQuotedParam(params, colsParamName ?? "layout"));
94
- if (cols) attrs.push(`data-cols="${cols}"`);
95
- return `<div ${attrs.join(" ")}>`;
96
- }
97
- function openColumnDiv(params, widthParamName) {
98
- const attrs = ['data-layout="column"'];
99
- const width = parseFractionWidth(extractQuotedParam(params, widthParamName ?? "width"));
100
- if (width) attrs.push(`data-col-width="${width}"`);
101
- return `<div ${attrs.join(" ")}>`;
102
- }
103
- function applyPrefixedLayoutMap(content, map) {
104
- let html = content;
105
- html = html.replace(map.sectionRegex, (_, params) => openSectionDiv(params, map.bgParamName));
106
- html = html.replace(map.sectionCloseRegex, "</div>");
107
- html = html.replace(map.rowRegex, (_, params) => openRowDiv(params, map.colsParamName));
108
- html = html.replace(map.rowCloseRegex, "</div>");
109
- html = html.replace(map.columnRegex, '<div data-layout="column">');
110
- html = html.replace(map.columnCloseRegex, "</div>");
111
- return html;
112
- }
113
- function applyFractionalLayoutMap(content, map) {
114
- let html = content;
115
- html = html.replace(map.sectionRegex, (_, params) => openSectionDiv(params, map.bgParamName));
116
- html = html.replace(map.sectionCloseRegex, "</div>");
117
- html = html.replace(map.rowRegex, (_, params) => openRowDiv(params));
118
- html = html.replace(map.rowCloseRegex, "</div>");
119
- for (let index = 0; index < map.columnTokens.length; index += 1) {
120
- const token = map.columnTokens[index];
121
- const width = map.columnWidths[token];
122
- const openRegex = map.columnOpenRegexes[index];
123
- const closeRegex = map.columnCloseRegexes[index];
124
- html = html.replace(openRegex, () => {
125
- const attrs = ['data-layout="column"'];
126
- if (width) attrs.push(`data-col-width="${width}"`);
127
- return `<div ${attrs.join(" ")}>`;
128
- });
129
- html = html.replace(closeRegex, "</div>");
130
- }
131
- return html;
132
- }
133
- function applyExtendedPrefixedLayoutMap(content, map) {
134
- let html = content;
135
- const levels = [...map.levels].sort((left, right) => {
136
- const leftMax = Math.max(...left.tokens.map((token) => token.length));
137
- const rightMax = Math.max(...right.tokens.map((token) => token.length));
138
- return rightMax - leftMax;
139
- });
140
- for (const level of levels) {
141
- const tokens = [...level.tokens].sort((left, right) => right.length - left.length);
142
- for (const token of tokens) {
143
- const openRegex = new RegExp(`\\[${escapeRegExp(token)}\\b([^\\]]*)\\]`, "gi");
144
- const closeRegex = new RegExp(`\\[\\/${escapeRegExp(token)}\\b[^\\]]*\\]`, "gi");
145
- html = html.replace(openRegex, (_, params) => {
146
- switch (level.role) {
147
- case "section":
148
- return openSectionDiv(params, level.bgParamName);
149
- case "row":
150
- return openRowDiv(params, level.colsParamName);
151
- case "column":
152
- return openColumnDiv(params, level.widthParamName);
153
- }
154
- });
155
- html = html.replace(closeRegex, "</div>");
156
- }
157
- }
158
- return html;
159
- }
160
- function applyStructuralLayoutMap(content, map) {
161
- switch (map.kind) {
162
- case "prefixed":
163
- return applyPrefixedLayoutMap(content, map);
164
- case "fractional":
165
- return applyFractionalLayoutMap(content, map);
166
- case "extended-prefixed":
167
- return applyExtendedPrefixedLayoutMap(content, map);
168
- }
169
- }
170
- function collectLayoutMaps(theme) {
171
- const maps = [];
172
- if (theme.layoutMap) maps.push(theme.layoutMap);
173
- if (theme.layoutMaps?.length) maps.push(...theme.layoutMaps);
174
- return maps;
175
- }
176
- function extractShortcodeParam(params, names) {
177
- for (const name of names) {
178
- const value = extractQuotedParam(params, name);
179
- if (value) return value;
180
- }
181
- return void 0;
182
- }
183
- function escapeHtmlText(text) {
184
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
185
- }
186
- function textToHtml(text, tag) {
187
- const paragraphs = text.split(/\n{2,}/).map((part) => part.trim()).filter(Boolean);
188
- if (paragraphs.length === 0) return "";
189
- return paragraphs.map((paragraph) => {
190
- const inner = escapeHtmlText(paragraph).replace(/\n/g, "<br />");
191
- return `<${tag}>${inner}</${tag}>`;
192
- }).join("\n");
193
- }
194
- function emitHtmlTag(tag, url) {
195
- const normalized = normalizeAssetUrl(url) ?? url;
196
- const escaped = normalized.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
197
- switch (tag) {
198
- case "img":
199
- return `<img src="${escaped}" alt="" />`;
200
- case "video":
201
- return `<video src="${escaped}" controls></video>`;
202
- case "iframe":
203
- return `<iframe src="${escaped}" loading="lazy"></iframe>`;
204
- }
205
- }
206
- function convertUrlRule(content, rule) {
207
- const prefix = escapeRegExp(rule.shortcodePrefix);
208
- const pattern = new RegExp(
209
- `\\[${prefix}\\b([^\\]]*)\\]\\s*(?:\\[\\/${prefix}\\b[^\\]]*\\])?`,
210
- "gi"
211
- );
212
- return content.replace(pattern, (block, params) => {
213
- const url = extractShortcodeParam(params, rule.urlParams);
214
- if (!url) return block;
215
- return emitHtmlTag(rule.tag, url);
216
- });
217
- }
218
- function convertTextRule(content, rule) {
219
- const prefix = escapeRegExp(rule.shortcodePrefix);
220
- const pattern = new RegExp(
221
- `\\[${prefix}\\b([^\\]]*)\\]\\s*(?:\\[\\/${prefix}\\b[^\\]]*\\])?`,
222
- "gis"
223
- );
224
- return content.replace(pattern, (block, params) => {
225
- const parts = [];
226
- for (const field of rule.fields) {
227
- const text = extractQuotedParam(params, field.param);
228
- if (!text) continue;
229
- const html = textToHtml(text, field.tag);
230
- if (html) parts.push(html);
231
- }
232
- return parts.length > 0 ? parts.join("\n") : block;
233
- });
234
- }
235
- function convertWrapperRule(content, rule) {
236
- const prefix = escapeRegExp(rule.shortcodePrefix);
237
- const pattern = new RegExp(
238
- `\\[${prefix}\\b([^\\]]*)\\]([\\s\\S]*?)\\[\\/${prefix}\\b[^\\]]*\\]`,
239
- "gi"
240
- );
241
- return content.replace(pattern, (_, params, inner) => {
242
- const parts = [];
243
- if (rule.urlParams?.length) {
244
- const url = extractShortcodeParam(params, rule.urlParams);
245
- if (url) parts.push(emitHtmlTag("img", url));
246
- }
247
- parts.push(inner.trim());
248
- return parts.filter(Boolean).join("\n");
249
- });
250
- }
251
- function convertIconImageRule(content, rule) {
252
- const prefix = escapeRegExp(rule.shortcodePrefix);
253
- const pattern = new RegExp(
254
- `\\[${prefix}\\b([^\\]]*)\\]\\s*(?:\\[\\/${prefix}\\b[^\\]]*\\])?`,
255
- "gi"
256
- );
257
- return content.replace(pattern, (_, params) => {
258
- const iconImage = extractQuotedParam(params, rule.imageParam);
259
- if (!iconImage?.startsWith("http") || iconImage.includes("placehold")) {
260
- return "";
261
- }
262
- const img = emitHtmlTag("img", iconImage);
263
- if (rule.hrefParam) {
264
- const href = extractQuotedParam(params, rule.hrefParam);
265
- if (href?.startsWith("http")) {
266
- const escapedHref = href.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
267
- return `<a href="${escapedHref}">${img}</a>`;
268
- }
269
- }
270
- return img;
271
- });
272
- }
273
- function convertPlaceholderRule(content, rule) {
274
- const prefix = escapeRegExp(rule.shortcodePrefix);
275
- const pattern = new RegExp(
276
- `\\[${prefix}\\b([^\\]]*)\\]\\s*(?:\\[\\/${prefix}\\b[^\\]]*\\])?`,
277
- "gi"
278
- );
279
- return content.replace(pattern, rule.html);
280
- }
281
- function stripScaffoldingPrefix(content, prefix) {
282
- const escaped = escapeRegExp(prefix);
283
- const opener = new RegExp(`\\[${escaped}[a-z0-9_-]*[^\\]]*\\]`, "gi");
284
- const closer = new RegExp(`\\[\\/${escaped}[a-z0-9_-]*[^\\]]*\\]`, "gi");
285
- return content.replace(opener, "").replace(closer, "");
286
- }
287
- function stripLegacyTokens(content, tokens) {
288
- let result = content;
289
- for (const token of tokens) {
290
- const escaped = escapeRegExp(token);
291
- const opener = new RegExp(`\\[${escaped}\\b[^\\]]*\\]`, "gi");
292
- const closer = new RegExp(`\\[\\/${escaped}\\b[^\\]]*\\]`, "gi");
293
- result = result.replace(opener, "").replace(closer, "");
294
- }
295
- return result;
296
- }
297
- function detectThemes(content, registry) {
298
- return registry.filter((theme) => theme.detect.test(content));
299
- }
300
- function extractBareOrQuotedParam(params, name) {
301
- const quoted = extractQuotedParam(params, name);
302
- if (quoted) return quoted;
303
- const pattern = new RegExp(`\\b${escapeRegExp(name)}\\s*=\\s*([^\\s"'\\]]+)`, "i");
304
- const match = pattern.exec(params);
305
- return match?.[1]?.trim() || void 0;
306
- }
307
- function emitWidgetStub(widget, attrs, tag = "div") {
308
- const parts = [`data-wp-widget="${escapeLayoutAttr(widget)}"`];
309
- for (const [key, value] of Object.entries(attrs)) {
310
- if (value) parts.push(`${key}="${escapeLayoutAttr(value)}"`);
311
- }
312
- return `<${tag} ${parts.join(" ")}>${WP_WIDGET_PLACEHOLDER}</${tag}>`;
313
- }
314
- function normalizeVideoEmbedUrl(raw) {
315
- const trimmed = raw.trim();
316
- if (!trimmed || trimmed.startsWith("data:")) return void 0;
317
- try {
318
- const url = new URL(trimmed.startsWith("//") ? `https:${trimmed}` : trimmed);
319
- const host = url.hostname.replace(/^www\./, "").replace(/^m\./, "");
320
- if (host === "youtu.be") {
321
- const id = url.pathname.split("/").filter(Boolean)[0];
322
- if (id) {
323
- return { provider: "youtube", embedUrl: `https://www.youtube-nocookie.com/embed/${id}` };
324
- }
325
- }
326
- if (host === "youtube.com" || host === "youtube-nocookie.com") {
327
- const embedMatch = url.pathname.match(/\/embed\/([^/?#]+)/);
328
- if (embedMatch?.[1]) {
329
- const start = url.searchParams.get("start");
330
- const suffix = start ? `?start=${start}` : "";
331
- return {
332
- provider: "youtube",
333
- embedUrl: `https://www.youtube-nocookie.com/embed/${embedMatch[1]}${suffix}`
334
- };
335
- }
336
- const videoId = url.searchParams.get("v");
337
- if (videoId) {
338
- const t = url.searchParams.get("t") ?? url.searchParams.get("start");
339
- const startSeconds = t?.endsWith("s") ? t.slice(0, -1) : t;
340
- const suffix = startSeconds ? `?start=${startSeconds}` : "";
341
- return {
342
- provider: "youtube",
343
- embedUrl: `https://www.youtube-nocookie.com/embed/${videoId}${suffix}`
344
- };
345
- }
346
- }
347
- if (host === "vimeo.com") {
348
- const segments = url.pathname.split("/").filter(Boolean);
349
- const id = segments[segments.length - 1];
350
- if (id && /^\d+$/.test(id)) {
351
- return { provider: "vimeo", embedUrl: `https://player.vimeo.com/video/${id}` };
352
- }
353
- }
354
- if (host === "player.vimeo.com") {
355
- const match = url.pathname.match(/\/video\/(\d+)/);
356
- if (match?.[1]) {
357
- return { provider: "vimeo", embedUrl: `https://player.vimeo.com/video/${match[1]}` };
358
- }
359
- }
360
- } catch {
361
- return void 0;
362
- }
363
- return void 0;
364
- }
365
- function emitVideoWidgetFromParams(params, inner) {
366
- const url = extractShortcodeParam(params, ["url", "src", "video", "link", "youtube_url", "vimeo_url"]) ?? inner.trim().match(/^https?:\/\/\S+/)?.[0];
367
- if (!url) {
368
- return emitWidgetStub("video", { "data-video-provider": "external" });
369
- }
370
- const normalized = normalizeVideoEmbedUrl(url);
371
- if (normalized) {
372
- return emitWidgetStub("video", {
373
- "data-video-provider": normalized.provider,
374
- "data-embed-url": normalized.embedUrl
375
- });
376
- }
377
- if (/\.(mp4|webm|ogg)(\?|#|$)/i.test(url)) {
378
- return emitHtmlTag("video", url);
379
- }
380
- return emitWidgetStub("video", {
381
- "data-video-provider": "external",
382
- "data-embed-url": url
383
- });
384
- }
385
- function flattenMapShortcodes(content, widgetRegistry) {
386
- let html = content;
387
- for (const prefix of widgetRegistry.mapShortcodePrefixes) {
388
- const pattern = new RegExp(
389
- `\\[${escapeRegExp(prefix)}\\b([^\\]]*)\\]\\s*(?:\\[\\/${escapeRegExp(prefix)}\\b[^\\]]*\\])?`,
390
- "gi"
391
- );
392
- html = html.replace(pattern, (_, params) => {
393
- const embedUrl = extractShortcodeParam(params, ["embed_url", "url", "src", "map_url"]);
394
- const query = extractBareOrQuotedParam(params, "address") ?? extractBareOrQuotedParam(params, "q");
395
- return emitWidgetStub("map", {
396
- ...embedUrl?.includes("google.com/maps") ? { "data-embed-url": embedUrl } : {},
397
- ...query && !embedUrl ? { "data-wp-map-query": query } : {}
398
- });
399
- });
400
- }
401
- return html;
402
- }
403
- function flattenContactFormShortcodes(content, widgetRegistry) {
404
- let html = content;
405
- for (const rule of widgetRegistry.contactFormRules) {
406
- const pattern = new RegExp(
407
- `\\[${escapeRegExp(rule.tag)}\\b([^\\]]*)\\]\\s*(?:\\[\\/${escapeRegExp(rule.tag)}\\b[^\\]]*\\])?`,
408
- "gi"
409
- );
410
- html = html.replace(pattern, (_, params) => {
411
- const id = extractBareOrQuotedParam(params, rule.idParam);
412
- return emitWidgetStub(
413
- "contact-form",
414
- {
415
- "data-wp-form-source": rule.source,
416
- ...id ? { "data-wp-form-id": id } : {}
417
- },
418
- "section"
419
- );
420
- });
421
- }
422
- return html;
423
- }
424
- function emitInlineGalleryFromIds(idList) {
425
- const images = idList.map((id) => `<img data-wp-attachment-id="${escapeLayoutAttr(id)}" alt="" />`).join("");
426
- return `<figure data-wp-inline-gallery>${images}</figure>`;
427
- }
428
- function parseGalleryAttachmentIds(params) {
429
- const ids = extractBareOrQuotedParam(params, "ids");
430
- const idList = ids?.split(",").map((part) => part.trim()).filter((part) => /^\d+$/.test(part));
431
- return idList?.length ? idList : void 0;
432
- }
433
- function flattenIdGalleryShortcode(content, tag) {
434
- const escaped = escapeRegExp(tag);
435
- const pattern = new RegExp(`\\[${escaped}\\b([^\\]]*)\\](?:\\s*\\[\\/${escaped}\\])?`, "gi");
436
- return content.replace(pattern, (fullMatch, params) => {
437
- const idList = parseGalleryAttachmentIds(params);
438
- if (idList?.length) {
439
- return emitInlineGalleryFromIds(idList);
440
- }
441
- return fullMatch;
442
- });
443
- }
444
- function flattenGalleryShortcodes(content, widgetRegistry) {
445
- const tag = escapeRegExp(widgetRegistry.galleryShortcode);
446
- const pattern = new RegExp(`\\[${tag}\\b([^\\]]*)\\](?:\\s*\\[\\/${tag}\\])?`, "gi");
447
- return content.replace(pattern, (_, params) => {
448
- const idList = parseGalleryAttachmentIds(params);
449
- if (idList?.length) {
450
- return emitInlineGalleryFromIds(idList);
451
- }
452
- const category = extractBareOrQuotedParam(params, "category") ?? extractBareOrQuotedParam(params, "type");
453
- return emitWidgetStub("portfolio", {
454
- "data-wp-gallery-dynamic": "1",
455
- ...category ? { "data-wp-portfolio-category": category } : {}
456
- });
457
- });
458
- }
459
- function flattenIdBasedGalleryShortcodes(content, widgetRegistry) {
460
- let html = content;
461
- for (const tag of widgetRegistry.idGalleryShortcodes) {
462
- html = flattenIdGalleryShortcode(html, tag);
463
- }
464
- return html;
465
- }
466
- function flattenPortfolioShortcodes(content, widgetRegistry) {
467
- const tag = escapeRegExp(widgetRegistry.portfolioShortcode);
468
- const pattern = new RegExp(`\\[${tag}\\b([^\\]]*)\\](?:\\s*\\[\\/${tag}\\])?`, "gi");
469
- return content.replace(pattern, (_, params) => {
470
- const category = extractBareOrQuotedParam(params, "category");
471
- const slug = extractBareOrQuotedParam(params, "slug");
472
- return emitWidgetStub("portfolio", {
473
- ...category ? { "data-wp-portfolio-category": category } : {},
474
- ...slug ? { "data-wp-portfolio-slug": slug } : {}
475
- });
476
- });
477
- }
478
- function flattenVideoShortcodes(content, widgetRegistry) {
479
- let html = content;
480
- for (const prefix of widgetRegistry.videoShortcodePrefixes) {
481
- const wrapped = new RegExp(
482
- `\\[${escapeRegExp(prefix)}\\b([^\\]]*)\\]([\\s\\S]*?)\\[\\/${escapeRegExp(prefix)}\\b[^\\]]*\\]`,
483
- "gi"
484
- );
485
- html = html.replace(
486
- wrapped,
487
- (_, params, inner) => emitVideoWidgetFromParams(params, inner)
488
- );
489
- const selfClosing = new RegExp(
490
- `\\[${escapeRegExp(prefix)}\\b([^\\]]*)\\]`,
491
- "gi"
492
- );
493
- html = html.replace(selfClosing, (_, params) => emitVideoWidgetFromParams(params, ""));
494
- }
495
- return html;
496
- }
497
- function flattenWordPressWidgets(content, widgetRegistry = WORDPRESS_WIDGET_REGISTRY) {
498
- let html = content;
499
- html = flattenGalleryShortcodes(html, widgetRegistry);
500
- html = flattenIdBasedGalleryShortcodes(html, widgetRegistry);
501
- html = flattenPortfolioShortcodes(html, widgetRegistry);
502
- html = flattenMapShortcodes(html, widgetRegistry);
503
- html = flattenContactFormShortcodes(html, widgetRegistry);
504
- html = flattenVideoShortcodes(html, widgetRegistry);
505
- return html;
506
- }
507
- function flattenWordPressBuilders(content, options = {}) {
508
- if (!content.trim()) {
509
- return { html: content, detectedThemes: [] };
510
- }
511
- const registry = options.registry ?? WORDPRESS_BUILDER_REGISTRY;
512
- const themes = detectThemes(content, registry);
513
- const widgetRegistry = options.widgetRegistry ?? WORDPRESS_WIDGET_REGISTRY;
514
- let html = flattenWordPressWidgets(content, widgetRegistry);
515
- for (const theme of themes) {
516
- for (const rule of theme.wrapperRules ?? []) {
517
- html = convertWrapperRule(html, rule);
518
- }
519
- for (const rule of theme.textRules ?? []) {
520
- html = convertTextRule(html, rule);
521
- }
522
- for (const rule of theme.urlRules ?? []) {
523
- html = convertUrlRule(html, rule);
524
- }
525
- for (const rule of theme.placeholderRules ?? []) {
526
- html = convertPlaceholderRule(html, rule);
527
- }
528
- for (const rule of theme.iconImageRules ?? []) {
529
- html = convertIconImageRule(html, rule);
530
- }
531
- for (const layoutMap of collectLayoutMaps(theme)) {
532
- html = applyStructuralLayoutMap(html, layoutMap);
533
- }
534
- for (const prefix of theme.scaffoldingPrefixes ?? []) {
535
- html = stripScaffoldingPrefix(html, prefix);
536
- }
537
- if (theme.legacyScaffoldingTokens?.length) {
538
- html = stripLegacyTokens(html, theme.legacyScaffoldingTokens);
539
- }
540
- }
541
- html = html.replace(/\n{3,}/g, "\n\n").trim();
542
- return {
543
- html,
544
- detectedThemes: themes.map((theme) => theme.id)
545
- };
546
- }
547
-
548
- // src/parsers/wordpress/parse-wxr.ts
549
31
  var PLATFORM = "wordpress";
550
32
  var DEFAULT_WORDPRESS_PORTFOLIO_CPT_SLUGS = ["portfolio"];
551
33
  var WOOCOMMERCE_STUB_PAGE_SLUGS = /* @__PURE__ */ new Set(["cart", "checkout", "my-account"]);
@@ -610,6 +92,53 @@ function isPortfolioCptPostType(postType, portfolioCptSlugs) {
610
92
  function countWxrPortfolioCptItems(items, portfolioCptSlugs = new Set(DEFAULT_WORDPRESS_PORTFOLIO_CPT_SLUGS)) {
611
93
  return items.filter((item) => isPortfolioCptPostType(textValue(item.post_type), portfolioCptSlugs)).length;
612
94
  }
95
+ function isImportableWxrPostType(postType, portfolioCptSlugs) {
96
+ const normalized = postType.toLowerCase();
97
+ return normalized === "post" || normalized === "page" || normalized === "attachment" || isPortfolioCptPostType(normalized, portfolioCptSlugs);
98
+ }
99
+ function contentForWooStubCheck(item, options) {
100
+ let html = getContentEncoded(item);
101
+ if (options.originUrlRewrite) {
102
+ html = rewriteOriginUrlsInText(html, options.originUrlRewrite);
103
+ }
104
+ if (options.flattenBuilders !== false) {
105
+ html = flattenWordPressBuilders(html).html;
106
+ }
107
+ return html;
108
+ }
109
+ function summarizeWxrImport(items, options) {
110
+ const portfolioCptSlugs = resolvePortfolioCptSlugs(options);
111
+ let importableItemCount = 0;
112
+ let skippedWooCommerceStubPages = 0;
113
+ const skippedPostTypes = {};
114
+ for (const item of items) {
115
+ const postType = textValue(item.post_type) || "unknown";
116
+ const normalizedType = postType.toLowerCase();
117
+ if (isImportableWxrPostType(normalizedType, portfolioCptSlugs)) {
118
+ if (normalizedType === "page" && options.skipWooCommerceStubPages !== false && isWooCommerceStubPage(
119
+ sanitizeSlug(textValue(item.post_name) || textValue(item.title) || textValue(item.post_id)),
120
+ contentForWooStubCheck(item, options)
121
+ )) {
122
+ skippedWooCommerceStubPages++;
123
+ continue;
124
+ }
125
+ importableItemCount++;
126
+ continue;
127
+ }
128
+ skippedPostTypes[normalizedType] = (skippedPostTypes[normalizedType] ?? 0) + 1;
129
+ }
130
+ const skippedUnsupported = Object.values(skippedPostTypes).reduce((sum, count) => sum + count, 0);
131
+ return {
132
+ importableItemCount,
133
+ unsupportedOnly: importableItemCount === 0 && skippedUnsupported > 0,
134
+ skippedPostTypes,
135
+ ...skippedWooCommerceStubPages > 0 ? { skippedWooCommerceStubPages } : {}
136
+ };
137
+ }
138
+ async function summarizeWxrImportFromFile(filePath, options = { filePath }) {
139
+ const xml = await readFile(filePath, "utf8");
140
+ return summarizeWxrImport(parseItems(xml), options);
141
+ }
613
142
  function getExcerpt(item) {
614
143
  const excerpt = item.excerpt;
615
144
  if (!excerpt) return "";
@@ -624,6 +153,21 @@ function getPostMeta(item, key) {
624
153
  }
625
154
  return void 0;
626
155
  }
156
+ var PORTFOLIO_LISTING_PAGE_SLUGS = /* @__PURE__ */ new Set([
157
+ "portfolio",
158
+ "work",
159
+ "works",
160
+ "gallery",
161
+ "projects",
162
+ "our-work"
163
+ ]);
164
+ function inferPortfolioListingPage(item, options) {
165
+ if (options.isPortfolioCpt || options.postType !== "page") return false;
166
+ const template = (getPostMeta(item, "_wp_page_template") ?? "").trim().toLowerCase();
167
+ if (template.includes("portfolio") && template !== "default") return true;
168
+ const hasPortfolioListingWidget = /data-wp-widget=["']portfolio["']/i.test(options.contentHtml);
169
+ return hasPortfolioListingWidget && PORTFOLIO_LISTING_PAGE_SLUGS.has(options.slug.toLowerCase());
170
+ }
627
171
  function parseItems(xml) {
628
172
  const parser = new XMLParser({
629
173
  ignoreAttributes: false,
@@ -875,6 +419,12 @@ async function* enumerateWxrEntities(options) {
875
419
  yield post;
876
420
  } else {
877
421
  const isHomePage = !isPortfolioCpt && (getPostMeta(item, "_wp_show_on_front") === "1" || getPostMeta(item, "page_on_front") === "1");
422
+ const isPortfolioPage = !isPortfolioCpt && inferPortfolioListingPage(item, {
423
+ postType,
424
+ isPortfolioCpt,
425
+ slug,
426
+ contentHtml
427
+ });
878
428
  const pageSourceId = isPortfolioCpt ? portfolioCptSourceId(id) : id;
879
429
  const page = {
880
430
  type: "page",
@@ -884,13 +434,14 @@ async function* enumerateWxrEntities(options) {
884
434
  slug,
885
435
  contentHtml,
886
436
  isHomePage: isHomePage || void 0,
437
+ isPortfolioPage: isPortfolioPage || void 0,
887
438
  status: mapPublishStatus(textValue(item.status))
888
439
  };
889
440
  yield page;
890
441
  }
891
442
  }
892
443
  }
893
- async function validateWxrFile(filePath) {
444
+ async function validateWxrFile(filePath, options = { filePath }) {
894
445
  const issues = [];
895
446
  let xml;
896
447
  try {
@@ -899,7 +450,12 @@ async function validateWxrFile(filePath) {
899
450
  return {
900
451
  ok: false,
901
452
  issues: [{ code: "file_not_found", message: `Cannot read file: ${filePath}` }],
902
- summary: {}
453
+ summary: {},
454
+ importSummary: {
455
+ importableItemCount: 0,
456
+ unsupportedOnly: false,
457
+ skippedPostTypes: {}
458
+ }
903
459
  };
904
460
  }
905
461
  const looksLikeWxr = xml.includes("<rss") && (xml.includes("wp:wxr_version") || xml.includes("xmlns:wp=") || xml.includes("WordPress eXtended RSS"));
@@ -907,18 +463,20 @@ async function validateWxrFile(filePath) {
907
463
  issues.push({ code: "invalid_wxr", message: "File does not appear to be WordPress WXR" });
908
464
  }
909
465
  const items = parseItems(xml);
466
+ const importSummary = summarizeWxrImport(items, { ...options, filePath });
910
467
  const summary = {
911
468
  posts: items.filter((i) => textValue(i.post_type) === "post").length,
912
469
  pages: items.filter((i) => textValue(i.post_type) === "page").length,
913
470
  assets: items.filter((i) => textValue(i.post_type) === "attachment").length,
914
471
  portfolioCpt: countWxrPortfolioCptItems(items),
915
472
  categories: 0,
916
- tags: 0
473
+ tags: 0,
474
+ importableItemCount: importSummary.importableItemCount
917
475
  };
918
476
  const { categories, tags } = collectTaxonomies(items);
919
477
  summary.categories = categories.size;
920
478
  summary.tags = tags.size;
921
- return { ok: issues.length === 0, issues, summary };
479
+ return { ok: issues.length === 0, issues, summary, importSummary };
922
480
  }
923
481
 
924
482
  // src/parsers/wordpress/index.ts
@@ -943,14 +501,22 @@ function resolveWxrOptions(input) {
943
501
  var wordpressAdapter = {
944
502
  platform: "wordpress",
945
503
  async validateInput(input) {
946
- const { filePath } = resolveWxrOptions(input);
947
- const result = await validateWxrFile(filePath);
504
+ const options = resolveWxrOptions(input);
505
+ const result = await validateWxrFile(options.filePath, options);
948
506
  return {
949
507
  ok: result.ok,
950
508
  issues: result.issues,
951
- summary: result.summary
509
+ summary: {
510
+ ...result.summary,
511
+ unsupportedOnly: result.importSummary.unsupportedOnly,
512
+ skippedPostTypes: result.importSummary.skippedPostTypes
513
+ }
952
514
  };
953
515
  },
516
+ async getImportSummary(input) {
517
+ const options = resolveWxrOptions(input);
518
+ return summarizeWxrImportFromFile(options.filePath, options);
519
+ },
954
520
  enumerateEntities(ctx) {
955
521
  return enumerateWxrEntities(resolveWxrOptions(ctx.input));
956
522
  }
@@ -2928,4 +2494,4 @@ export {
2928
2494
  wixAdapter,
2929
2495
  getAdapter
2930
2496
  };
2931
- //# sourceMappingURL=chunk-FB3MMCHY.js.map
2497
+ //# sourceMappingURL=chunk-Q44KGFIH.js.map