@artinstack/migrator 0.1.5 → 0.1.7
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/README.md +29 -1
- package/dist/chunk-BOYB6XRA.js +175 -0
- package/dist/chunk-BOYB6XRA.js.map +1 -0
- package/dist/{chunk-XUBCG3IA.js → chunk-KF7G7DM6.js} +87 -11
- package/dist/chunk-KF7G7DM6.js.map +1 -0
- package/dist/{chunk-YLFVYPB3.js → chunk-MDSY3FEZ.js} +66 -5
- package/dist/chunk-MDSY3FEZ.js.map +1 -0
- package/dist/{chunk-CIOYDRY5.js → chunk-S4XYG4SM.js} +81 -2
- package/dist/chunk-S4XYG4SM.js.map +1 -0
- package/dist/chunk-XYP3VYDH.js +159 -0
- package/dist/chunk-XYP3VYDH.js.map +1 -0
- package/dist/cli/index.js +4 -4
- package/dist/index-Dp6nqBqe.d.ts +177 -0
- package/dist/index.d.ts +16 -4
- package/dist/index.js +29 -7
- package/dist/lib/index.d.ts +14 -4
- package/dist/lib/index.js +7 -3
- package/dist/rewrite-inline-images-Bq16bJA5.d.ts +41 -0
- package/dist/sinks/index.d.ts +2 -2
- package/dist/sinks/index.js +3 -3
- package/dist/transformers/index.d.ts +4 -157
- package/dist/transformers/index.js +10 -4
- package/package.json +1 -1
- package/dist/chunk-2PNSVE5Y.js +0 -67
- package/dist/chunk-2PNSVE5Y.js.map +0 -1
- package/dist/chunk-CIOYDRY5.js.map +0 -1
- package/dist/chunk-S7TRWILI.js +0 -71
- package/dist/chunk-S7TRWILI.js.map +0 -1
- package/dist/chunk-XUBCG3IA.js.map +0 -1
- package/dist/chunk-YLFVYPB3.js.map +0 -1
- package/dist/rewrite-inline-images-BckVKPbh.d.ts +0 -21
package/README.md
CHANGED
|
@@ -15,7 +15,8 @@ src/
|
|
|
15
15
|
normalizer/ Canonical DTOs + portable idempotency types
|
|
16
16
|
sinks/ filesystem export, MigrationSink interface
|
|
17
17
|
cli/ artinstack-migrate
|
|
18
|
-
transformers/ HtmlToGrapes, css-to-styles, rewrite
|
|
18
|
+
transformers/ HtmlToGrapes, css-to-styles, inline image rewrite, media ref expand
|
|
19
|
+
lib/ content-asset-urls, migration-media-ref, origin-url-rewrite
|
|
19
20
|
```
|
|
20
21
|
|
|
21
22
|
## Install
|
|
@@ -114,6 +115,31 @@ Each file contains an array of normalized DTOs (`NormalizedPost`, `NormalizedPag
|
|
|
114
115
|
|
|
115
116
|
Per-platform export file formats and API client usage are documented in [docs/architecture.md](./docs/architecture.md).
|
|
116
117
|
|
|
118
|
+
## Migration media refs
|
|
119
|
+
|
|
120
|
+
WordPress `contentHtml` is stamped with `artinstack-migration://asset/…` refs by default (not CDN URLs). Rationale, ref format, and OSS/host split: [docs/architecture.md § Migration media refs](./docs/architecture.md#migration-media-refs).
|
|
121
|
+
|
|
122
|
+
**Host — expand refs before persist** (`htmlToGrapes`, hero promotion, sink write):
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import {
|
|
126
|
+
expandMigrationMediaRefs,
|
|
127
|
+
formatMigrationMediaRef,
|
|
128
|
+
isMigrationMediaRef,
|
|
129
|
+
parseMigrationMediaRef,
|
|
130
|
+
rewriteInlineImages,
|
|
131
|
+
stampMigrationMediaRefs,
|
|
132
|
+
} from "@artinstack/migrator";
|
|
133
|
+
|
|
134
|
+
const { html, unresolved } = expandMigrationMediaRefs(contentHtml, (sourceId) =>
|
|
135
|
+
lookupPublicUrl(sourceId), // migration_entities → CDN
|
|
136
|
+
);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**CLI / JSON export:** use `--rewrite-gateway` + `--rewrite-public` so gateway uploads normalize before refs are stamped. Unresolved upload URLs stay in HTML and appear in `conflicts.json` as `unresolvedInlineImages`.
|
|
140
|
+
|
|
141
|
+
**Tests:** `fixtures/wordpress/pages-export.test.ts` (naikonpixels pages WXR).
|
|
142
|
+
|
|
117
143
|
## Development
|
|
118
144
|
|
|
119
145
|
```bash
|
|
@@ -129,6 +155,8 @@ pnpm dev # watch build
|
|
|
129
155
|
|-------|------------------------|------------------|
|
|
130
156
|
| Parsers + normalizer DTOs | Yes | No |
|
|
131
157
|
| WordPress builder flattening + origin URL rewrite (pre-DTO) | Yes | Optional same config on adapter input |
|
|
158
|
+
| Stamp `artinstack-migration://asset/…` refs in content HTML | Yes | No |
|
|
159
|
+
| Expand refs → CDN URLs at persist | Exported helper | Call site + DB lookup |
|
|
132
160
|
| CLI + filesystem JSON export | Yes | No |
|
|
133
161
|
| `MigrationSink` interface | Yes | Implementation |
|
|
134
162
|
| Dynamic shortcodes (`[portfolio]`, `[recent_posts]`), forms, sanitize | No | Yes |
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizeAssetUrl
|
|
3
|
+
} from "./chunk-XYP3VYDH.js";
|
|
4
|
+
|
|
5
|
+
// src/lib/migration-media-ref.ts
|
|
6
|
+
var MIGRATION_MEDIA_REF_SCHEME = "artinstack-migration://asset/";
|
|
7
|
+
function formatMigrationMediaRef(sourceAssetId) {
|
|
8
|
+
return `${MIGRATION_MEDIA_REF_SCHEME}${encodeURIComponent(sourceAssetId)}`;
|
|
9
|
+
}
|
|
10
|
+
function isMigrationMediaRef(value) {
|
|
11
|
+
return value.trim().startsWith(MIGRATION_MEDIA_REF_SCHEME);
|
|
12
|
+
}
|
|
13
|
+
function parseMigrationMediaRef(value) {
|
|
14
|
+
const trimmed = value.trim();
|
|
15
|
+
if (!trimmed.startsWith(MIGRATION_MEDIA_REF_SCHEME)) return void 0;
|
|
16
|
+
const encoded = trimmed.slice(MIGRATION_MEDIA_REF_SCHEME.length);
|
|
17
|
+
if (!encoded) return void 0;
|
|
18
|
+
try {
|
|
19
|
+
return decodeURIComponent(encoded);
|
|
20
|
+
} catch {
|
|
21
|
+
return void 0;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function createMigrationMediaRefReplaceWith() {
|
|
25
|
+
return (ref) => {
|
|
26
|
+
if (!ref.sourceAssetId) return "";
|
|
27
|
+
return formatMigrationMediaRef(ref.sourceAssetId);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/lib/migration-media-url-index.ts
|
|
32
|
+
function urlPathname(url) {
|
|
33
|
+
try {
|
|
34
|
+
return new URL(url, "http://migration.local").pathname;
|
|
35
|
+
} catch {
|
|
36
|
+
return void 0;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function buildMigrationMediaUrlIndex(entries) {
|
|
40
|
+
const index = /* @__PURE__ */ new Map();
|
|
41
|
+
for (const entry of entries) {
|
|
42
|
+
index.set(entry.sourceUrl, entry.sourceId);
|
|
43
|
+
const normalized = normalizeAssetUrl(entry.sourceUrl);
|
|
44
|
+
if (normalized) index.set(normalized, entry.sourceId);
|
|
45
|
+
const pathname = urlPathname(entry.sourceUrl);
|
|
46
|
+
if (pathname) index.set(pathname, entry.sourceId);
|
|
47
|
+
}
|
|
48
|
+
return index;
|
|
49
|
+
}
|
|
50
|
+
function resolveMigrationMediaSourceId(src, urlIndex) {
|
|
51
|
+
const normalized = normalizeAssetUrl(src);
|
|
52
|
+
if (!normalized) return void 0;
|
|
53
|
+
return urlIndex.get(normalized) ?? urlIndex.get(src) ?? (urlPathname(normalized) ? urlIndex.get(urlPathname(normalized)) : void 0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// src/transformers/rewrite-inline-images.ts
|
|
57
|
+
import * as cheerio from "cheerio";
|
|
58
|
+
var BACKGROUND_IMAGE_URL_PATTERN = /background(?:-image)?\s*:[^;]*?url\s*\(\s*(['"]?)([^'")]+)\1\s*\)/gi;
|
|
59
|
+
function resolveRewriteOptions(options) {
|
|
60
|
+
const replaceWith = options.replaceWith ?? createMigrationMediaRefReplaceWith();
|
|
61
|
+
const requireUploaded = options.requireUploaded ?? Boolean(options.replaceWith);
|
|
62
|
+
return { replaceWith, requireUploaded };
|
|
63
|
+
}
|
|
64
|
+
function tryRewriteUrl(src, options, uploadedBySourceId, referencedSources, unresolved) {
|
|
65
|
+
const normalized = normalizeAssetUrl(src);
|
|
66
|
+
if (!normalized) return void 0;
|
|
67
|
+
if (isMigrationMediaRef(normalized)) {
|
|
68
|
+
referencedSources.add(normalized);
|
|
69
|
+
return normalized;
|
|
70
|
+
}
|
|
71
|
+
referencedSources.add(normalized);
|
|
72
|
+
const ref = options.resolveAsset(normalized);
|
|
73
|
+
if (!ref?.sourceAssetId) {
|
|
74
|
+
unresolved.add(normalized);
|
|
75
|
+
return void 0;
|
|
76
|
+
}
|
|
77
|
+
const { replaceWith, requireUploaded } = resolveRewriteOptions(options);
|
|
78
|
+
const uploaded = uploadedBySourceId.get(ref.sourceAssetId);
|
|
79
|
+
if (requireUploaded && !uploaded) {
|
|
80
|
+
unresolved.add(normalized);
|
|
81
|
+
return void 0;
|
|
82
|
+
}
|
|
83
|
+
return replaceWith(ref, uploaded);
|
|
84
|
+
}
|
|
85
|
+
function rewriteBackgroundUrlsInStyle(style, options, uploadedBySourceId, referencedSources, unresolved) {
|
|
86
|
+
return style.replace(BACKGROUND_IMAGE_URL_PATTERN, (full, quote, rawUrl) => {
|
|
87
|
+
const replaced = tryRewriteUrl(rawUrl.trim(), options, uploadedBySourceId, referencedSources, unresolved);
|
|
88
|
+
if (!replaced) return full;
|
|
89
|
+
const urlCall = quote ? `url(${quote}${replaced}${quote})` : `url(${replaced})`;
|
|
90
|
+
return full.replace(/url\s*\(\s*(['"]?)([^'")]+)\1\s*\)/i, urlCall);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function rewriteSrcset(srcset, options, uploadedBySourceId, referencedSources, unresolved) {
|
|
94
|
+
return srcset.split(",").map((entry) => {
|
|
95
|
+
const trimmed = entry.trim();
|
|
96
|
+
if (!trimmed) return entry;
|
|
97
|
+
const [urlPart, descriptor] = trimmed.split(/\s+/, 2);
|
|
98
|
+
const replaced = tryRewriteUrl(urlPart ?? "", options, uploadedBySourceId, referencedSources, unresolved);
|
|
99
|
+
if (!replaced) return entry;
|
|
100
|
+
return descriptor ? `${replaced} ${descriptor}` : replaced;
|
|
101
|
+
}).join(", ");
|
|
102
|
+
}
|
|
103
|
+
function rewriteInlineImages(html, options, uploadedBySourceId) {
|
|
104
|
+
if (!html.trim()) {
|
|
105
|
+
return { html, referencedSources: [], unresolved: [] };
|
|
106
|
+
}
|
|
107
|
+
const $ = cheerio.load(html, { xml: false });
|
|
108
|
+
const referencedSources = /* @__PURE__ */ new Set();
|
|
109
|
+
const unresolved = /* @__PURE__ */ new Set();
|
|
110
|
+
$("img").each((_, element) => {
|
|
111
|
+
const img = $(element);
|
|
112
|
+
const src = img.attr("src")?.trim();
|
|
113
|
+
if (src && !src.startsWith("data:")) {
|
|
114
|
+
const replaced = tryRewriteUrl(src, options, uploadedBySourceId, referencedSources, unresolved);
|
|
115
|
+
if (replaced) img.attr("src", replaced);
|
|
116
|
+
}
|
|
117
|
+
const srcset = img.attr("srcset")?.trim();
|
|
118
|
+
if (srcset) {
|
|
119
|
+
img.attr("srcset", rewriteSrcset(srcset, options, uploadedBySourceId, referencedSources, unresolved));
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
$("[data-bg-image]").each((_, element) => {
|
|
123
|
+
const node = $(element);
|
|
124
|
+
const bgImage = node.attr("data-bg-image")?.trim();
|
|
125
|
+
if (!bgImage || bgImage.startsWith("data:")) return;
|
|
126
|
+
const replaced = tryRewriteUrl(bgImage, options, uploadedBySourceId, referencedSources, unresolved);
|
|
127
|
+
if (replaced) node.attr("data-bg-image", replaced);
|
|
128
|
+
});
|
|
129
|
+
$("[style]").each((_, element) => {
|
|
130
|
+
const node = $(element);
|
|
131
|
+
const style = node.attr("style");
|
|
132
|
+
if (!style?.includes("background")) return;
|
|
133
|
+
const rewritten = rewriteBackgroundUrlsInStyle(
|
|
134
|
+
style,
|
|
135
|
+
options,
|
|
136
|
+
uploadedBySourceId,
|
|
137
|
+
referencedSources,
|
|
138
|
+
unresolved
|
|
139
|
+
);
|
|
140
|
+
if (rewritten !== style) node.attr("style", rewritten);
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
html: $.root().html() ?? html,
|
|
144
|
+
referencedSources: [...referencedSources],
|
|
145
|
+
unresolved: [...unresolved]
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function stampMigrationMediaRefs(html, options) {
|
|
149
|
+
return rewriteInlineImages(
|
|
150
|
+
html,
|
|
151
|
+
{
|
|
152
|
+
resolveAsset: (src) => {
|
|
153
|
+
const sourceAssetId = resolveMigrationMediaSourceId(src, options.urlToSourceId);
|
|
154
|
+
if (!sourceAssetId) return void 0;
|
|
155
|
+
return { originalSrc: src, sourceAssetId };
|
|
156
|
+
},
|
|
157
|
+
replaceWith: options.replaceWith,
|
|
158
|
+
requireUploaded: options.requireUploaded ?? false
|
|
159
|
+
},
|
|
160
|
+
/* @__PURE__ */ new Map()
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export {
|
|
165
|
+
MIGRATION_MEDIA_REF_SCHEME,
|
|
166
|
+
formatMigrationMediaRef,
|
|
167
|
+
isMigrationMediaRef,
|
|
168
|
+
parseMigrationMediaRef,
|
|
169
|
+
createMigrationMediaRefReplaceWith,
|
|
170
|
+
buildMigrationMediaUrlIndex,
|
|
171
|
+
resolveMigrationMediaSourceId,
|
|
172
|
+
rewriteInlineImages,
|
|
173
|
+
stampMigrationMediaRefs
|
|
174
|
+
};
|
|
175
|
+
//# sourceMappingURL=chunk-BOYB6XRA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/migration-media-ref.ts","../src/lib/migration-media-url-index.ts","../src/transformers/rewrite-inline-images.ts"],"sourcesContent":["/** Pseudo-URL scheme for portable migration asset pointers (not WordPress shortcodes). */\nexport const MIGRATION_MEDIA_REF_SCHEME = \"artinstack-migration://asset/\";\n\n/** Build `artinstack-migration://asset/{sourceId}` (percent-encodes the normalizer source id). */\nexport function formatMigrationMediaRef(sourceAssetId: string): string {\n return `${MIGRATION_MEDIA_REF_SCHEME}${encodeURIComponent(sourceAssetId)}`;\n}\n\nexport function isMigrationMediaRef(value: string): boolean {\n return value.trim().startsWith(MIGRATION_MEDIA_REF_SCHEME);\n}\n\n/** Parse a migration media ref back to the normalizer `sourceId`, or `undefined` if not a ref. */\nexport function parseMigrationMediaRef(value: string): string | undefined {\n const trimmed = value.trim();\n if (!trimmed.startsWith(MIGRATION_MEDIA_REF_SCHEME)) return undefined;\n const encoded = trimmed.slice(MIGRATION_MEDIA_REF_SCHEME.length);\n if (!encoded) return undefined;\n try {\n return decodeURIComponent(encoded);\n } catch {\n return undefined;\n }\n}\n\n/** Default `replaceWith` for `rewriteInlineImages` / `stampMigrationMediaRefs` (OSS-14). */\nexport function createMigrationMediaRefReplaceWith(): (\n ref: { sourceAssetId?: string },\n) => string {\n return (ref) => {\n if (!ref.sourceAssetId) return \"\";\n return formatMigrationMediaRef(ref.sourceAssetId);\n };\n}\n","import { normalizeAssetUrl } from \"./content-asset-urls.js\";\n\nfunction urlPathname(url: string): string | undefined {\n try {\n return new URL(url, \"http://migration.local\").pathname;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Map normalized upload URLs (and pathnames) → normalizer `sourceId`.\n * Attachment ids are WXR `post_id` strings; inline discoveries use `url:{src}`.\n */\nexport function buildMigrationMediaUrlIndex(\n entries: Iterable<{ sourceUrl: string; sourceId: string }>,\n): Map<string, string> {\n const index = new Map<string, string>();\n\n for (const entry of entries) {\n index.set(entry.sourceUrl, entry.sourceId);\n const normalized = normalizeAssetUrl(entry.sourceUrl);\n if (normalized) index.set(normalized, entry.sourceId);\n const pathname = urlPathname(entry.sourceUrl);\n if (pathname) index.set(pathname, entry.sourceId);\n }\n\n return index;\n}\n\nexport function resolveMigrationMediaSourceId(\n src: string,\n urlIndex: Map<string, string>,\n): string | undefined {\n const normalized = normalizeAssetUrl(src);\n if (!normalized) return undefined;\n\n return (\n urlIndex.get(normalized) ??\n urlIndex.get(src) ??\n (urlPathname(normalized) ? urlIndex.get(urlPathname(normalized)!) : undefined)\n );\n}\n","import * as cheerio from \"cheerio\";\n\nimport { normalizeAssetUrl } from \"../lib/content-asset-urls.js\";\nimport {\n createMigrationMediaRefReplaceWith,\n isMigrationMediaRef,\n} from \"../lib/migration-media-ref.js\";\nimport {\n buildMigrationMediaUrlIndex,\n resolveMigrationMediaSourceId,\n} from \"../lib/migration-media-url-index.js\";\n\nexport interface RewriteInlineImageRef {\n originalSrc: string;\n sourceAssetId?: string;\n}\n\nexport interface UploadedAssetRef {\n targetId: string;\n publicUrl?: string;\n}\n\nexport interface RewriteInlineImagesOptions {\n resolveAsset: (src: string) => RewriteInlineImageRef | undefined;\n /**\n * Replace a resolved source id with a migration ref or CDN URL.\n * When omitted, defaults to OSS-14 `artinstack-migration://asset/…` refs.\n */\n replaceWith?: (ref: RewriteInlineImageRef, uploaded?: UploadedAssetRef) => string;\n /**\n * When true, skip URLs that cannot be matched to an uploaded vault target.\n * Default: false when using migration refs; true when a custom `replaceWith` is supplied.\n */\n requireUploaded?: boolean;\n}\n\nexport interface RewriteInlineImagesResult {\n html: string;\n referencedSources: string[];\n unresolved: string[];\n}\n\n/** Inline CSS `background` / `background-image: url(…)` (quoted or bare). */\nconst BACKGROUND_IMAGE_URL_PATTERN =\n /background(?:-image)?\\s*:[^;]*?url\\s*\\(\\s*(['\"]?)([^'\")]+)\\1\\s*\\)/gi;\n\nfunction resolveRewriteOptions(\n options: RewriteInlineImagesOptions,\n): Required<Pick<RewriteInlineImagesOptions, \"replaceWith\" | \"requireUploaded\">> {\n const replaceWith = options.replaceWith ?? createMigrationMediaRefReplaceWith();\n const requireUploaded = options.requireUploaded ?? Boolean(options.replaceWith);\n return { replaceWith, requireUploaded };\n}\n\nfunction tryRewriteUrl(\n src: string,\n options: RewriteInlineImagesOptions,\n uploadedBySourceId: Map<string, UploadedAssetRef>,\n referencedSources: Set<string>,\n unresolved: Set<string>,\n): string | undefined {\n const normalized = normalizeAssetUrl(src);\n if (!normalized) return undefined;\n\n if (isMigrationMediaRef(normalized)) {\n referencedSources.add(normalized);\n return normalized;\n }\n\n referencedSources.add(normalized);\n const ref = options.resolveAsset(normalized);\n if (!ref?.sourceAssetId) {\n unresolved.add(normalized);\n return undefined;\n }\n\n const { replaceWith, requireUploaded } = resolveRewriteOptions(options);\n const uploaded = uploadedBySourceId.get(ref.sourceAssetId);\n if (requireUploaded && !uploaded) {\n unresolved.add(normalized);\n return undefined;\n }\n\n return replaceWith(ref, uploaded);\n}\n\nfunction rewriteBackgroundUrlsInStyle(\n style: string,\n options: RewriteInlineImagesOptions,\n uploadedBySourceId: Map<string, UploadedAssetRef>,\n referencedSources: Set<string>,\n unresolved: Set<string>,\n): string {\n return style.replace(BACKGROUND_IMAGE_URL_PATTERN, (full, quote: string, rawUrl: string) => {\n const replaced = tryRewriteUrl(rawUrl.trim(), options, uploadedBySourceId, referencedSources, unresolved);\n if (!replaced) return full;\n\n const urlCall = quote\n ? `url(${quote}${replaced}${quote})`\n : `url(${replaced})`;\n return full.replace(/url\\s*\\(\\s*(['\"]?)([^'\")]+)\\1\\s*\\)/i, urlCall);\n });\n}\n\nfunction rewriteSrcset(\n srcset: string,\n options: RewriteInlineImagesOptions,\n uploadedBySourceId: Map<string, UploadedAssetRef>,\n referencedSources: Set<string>,\n unresolved: Set<string>,\n): string {\n return srcset\n .split(\",\")\n .map((entry) => {\n const trimmed = entry.trim();\n if (!trimmed) return entry;\n const [urlPart, descriptor] = trimmed.split(/\\s+/, 2);\n const replaced = tryRewriteUrl(urlPart ?? \"\", options, uploadedBySourceId, referencedSources, unresolved);\n if (!replaced) return entry;\n return descriptor ? `${replaced} ${descriptor}` : replaced;\n })\n .join(\", \");\n}\n\n/** Rewrite `<img src>` / `srcset`, `data-bg-image`, and inline CSS backgrounds using uploaded asset targets. */\nexport function rewriteInlineImages(\n html: string,\n options: RewriteInlineImagesOptions,\n uploadedBySourceId: Map<string, UploadedAssetRef>,\n): RewriteInlineImagesResult {\n if (!html.trim()) {\n return { html, referencedSources: [], unresolved: [] };\n }\n\n const $ = cheerio.load(html, { xml: false });\n const referencedSources = new Set<string>();\n const unresolved = new Set<string>();\n\n $(\"img\").each((_, element) => {\n const img = $(element);\n const src = img.attr(\"src\")?.trim();\n if (src && !src.startsWith(\"data:\")) {\n const replaced = tryRewriteUrl(src, options, uploadedBySourceId, referencedSources, unresolved);\n if (replaced) img.attr(\"src\", replaced);\n }\n\n const srcset = img.attr(\"srcset\")?.trim();\n if (srcset) {\n img.attr(\"srcset\", rewriteSrcset(srcset, options, uploadedBySourceId, referencedSources, unresolved));\n }\n });\n\n $(\"[data-bg-image]\").each((_, element) => {\n const node = $(element);\n const bgImage = node.attr(\"data-bg-image\")?.trim();\n if (!bgImage || bgImage.startsWith(\"data:\")) return;\n const replaced = tryRewriteUrl(bgImage, options, uploadedBySourceId, referencedSources, unresolved);\n if (replaced) node.attr(\"data-bg-image\", replaced);\n });\n\n $(\"[style]\").each((_, element) => {\n const node = $(element);\n const style = node.attr(\"style\");\n if (!style?.includes(\"background\")) return;\n const rewritten = rewriteBackgroundUrlsInStyle(\n style,\n options,\n uploadedBySourceId,\n referencedSources,\n unresolved,\n );\n if (rewritten !== style) node.attr(\"style\", rewritten);\n });\n\n return {\n html: $.root().html() ?? html,\n referencedSources: [...referencedSources],\n unresolved: [...unresolved],\n };\n}\n\nexport interface StampMigrationMediaRefsOptions {\n /** Pre-built url/pathname → sourceId map (from attachments + inline assets). */\n urlToSourceId: Map<string, string>;\n replaceWith?: RewriteInlineImagesOptions[\"replaceWith\"];\n requireUploaded?: boolean;\n}\n\n/**\n * OSS-14 — replace resolved `wp-content/uploads` URLs with `artinstack-migration://asset/…`\n * refs. Does not invent refs for unknown URLs (left unchanged + listed in `unresolved`).\n */\nexport function stampMigrationMediaRefs(\n html: string,\n options: StampMigrationMediaRefsOptions,\n): RewriteInlineImagesResult {\n return rewriteInlineImages(\n html,\n {\n resolveAsset: (src) => {\n const sourceAssetId = resolveMigrationMediaSourceId(src, options.urlToSourceId);\n if (!sourceAssetId) return undefined;\n return { originalSrc: src, sourceAssetId };\n },\n replaceWith: options.replaceWith,\n requireUploaded: options.requireUploaded ?? false,\n },\n new Map(),\n );\n}\n\n/** Build a url index from attachment rows and/or normalized assets. */\nexport { buildMigrationMediaUrlIndex };\n"],"mappings":"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","names":[]}
|
|
@@ -6,11 +6,16 @@ import {
|
|
|
6
6
|
sanitizeSlug,
|
|
7
7
|
summarizeSquarespaceExport,
|
|
8
8
|
validateSquarespaceExportFile
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-MDSY3FEZ.js";
|
|
10
|
+
import {
|
|
11
|
+
buildMigrationMediaUrlIndex,
|
|
12
|
+
stampMigrationMediaRefs
|
|
13
|
+
} from "./chunk-BOYB6XRA.js";
|
|
10
14
|
import {
|
|
11
15
|
discoverContentAssetUrls,
|
|
12
|
-
normalizeAssetUrl
|
|
13
|
-
|
|
16
|
+
normalizeAssetUrl,
|
|
17
|
+
resolveFeaturedContentAssetUrl
|
|
18
|
+
} from "./chunk-XYP3VYDH.js";
|
|
14
19
|
|
|
15
20
|
// src/lib/origin-url-rewrite.ts
|
|
16
21
|
function rewriteOriginUrlsInText(text, config) {
|
|
@@ -75,6 +80,20 @@ function extractQuotedParam(params, name) {
|
|
|
75
80
|
function escapeLayoutAttr(value) {
|
|
76
81
|
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
|
77
82
|
}
|
|
83
|
+
function parseFractionWidth(fraction) {
|
|
84
|
+
if (!fraction?.trim()) return void 0;
|
|
85
|
+
const trimmed = fraction.trim();
|
|
86
|
+
const match = trimmed.match(/^(\d+)\s*\/\s*(\d+)$/);
|
|
87
|
+
if (!match) return void 0;
|
|
88
|
+
const numerator = Number(match[1]);
|
|
89
|
+
const denominator = Number(match[2]);
|
|
90
|
+
if (!Number.isFinite(numerator) || !Number.isFinite(denominator) || denominator <= 0) {
|
|
91
|
+
return void 0;
|
|
92
|
+
}
|
|
93
|
+
const percent = numerator / denominator * 100;
|
|
94
|
+
const rounded = Math.round(percent * 100) / 100;
|
|
95
|
+
return `${rounded % 1 === 0 ? rounded.toFixed(0) : rounded}%`;
|
|
96
|
+
}
|
|
78
97
|
function parseRowLayoutCols(layout) {
|
|
79
98
|
if (!layout?.trim()) return void 0;
|
|
80
99
|
const parts = layout.split("+").map((part) => part.trim()).filter(Boolean);
|
|
@@ -94,6 +113,12 @@ function openRowDiv(params, colsParamName) {
|
|
|
94
113
|
if (cols) attrs.push(`data-cols="${cols}"`);
|
|
95
114
|
return `<div ${attrs.join(" ")}>`;
|
|
96
115
|
}
|
|
116
|
+
function openColumnDiv(params, widthParamName) {
|
|
117
|
+
const attrs = ['data-layout="column"'];
|
|
118
|
+
const width = parseFractionWidth(extractQuotedParam(params, widthParamName ?? "width"));
|
|
119
|
+
if (width) attrs.push(`data-col-width="${width}"`);
|
|
120
|
+
return `<div ${attrs.join(" ")}>`;
|
|
121
|
+
}
|
|
97
122
|
function applyPrefixedLayoutMap(content, map) {
|
|
98
123
|
let html = content;
|
|
99
124
|
html = html.replace(map.sectionRegex, (_, params) => openSectionDiv(params, map.bgParamName));
|
|
@@ -124,14 +149,49 @@ function applyFractionalLayoutMap(content, map) {
|
|
|
124
149
|
}
|
|
125
150
|
return html;
|
|
126
151
|
}
|
|
152
|
+
function applyExtendedPrefixedLayoutMap(content, map) {
|
|
153
|
+
let html = content;
|
|
154
|
+
const levels = [...map.levels].sort((left, right) => {
|
|
155
|
+
const leftMax = Math.max(...left.tokens.map((token) => token.length));
|
|
156
|
+
const rightMax = Math.max(...right.tokens.map((token) => token.length));
|
|
157
|
+
return rightMax - leftMax;
|
|
158
|
+
});
|
|
159
|
+
for (const level of levels) {
|
|
160
|
+
const tokens = [...level.tokens].sort((left, right) => right.length - left.length);
|
|
161
|
+
for (const token of tokens) {
|
|
162
|
+
const openRegex = new RegExp(`\\[${escapeRegExp(token)}\\b([^\\]]*)\\]`, "gi");
|
|
163
|
+
const closeRegex = new RegExp(`\\[\\/${escapeRegExp(token)}\\b[^\\]]*\\]`, "gi");
|
|
164
|
+
html = html.replace(openRegex, (_, params) => {
|
|
165
|
+
switch (level.role) {
|
|
166
|
+
case "section":
|
|
167
|
+
return openSectionDiv(params, level.bgParamName);
|
|
168
|
+
case "row":
|
|
169
|
+
return openRowDiv(params, level.colsParamName);
|
|
170
|
+
case "column":
|
|
171
|
+
return openColumnDiv(params, level.widthParamName);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
html = html.replace(closeRegex, "</div>");
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return html;
|
|
178
|
+
}
|
|
127
179
|
function applyStructuralLayoutMap(content, map) {
|
|
128
180
|
switch (map.kind) {
|
|
129
181
|
case "prefixed":
|
|
130
182
|
return applyPrefixedLayoutMap(content, map);
|
|
131
183
|
case "fractional":
|
|
132
184
|
return applyFractionalLayoutMap(content, map);
|
|
185
|
+
case "extended-prefixed":
|
|
186
|
+
return applyExtendedPrefixedLayoutMap(content, map);
|
|
133
187
|
}
|
|
134
188
|
}
|
|
189
|
+
function collectLayoutMaps(theme) {
|
|
190
|
+
const maps = [];
|
|
191
|
+
if (theme.layoutMap) maps.push(theme.layoutMap);
|
|
192
|
+
if (theme.layoutMaps?.length) maps.push(...theme.layoutMaps);
|
|
193
|
+
return maps;
|
|
194
|
+
}
|
|
135
195
|
function extractShortcodeParam(params, names) {
|
|
136
196
|
for (const name of names) {
|
|
137
197
|
const value = extractQuotedParam(params, name);
|
|
@@ -282,8 +342,8 @@ function flattenWordPressBuilders(content, options = {}) {
|
|
|
282
342
|
for (const rule of theme.iconImageRules ?? []) {
|
|
283
343
|
html = convertIconImageRule(html, rule);
|
|
284
344
|
}
|
|
285
|
-
|
|
286
|
-
html = applyStructuralLayoutMap(html,
|
|
345
|
+
for (const layoutMap of collectLayoutMaps(theme)) {
|
|
346
|
+
html = applyStructuralLayoutMap(html, layoutMap);
|
|
287
347
|
}
|
|
288
348
|
for (const prefix of theme.scaffoldingPrefixes ?? []) {
|
|
289
349
|
html = stripScaffoldingPrefix(html, prefix);
|
|
@@ -482,8 +542,8 @@ function resolveFeaturedAssetSourceId(thumbnailId, attachmentIndex, contentHtml)
|
|
|
482
542
|
if (thumbnailId && attachmentIndex.has(thumbnailId)) {
|
|
483
543
|
return thumbnailId;
|
|
484
544
|
}
|
|
485
|
-
const
|
|
486
|
-
return
|
|
545
|
+
const featuredUrl = resolveFeaturedContentAssetUrl(contentHtml);
|
|
546
|
+
return featuredUrl ? `url:${featuredUrl}` : void 0;
|
|
487
547
|
}
|
|
488
548
|
function maybeRewriteUrl(url, config) {
|
|
489
549
|
if (!url) return void 0;
|
|
@@ -497,6 +557,12 @@ async function* enumerateWxrEntities(options) {
|
|
|
497
557
|
const { categories, tags } = collectTaxonomies(items);
|
|
498
558
|
const seenAssetUrls = /* @__PURE__ */ new Set();
|
|
499
559
|
const emittedAttachmentIds = /* @__PURE__ */ new Set();
|
|
560
|
+
const attachmentUrlIndex = buildMigrationMediaUrlIndex(
|
|
561
|
+
[...attachmentIndex.entries()].map(([sourceId, entry]) => ({
|
|
562
|
+
sourceId,
|
|
563
|
+
sourceUrl: entry.sourceUrl
|
|
564
|
+
}))
|
|
565
|
+
);
|
|
500
566
|
for (const category of categories.values()) {
|
|
501
567
|
yield category;
|
|
502
568
|
}
|
|
@@ -522,18 +588,28 @@ async function* enumerateWxrEntities(options) {
|
|
|
522
588
|
const id = textValue(item.post_id);
|
|
523
589
|
const link = maybeRewriteUrl(textValue(item.link), options.originUrlRewrite);
|
|
524
590
|
const slug = sanitizeSlug(textValue(item.post_name) || textValue(item.title) || id);
|
|
525
|
-
|
|
591
|
+
let contentHtml = preprocessContent(getContentEncoded(item), options);
|
|
526
592
|
if (postType === "page" && options.skipWooCommerceStubPages !== false && isWooCommerceStubPage(slug, contentHtml)) {
|
|
527
593
|
continue;
|
|
528
594
|
}
|
|
529
|
-
|
|
595
|
+
const inlineAssets = collectInlineAssets(
|
|
530
596
|
contentHtml,
|
|
531
597
|
attachmentIndex,
|
|
532
598
|
seenAssetUrls,
|
|
533
599
|
options.exportedAt
|
|
534
|
-
)
|
|
600
|
+
);
|
|
601
|
+
for (const asset of inlineAssets) {
|
|
535
602
|
yield asset;
|
|
536
603
|
}
|
|
604
|
+
if (options.stampMigrationMediaRefs !== false) {
|
|
605
|
+
const urlIndex = new Map(attachmentUrlIndex);
|
|
606
|
+
for (const asset of inlineAssets) {
|
|
607
|
+
urlIndex.set(asset.sourceUrl, asset.sourceId);
|
|
608
|
+
const normalized = normalizeAssetUrl(asset.sourceUrl);
|
|
609
|
+
if (normalized) urlIndex.set(normalized, asset.sourceId);
|
|
610
|
+
}
|
|
611
|
+
contentHtml = stampMigrationMediaRefs(contentHtml, { urlToSourceId: urlIndex }).html;
|
|
612
|
+
}
|
|
537
613
|
const categorySlugs = [];
|
|
538
614
|
const tagSlugs = [];
|
|
539
615
|
for (const cat of asArray(item.category)) {
|
|
@@ -2621,4 +2697,4 @@ export {
|
|
|
2621
2697
|
wixAdapter,
|
|
2622
2698
|
getAdapter
|
|
2623
2699
|
};
|
|
2624
|
-
//# sourceMappingURL=chunk-
|
|
2700
|
+
//# sourceMappingURL=chunk-KF7G7DM6.js.map
|