@contentrain/wp-import 0.10.4 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +24 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +62 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -84,9 +84,25 @@ interface RestImportResult {
|
|
|
84
84
|
declare function fetchRestRawIR(options: RestImportOptions): Promise<RestImportResult>;
|
|
85
85
|
//#endregion
|
|
86
86
|
//#region src/contentrain.d.ts
|
|
87
|
+
/** A post whose imported slug is not the address it was served at: it gets a 301 from `from`. */
|
|
88
|
+
interface SlugMove {
|
|
89
|
+
wp_id: number;
|
|
90
|
+
type: string;
|
|
91
|
+
/** The path the source site served it at (its `link`), when the source named one. */
|
|
92
|
+
from: string | null;
|
|
93
|
+
/** The slug the entry was written under. */
|
|
94
|
+
slug: string;
|
|
95
|
+
/**
|
|
96
|
+
* `empty`: the source slug left no word, `collision`: another entry of the same type already holds it,
|
|
97
|
+
* `rewritten`: characters outside the slug alphabet (ZWNJ/ZWJ, `・`, an NFD spelling...) were changed.
|
|
98
|
+
*/
|
|
99
|
+
reason: 'empty' | 'collision' | 'rewritten';
|
|
100
|
+
}
|
|
87
101
|
interface ImportReport {
|
|
88
102
|
slug_rewritten: number;
|
|
89
103
|
slug_fallback: number;
|
|
104
|
+
/** Posts whose address changed (empty or colliding slug): each needs a 301 from its old address. */
|
|
105
|
+
slug_moves: SlugMove[];
|
|
90
106
|
title_fallback: number;
|
|
91
107
|
meta_fields: Record<string, string>;
|
|
92
108
|
acf_fields: Record<string, string>;
|
|
@@ -183,6 +199,13 @@ declare const PLUGIN_META: RegExp;
|
|
|
183
199
|
/** Taxonomy → model id. */
|
|
184
200
|
declare const taxModelId: (t: string) => string;
|
|
185
201
|
declare const slugify: (s: unknown) => string;
|
|
202
|
+
/**
|
|
203
|
+
* A WordPress `post_name` as the address it is: percent-encoded UTF-8 decoded, NFC, lowercase, anything outside letters,
|
|
204
|
+
* digits and marks (the contract's slug alphabet) a hyphen. Nothing is transliterated: `日本語`, `ışık` and `straße`
|
|
205
|
+
* stay what the source site served, because the slug is the page's address. ASCII comes out exactly as `slugify`
|
|
206
|
+
* gives it. Empty when nothing of a word is left, or when the slug would not fit a file name (200 bytes).
|
|
207
|
+
*/
|
|
208
|
+
declare const addressSlug: (raw: string) => string;
|
|
186
209
|
//#endregion
|
|
187
210
|
//#region src/delta.d.ts
|
|
188
211
|
/** A `.contentrain` store as a file map (repo-relative path → file text), with the source map it was built from. */
|
|
@@ -227,5 +250,5 @@ declare function planSourceDelta(input: PlanSourceDeltaInput): SourceDeltaPlan;
|
|
|
227
250
|
/** The plan as text, for a dry run: one line per record, then conflicts, unmapped records, redirects and warnings. */
|
|
228
251
|
declare function formatSourceDeltaReport(plan: SourceDeltaPlan): string;
|
|
229
252
|
//#endregion
|
|
230
|
-
export { type ContentrainResult, DEFAULT_IMPORTERS, DEFAULT_TAXONOMIES, type DeltaStore, type ImportReport, PLUGIN_META, type PlanSourceDeltaInput, type RestImportOptions, type RestImportResult, SKIP_TYPES, type UnserializeResult, type WxrParseResult, type WxrStats, buildCommentsExport, canon, fetchRestRawIR, formatSourceDeltaReport, hexId, looksSerialized, parseWxr, phpUnserialize, planSourceDelta, rawToContentrain, selectComments, slugify, strip, summarizeComments, taxModelId, tryUnserialize };
|
|
253
|
+
export { type ContentrainResult, DEFAULT_IMPORTERS, DEFAULT_TAXONOMIES, type DeltaStore, type ImportReport, PLUGIN_META, type PlanSourceDeltaInput, type RestImportOptions, type RestImportResult, SKIP_TYPES, type UnserializeResult, type WxrParseResult, type WxrStats, addressSlug, buildCommentsExport, canon, fetchRestRawIR, formatSourceDeltaReport, hexId, looksSerialized, parseWxr, phpUnserialize, planSourceDelta, rawToContentrain, selectComments, slugify, strip, summarizeComments, taxModelId, tryUnserialize };
|
|
231
254
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/wxr.ts","../src/rest.ts","../src/contentrain.ts","../src/comments.ts","../src/php-unserialize.ts","../src/core.ts","../src/delta.ts"],"mappings":";;;;UA2BiB,QAAA;EACf,UAAA;IAAc,QAAA;IAAkB,OAAA;IAAiB,MAAA;IAAgB,IAAA;EAAA;EACjE,UAAA;EACA,gBAAA;EACA,MAAA,EAAQ,MAAA;AAAA;AAAA,UAGO,cAAA;EACf,GAAA,EAAK,KAAA;EACL,KAAA,EAAO,QAAA;AAAA;AAAA,iBAiBa,QAAA,CAAS,KAAA,WAAgB,QAAA,EAAU,IAAA;EAAS,IAAA;AAAA,IAAkB,OAAA,CAAQ,cAAA;;;UCnC3E,iBAAA;EACf,MAAA;;EAEA,SAAA,UAAmB,KAAA;EDMI;;;;;;;;ECGvB,IAAA;IAAS,IAAA;IAAc,WAAA;EAAA;EACvB,OAAA;EDAc;AAGhB;;;;ECGE,WAAA;EDFK;;;;ECOL,QAAA;EACA,IAAA;AAAA;AAAA,UAGe,gBAAA;EACf,GAAA,EAAK,KAAA;EACL,QAAA;EDKkF;;;;;;;;ECIlF,UAAA;IAAc,MAAA;IAA0C,SAAA;EAAA;;;AAvC1D;;;;;;;;;;EAoDE,IAAA;AAAA;AAAA,iBA2EoB,cAAA,CAAe,OAAA,EAAS,iBAAA,GAAoB,OAAA,CAAQ,gBAAA
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/wxr.ts","../src/rest.ts","../src/contentrain.ts","../src/comments.ts","../src/php-unserialize.ts","../src/core.ts","../src/delta.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -181,6 +181,16 @@ const PLUGIN_META = /^(jetpack|wpdc|discourse|footnotes|inline_featured|spay_|ad
|
|
|
181
181
|
/** Taxonomy → model id. */
|
|
182
182
|
const taxModelId = (t) => t === "post_tag" ? "tags" : t === "category" ? "categories" : t.replace(/_/g, "-");
|
|
183
183
|
const slugify = (s) => String(s ?? "").normalize("NFKD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
184
|
+
/**
|
|
185
|
+
* A WordPress `post_name` as the address it is: percent-encoded UTF-8 decoded, NFC, lowercase, anything outside letters,
|
|
186
|
+
* digits and marks (the contract's slug alphabet) a hyphen. Nothing is transliterated: `日本語`, `ışık` and `straße`
|
|
187
|
+
* stay what the source site served, because the slug is the page's address. ASCII comes out exactly as `slugify`
|
|
188
|
+
* gives it. Empty when nothing of a word is left, or when the slug would not fit a file name (200 bytes).
|
|
189
|
+
*/
|
|
190
|
+
const addressSlug = (raw) => {
|
|
191
|
+
const slug = decodeSlug(raw).normalize("NFC").toLowerCase().normalize("NFC").replace(/[^\p{Ll}\p{Lo}\p{Lm}\p{N}\p{M}]+/gu, "-").replace(/^-+|-+$/g, "");
|
|
192
|
+
return new TextEncoder().encode(slug).length > 200 ? "" : slug;
|
|
193
|
+
};
|
|
184
194
|
const decodeSlug = (s) => {
|
|
185
195
|
try {
|
|
186
196
|
return decodeURIComponent(s);
|
|
@@ -2102,6 +2112,19 @@ async function fetchRestRawIR(options) {
|
|
|
2102
2112
|
}
|
|
2103
2113
|
//#endregion
|
|
2104
2114
|
//#region src/contentrain.ts
|
|
2115
|
+
const utf8Length = (s) => new TextEncoder().encode(s).length;
|
|
2116
|
+
/** `s` cut on a code-point boundary to at most `max` UTF-8 bytes, without a trailing hyphen. */
|
|
2117
|
+
const fitBytes = (s, max) => {
|
|
2118
|
+
let out = "";
|
|
2119
|
+
let bytes = 0;
|
|
2120
|
+
for (const ch of s) {
|
|
2121
|
+
const n = utf8Length(ch);
|
|
2122
|
+
if (bytes + n > max) break;
|
|
2123
|
+
out += ch;
|
|
2124
|
+
bytes += n;
|
|
2125
|
+
}
|
|
2126
|
+
return out.replace(/-+$/, "");
|
|
2127
|
+
};
|
|
2105
2128
|
const pick = (o, keys) => Object.fromEntries(Object.entries(o).filter(([k, v]) => keys.has(k) && v !== void 0 && v !== null && v !== ""));
|
|
2106
2129
|
const typeModelId = (t) => t === "post" ? "posts" : t === "page" ? "pages" : t.replace(/_/g, "-");
|
|
2107
2130
|
const domainOf = (t) => t === "post" ? "blog" : t === "page" ? "site" : "custom";
|
|
@@ -2162,6 +2185,7 @@ function rawToContentrain(raw, opts) {
|
|
|
2162
2185
|
const report = {
|
|
2163
2186
|
slug_rewritten: 0,
|
|
2164
2187
|
slug_fallback: 0,
|
|
2188
|
+
slug_moves: [],
|
|
2165
2189
|
title_fallback: 0,
|
|
2166
2190
|
meta_fields: {},
|
|
2167
2191
|
acf_fields: {},
|
|
@@ -2200,15 +2224,44 @@ function rawToContentrain(raw, opts) {
|
|
|
2200
2224
|
const termId = (tax, slug) => hexId(`${tax}:${slug}`);
|
|
2201
2225
|
const usedSlugs = /* @__PURE__ */ new Map();
|
|
2202
2226
|
const postSlug = (p) => {
|
|
2203
|
-
let s =
|
|
2227
|
+
let s = addressSlug(p.slug);
|
|
2228
|
+
const moved = (reason, slug) => {
|
|
2229
|
+
let from = null;
|
|
2230
|
+
try {
|
|
2231
|
+
from = p.link ? new URL(p.link).pathname : null;
|
|
2232
|
+
} catch {
|
|
2233
|
+
from = null;
|
|
2234
|
+
}
|
|
2235
|
+
report.slug_moves.push({
|
|
2236
|
+
wp_id: p.id,
|
|
2237
|
+
type: p.type,
|
|
2238
|
+
from,
|
|
2239
|
+
slug,
|
|
2240
|
+
reason
|
|
2241
|
+
});
|
|
2242
|
+
};
|
|
2243
|
+
let empty = false;
|
|
2244
|
+
let rewritten = false;
|
|
2204
2245
|
if (!s) {
|
|
2205
2246
|
s = `${p.type}-${p.id}`;
|
|
2247
|
+
empty = true;
|
|
2206
2248
|
report.slug_fallback++;
|
|
2207
|
-
} else if (s !== p.slug)
|
|
2249
|
+
} else if (s !== decodeSlug(p.slug)) {
|
|
2250
|
+
report.slug_rewritten++;
|
|
2251
|
+
rewritten = s !== decodeSlug(p.slug).toLowerCase();
|
|
2252
|
+
}
|
|
2208
2253
|
const key = multilingual ? `${p.type}:${postLocale(p)}:${s}` : `${p.type}:${s}`;
|
|
2209
2254
|
const n = usedSlugs.get(key) ?? 0;
|
|
2210
2255
|
usedSlugs.set(key, n + 1);
|
|
2211
|
-
|
|
2256
|
+
if (n) {
|
|
2257
|
+
const suffix = `-${p.id}`;
|
|
2258
|
+
const unique = `${fitBytes(s, 200 - utf8Length(suffix))}${suffix}`;
|
|
2259
|
+
moved("collision", unique);
|
|
2260
|
+
return unique;
|
|
2261
|
+
}
|
|
2262
|
+
if (empty) moved("empty", s);
|
|
2263
|
+
else if (rewritten) moved("rewritten", s);
|
|
2264
|
+
return s;
|
|
2212
2265
|
};
|
|
2213
2266
|
const slugOf = /* @__PURE__ */ new Map();
|
|
2214
2267
|
const postEntry = /* @__PURE__ */ new Map();
|
|
@@ -2448,7 +2501,7 @@ function rawToContentrain(raw, opts) {
|
|
|
2448
2501
|
const id = termId(tax, t.slug);
|
|
2449
2502
|
contents[mid][id] = pick({
|
|
2450
2503
|
name: strip(t.name),
|
|
2451
|
-
slug:
|
|
2504
|
+
slug: addressSlug(t.slug) || `term-${t.id}`,
|
|
2452
2505
|
description: strip(t.description),
|
|
2453
2506
|
parent: t.parent && t.parent_resolved ? termId(tax, t.parent) : null,
|
|
2454
2507
|
wp_id: t.id
|
|
@@ -2469,7 +2522,7 @@ function rawToContentrain(raw, opts) {
|
|
|
2469
2522
|
const id = termId(t.taxonomy, t.slug);
|
|
2470
2523
|
contents[mid][id] ??= {
|
|
2471
2524
|
name: strip(t.name),
|
|
2472
|
-
slug:
|
|
2525
|
+
slug: addressSlug(t.slug) || `term-${hexId(`${t.taxonomy}:${t.slug}`).slice(0, 8)}`
|
|
2473
2526
|
};
|
|
2474
2527
|
metas[mid][id] ??= importMeta("published");
|
|
2475
2528
|
}
|
|
@@ -2565,7 +2618,7 @@ function rawToContentrain(raw, opts) {
|
|
|
2565
2618
|
const mediaSlugs = /* @__PURE__ */ new Set();
|
|
2566
2619
|
for (const a of raw.attachments) {
|
|
2567
2620
|
const id = hexId(`media:${a.id}`);
|
|
2568
|
-
let slug =
|
|
2621
|
+
let slug = addressSlug(a.slug) || `media-${a.id}`;
|
|
2569
2622
|
if (mediaSlugs.has(slug)) slug = `${slug}-${a.id}`;
|
|
2570
2623
|
mediaSlugs.add(slug);
|
|
2571
2624
|
const target = a.parent ? postEntry.get(a.parent) : void 0;
|
|
@@ -2889,7 +2942,7 @@ function rawToContentrain(raw, opts) {
|
|
|
2889
2942
|
const mid = hexId(`menus:${m.slug}`);
|
|
2890
2943
|
contents.menus[mid] = {
|
|
2891
2944
|
name: strip(m.name),
|
|
2892
|
-
slug:
|
|
2945
|
+
slug: addressSlug(m.slug) || `menu-${m.id}`,
|
|
2893
2946
|
items: m.items.map((i) => itemRef(i.id)),
|
|
2894
2947
|
...m.id !== null && m.id > 0 ? { wp_id: m.id } : {},
|
|
2895
2948
|
...m.locations?.length ? { locations: m.locations } : {}
|
|
@@ -2905,7 +2958,7 @@ function rawToContentrain(raw, opts) {
|
|
|
2905
2958
|
open_in_new_tab: i.target_attr === "_blank"
|
|
2906
2959
|
};
|
|
2907
2960
|
if (i.parent && !i.parent_unresolved) e.parent = itemRef(i.parent);
|
|
2908
|
-
if (i.target.kind === "url" && i.url) e.url = i.url;
|
|
2961
|
+
if ((i.target.kind === "url" || i.target.kind === "archive") && i.url) e.url = i.url;
|
|
2909
2962
|
if (i.target.kind === "post" && i.target.resolved && i.target.id != null) {
|
|
2910
2963
|
const pe = postEntry.get(i.target.id);
|
|
2911
2964
|
if (pe) e.target = toTarget.ref(pe);
|
|
@@ -3492,6 +3545,6 @@ function formatSourceDeltaReport(plan) {
|
|
|
3492
3545
|
return `${lines.join("\n")}\n`;
|
|
3493
3546
|
}
|
|
3494
3547
|
//#endregion
|
|
3495
|
-
export { DEFAULT_IMPORTERS, DEFAULT_TAXONOMIES, PLUGIN_META, SKIP_TYPES, buildCommentsExport, canon, fetchRestRawIR, formatSourceDeltaReport, hexId, looksSerialized, parseWxr, phpUnserialize, planSourceDelta, rawToContentrain, selectComments, slugify, strip, summarizeComments, taxModelId, tryUnserialize };
|
|
3548
|
+
export { DEFAULT_IMPORTERS, DEFAULT_TAXONOMIES, PLUGIN_META, SKIP_TYPES, addressSlug, buildCommentsExport, canon, fetchRestRawIR, formatSourceDeltaReport, hexId, looksSerialized, parseWxr, phpUnserialize, planSourceDelta, rawToContentrain, selectComments, slugify, strip, summarizeComments, taxModelId, tryUnserialize };
|
|
3496
3549
|
|
|
3497
3550
|
//# sourceMappingURL=index.mjs.map
|