@geekity/plugin-wordpress 0.1.1 → 0.3.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/README.md +232 -9
- package/dist/bundle/index.js +24475 -2130
- package/dist/bundle/plugin.json +2 -2
- package/dist/comments-import.d.ts +3 -0
- package/dist/comments-import.d.ts.map +1 -0
- package/dist/comments-import.js +246 -0
- package/dist/comments-import.js.map +1 -0
- package/dist/content-command.d.ts +3 -0
- package/dist/content-command.d.ts.map +1 -0
- package/dist/content-command.js +100 -0
- package/dist/content-command.js.map +1 -0
- package/dist/content-import.d.ts +55 -0
- package/dist/content-import.d.ts.map +1 -0
- package/dist/content-import.js +299 -0
- package/dist/content-import.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/media-import.d.ts +3 -0
- package/dist/media-import.d.ts.map +1 -0
- package/dist/media-import.js +89 -0
- package/dist/media-import.js.map +1 -0
- package/dist/media-redirect.d.ts +4 -0
- package/dist/media-redirect.d.ts.map +1 -0
- package/dist/media-redirect.js +28 -0
- package/dist/media-redirect.js.map +1 -0
- package/dist/media.d.ts +14 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +102 -0
- package/dist/media.js.map +1 -0
- package/dist/posts-import.d.ts +34 -0
- package/dist/posts-import.d.ts.map +1 -0
- package/dist/posts-import.js +304 -0
- package/dist/posts-import.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/wordpress-html.d.ts +7 -0
- package/dist/wordpress-html.d.ts.map +1 -0
- package/dist/wordpress-html.js +360 -0
- package/dist/wordpress-html.js.map +1 -0
- package/dist/wxr.d.ts +80 -0
- package/dist/wxr.d.ts.map +1 -0
- package/dist/wxr.js +212 -0
- package/dist/wxr.js.map +1 -0
- package/package.json +8 -4
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdir, readFile, rename, stat, writeFile } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { repliesKey } from '@geekity/cms/plugin';
|
|
5
|
+
const SKIPPED_POST_TYPES = {
|
|
6
|
+
revision: 'an earlier version of a post; the import takes the current one',
|
|
7
|
+
oembed_cache: "WordPress's cache of embed answers; Geekity fetches its own",
|
|
8
|
+
nav_menu_item: 'a navigation menu entry; a Geekity theme draws its own menu',
|
|
9
|
+
wp_navigation: 'a block navigation menu; a Geekity theme draws its own menu',
|
|
10
|
+
wp_global_styles: "the block theme's styles; a Geekity theme carries its own",
|
|
11
|
+
wp_template: 'a block theme template; a Geekity theme carries its own',
|
|
12
|
+
wp_template_part: 'a block theme template part; a Geekity theme carries its own',
|
|
13
|
+
wp_font_family: 'a font installed into the block theme',
|
|
14
|
+
wp_font_face: 'a font installed into the block theme',
|
|
15
|
+
custom_css: "the Customizer's extra CSS; put it in a Geekity theme",
|
|
16
|
+
customize_changeset: 'an unsaved Customizer session',
|
|
17
|
+
user_request: 'a personal data export or erasure request',
|
|
18
|
+
wp_block: 'a reusable block; its content is already in each post that used it',
|
|
19
|
+
ap_actor: "the ActivityPub plugin's cache of a remote actor; its avatar goes to the comments it wrote",
|
|
20
|
+
ap_inbox: "the ActivityPub plugin's record of an incoming activity",
|
|
21
|
+
ap_outbox: "the ActivityPub plugin's record of an outgoing activity",
|
|
22
|
+
ap_extrafield: "an ActivityPub plugin profile field; carry it to the user's profile by hand",
|
|
23
|
+
ap_extrafield_blog: "an ActivityPub plugin blog profile field; carry it to the site's profile by hand",
|
|
24
|
+
ap_post: "the ActivityPub plugin's copy of a remote post",
|
|
25
|
+
feedback: 'a contact form submission; Geekity keeps its own',
|
|
26
|
+
};
|
|
27
|
+
export const IMPORT_RECORD_FILE = 'import.json';
|
|
28
|
+
export const IMPORT_REDIRECTS_FILE = '_data/redirects/wordpress.json';
|
|
29
|
+
export async function importWordPressContent(options) {
|
|
30
|
+
const { exported, context, data, importers } = options;
|
|
31
|
+
const contentDir = context.site.contentDir;
|
|
32
|
+
const claimed = new Set(importers.flatMap((importer) => importer.postTypes));
|
|
33
|
+
const rows = exported.items
|
|
34
|
+
.filter((item) => !claimed.has(item.type))
|
|
35
|
+
.map((item) => ({
|
|
36
|
+
outcome: 'skipped',
|
|
37
|
+
item,
|
|
38
|
+
why: SKIPPED_POST_TYPES[item.type] ?? `there is no importer for ${item.type} items`,
|
|
39
|
+
}));
|
|
40
|
+
const files = [];
|
|
41
|
+
const entries = [];
|
|
42
|
+
const comments = [];
|
|
43
|
+
for (const importer of importers) {
|
|
44
|
+
const output = await importer.import(exported, context);
|
|
45
|
+
files.push(...output.files);
|
|
46
|
+
rows.push(...output.notes);
|
|
47
|
+
entries.push(...(output.entries ?? []));
|
|
48
|
+
comments.push(...(output.comments ?? []));
|
|
49
|
+
}
|
|
50
|
+
const record = readRecord(data);
|
|
51
|
+
const seen = new Set();
|
|
52
|
+
for (const file of files) {
|
|
53
|
+
const relative = contentPath(file.path);
|
|
54
|
+
if (seen.has(relative)) {
|
|
55
|
+
throw new Error(`Two importers both write ${relative}; each path has one owner.`);
|
|
56
|
+
}
|
|
57
|
+
seen.add(relative);
|
|
58
|
+
rows.push(await writeOne(contentDir, relative, file, record, data));
|
|
59
|
+
}
|
|
60
|
+
const byFile = new Map();
|
|
61
|
+
for (const entry of entries) {
|
|
62
|
+
const file = contentPath(entry.file);
|
|
63
|
+
byFile.set(file, [...(byFile.get(file) ?? []), entry]);
|
|
64
|
+
}
|
|
65
|
+
for (const [file, inFile] of byFile) {
|
|
66
|
+
rows.push(...(await writeEntries(contentDir, file, inFile, record, data)));
|
|
67
|
+
}
|
|
68
|
+
const byPost = new Map();
|
|
69
|
+
for (const comment of comments) {
|
|
70
|
+
byPost.set(comment.permalink, [...(byPost.get(comment.permalink) ?? []), comment]);
|
|
71
|
+
}
|
|
72
|
+
for (const [permalink, onPost] of byPost) {
|
|
73
|
+
rows.push(...(await writeComments(context.site, permalink, onPost, record, data)));
|
|
74
|
+
}
|
|
75
|
+
const order = new Map(exported.items.map((item, index) => [item, index]));
|
|
76
|
+
rows.sort((a, b) => (order.get(a.item) ?? 0) - (order.get(b.item) ?? 0));
|
|
77
|
+
return { rows };
|
|
78
|
+
}
|
|
79
|
+
function readRecord(data) {
|
|
80
|
+
const text = data.read(IMPORT_RECORD_FILE);
|
|
81
|
+
const parsed = text === undefined ? {} : JSON.parse(text);
|
|
82
|
+
const nested = (saved) => new Map(Object.entries(saved ?? {}).map(([file, keys]) => [file, new Map(Object.entries(keys))]));
|
|
83
|
+
return {
|
|
84
|
+
files: new Map(Object.entries(parsed.files ?? {})),
|
|
85
|
+
entries: nested(parsed.entries),
|
|
86
|
+
comments: nested(parsed.comments),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
async function saveRecord(data, record) {
|
|
90
|
+
const sorted = (map) => Object.fromEntries([...map].sort(([a], [b]) => (a < b ? -1 : 1)));
|
|
91
|
+
const nested = (map) => Object.fromEntries([...map].sort(([a], [b]) => (a < b ? -1 : 1)).map(([file, keys]) => [file, sorted(keys)]));
|
|
92
|
+
const saved = { files: sorted(record.files) };
|
|
93
|
+
if (record.entries.size > 0)
|
|
94
|
+
saved.entries = nested(record.entries);
|
|
95
|
+
if (record.comments.size > 0)
|
|
96
|
+
saved.comments = nested(record.comments);
|
|
97
|
+
await data.update(IMPORT_RECORD_FILE, () => `${JSON.stringify(saved, null, 2)}\n`);
|
|
98
|
+
}
|
|
99
|
+
function decide(current, recorded, next) {
|
|
100
|
+
if (current === undefined) {
|
|
101
|
+
return {
|
|
102
|
+
action: 'write',
|
|
103
|
+
outcome: 'written',
|
|
104
|
+
why: recorded === undefined ? 'new' : 'removed on this site, written again',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (current === next) {
|
|
108
|
+
return { action: 'adopt', outcome: 'unchanged', why: 'already as WordPress has it' };
|
|
109
|
+
}
|
|
110
|
+
if (recorded === undefined) {
|
|
111
|
+
return {
|
|
112
|
+
action: 'leave',
|
|
113
|
+
outcome: 'clash',
|
|
114
|
+
why: 'a file not written by the import is already there; it was left alone',
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (current === recorded) {
|
|
118
|
+
return {
|
|
119
|
+
action: 'write',
|
|
120
|
+
outcome: 'written',
|
|
121
|
+
why: 'changed on WordPress since the last import',
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return next === recorded
|
|
125
|
+
? {
|
|
126
|
+
action: 'leave',
|
|
127
|
+
outcome: 'kept',
|
|
128
|
+
why: 'WordPress has not changed it since the last import',
|
|
129
|
+
}
|
|
130
|
+
: { action: 'leave', outcome: 'conflict', why: "this site's version was kept" };
|
|
131
|
+
}
|
|
132
|
+
async function writeOne(contentDir, relative, file, record, data) {
|
|
133
|
+
const absolute = path.join(contentDir, relative);
|
|
134
|
+
const current = await readIfPresent(absolute);
|
|
135
|
+
const recorded = record.files.get(relative);
|
|
136
|
+
const next = sha256(file.contents);
|
|
137
|
+
const decision = decide(current === undefined ? undefined : sha256(current), recorded, next);
|
|
138
|
+
if (decision.action === 'write')
|
|
139
|
+
await writeAtomically(absolute, file.contents);
|
|
140
|
+
if (decision.action !== 'leave' && recorded !== next) {
|
|
141
|
+
record.files.set(relative, next);
|
|
142
|
+
await saveRecord(data, record);
|
|
143
|
+
}
|
|
144
|
+
let why = decision.why;
|
|
145
|
+
if (decision.outcome === 'kept' || decision.outcome === 'conflict') {
|
|
146
|
+
const editedAt = (await stat(absolute)).mtime.toISOString();
|
|
147
|
+
const changedAt = file.item.modifiedGmt ?? file.item.dateGmt ?? 'an unknown time';
|
|
148
|
+
why =
|
|
149
|
+
decision.outcome === 'kept'
|
|
150
|
+
? `edited on this site at ${editedAt}; ${why}`
|
|
151
|
+
: `edited on this site at ${editedAt}, and changed on WordPress at ${changedAt}; ${why}`;
|
|
152
|
+
}
|
|
153
|
+
return { outcome: decision.outcome, item: file.item, path: relative, why };
|
|
154
|
+
}
|
|
155
|
+
async function writeEntries(contentDir, file, entries, record, data) {
|
|
156
|
+
const absolute = path.join(contentDir, file);
|
|
157
|
+
const text = await readIfPresent(absolute);
|
|
158
|
+
const held = text === undefined ? {} : JSON.parse(text.toString('utf8'));
|
|
159
|
+
const recordedKeys = record.entries.get(file) ?? new Map();
|
|
160
|
+
const name = path.posix.basename(file);
|
|
161
|
+
const rows = [];
|
|
162
|
+
let changed = false;
|
|
163
|
+
for (const { item, key, value } of entries) {
|
|
164
|
+
const next = JSON.stringify(value);
|
|
165
|
+
const current = held[key] === undefined ? undefined : JSON.stringify(held[key]);
|
|
166
|
+
const recorded = recordedKeys.get(key);
|
|
167
|
+
const decision = decide(current, recorded, next);
|
|
168
|
+
if (decision.action === 'write') {
|
|
169
|
+
held[key] = value;
|
|
170
|
+
changed = true;
|
|
171
|
+
}
|
|
172
|
+
if (decision.action !== 'leave' && recorded !== next) {
|
|
173
|
+
recordedKeys.set(key, next);
|
|
174
|
+
record.entries.set(file, recordedKeys);
|
|
175
|
+
await saveRecord(data, record);
|
|
176
|
+
}
|
|
177
|
+
let why = `${key} is ${next}`;
|
|
178
|
+
if (decision.outcome === 'clash') {
|
|
179
|
+
why = `${name} already sets ${key} to ${current ?? ''}; it was left alone`;
|
|
180
|
+
}
|
|
181
|
+
else if (decision.action === 'leave') {
|
|
182
|
+
why = `${key} was changed on this site to ${current ?? ''}; it was left alone`;
|
|
183
|
+
}
|
|
184
|
+
rows.push({ outcome: decision.outcome, item, path: file, why });
|
|
185
|
+
}
|
|
186
|
+
if (changed)
|
|
187
|
+
await writeAtomically(absolute, `${JSON.stringify(held, null, 2)}\n`);
|
|
188
|
+
return rows;
|
|
189
|
+
}
|
|
190
|
+
async function writeComments(site, permalink, comments, record, data) {
|
|
191
|
+
// A boot gave each id 0.2.0 wrote the key of that WordPress URL. A comment
|
|
192
|
+
// now keyed by its origin moves from that key to its own.
|
|
193
|
+
await site.renameComments(permalink, new Map(comments.map(({ comment, formerId }) => [repliesKey(formerId), comment.id])));
|
|
194
|
+
const held = site.comments(permalink);
|
|
195
|
+
const byId = new Map(held.comments.map((comment) => [comment.id, comment]));
|
|
196
|
+
const { recorded, carried } = carryOver(record.comments.get(held.file), comments, byId);
|
|
197
|
+
const rows = [];
|
|
198
|
+
const writes = [];
|
|
199
|
+
for (const { item, comment, label } of comments) {
|
|
200
|
+
const here = byId.get(comment.id);
|
|
201
|
+
const now = here === undefined ? undefined : sha256(canonical(here));
|
|
202
|
+
const before = recorded.get(comment.id);
|
|
203
|
+
const next = sha256(canonical(comment));
|
|
204
|
+
const deletedHere = now === undefined && before !== undefined;
|
|
205
|
+
const decision = deletedHere
|
|
206
|
+
? { action: 'leave', outcome: 'kept', why: 'deleted on this site; left out' }
|
|
207
|
+
: decide(now, before, next);
|
|
208
|
+
let why = decision.why;
|
|
209
|
+
if (decision.action === 'write') {
|
|
210
|
+
writes.push(comment);
|
|
211
|
+
if (carried.has(comment.id))
|
|
212
|
+
why = 'written again with its key, migrated and its origin';
|
|
213
|
+
}
|
|
214
|
+
else if (here !== undefined &&
|
|
215
|
+
before !== undefined &&
|
|
216
|
+
(here.migrated !== true ||
|
|
217
|
+
here.origin !== comment.origin ||
|
|
218
|
+
(comment.user !== undefined && here.user !== comment.user) ||
|
|
219
|
+
(comment.actor !== undefined && here.actor !== comment.actor))) {
|
|
220
|
+
// The import owns what a comment is and who wrote it, though this site
|
|
221
|
+
// owns what it says.
|
|
222
|
+
writes.push({
|
|
223
|
+
...here,
|
|
224
|
+
migrated: true,
|
|
225
|
+
...(comment.origin === undefined ? {} : { origin: comment.origin }),
|
|
226
|
+
...(comment.user === undefined ? {} : { user: comment.user }),
|
|
227
|
+
...(comment.actor === undefined ? {} : { actor: comment.actor }),
|
|
228
|
+
});
|
|
229
|
+
why = `${why}; marked migrated, with its origin${comment.user === undefined ? '' : ' and its user'}${comment.actor === undefined ? '' : ' and its actor'}`;
|
|
230
|
+
}
|
|
231
|
+
if (decision.action !== 'leave')
|
|
232
|
+
recorded.set(comment.id, next);
|
|
233
|
+
rows.push({ outcome: decision.outcome, item, path: held.file, why: `${label}: ${why}` });
|
|
234
|
+
}
|
|
235
|
+
await site.putComments(permalink, writes);
|
|
236
|
+
if (recorded.size > 0)
|
|
237
|
+
record.comments.set(held.file, recorded);
|
|
238
|
+
await saveRecord(data, record);
|
|
239
|
+
return rows;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* The record of what the import last wrote on a post, each entry under the
|
|
243
|
+
* comment's key. 0.2.0 recorded each one under its former id; its entry moves
|
|
244
|
+
* to the key, and keeps saying the import wrote what is there only while the
|
|
245
|
+
* comment still is what 0.2.0 wrote.
|
|
246
|
+
*/
|
|
247
|
+
function carryOver(saved, comments, held) {
|
|
248
|
+
const recorded = new Map(saved);
|
|
249
|
+
const carried = new Set();
|
|
250
|
+
const formerIds = new Map(comments.map(({ comment, formerId }) => [comment.id, formerId]));
|
|
251
|
+
for (const { comment, formerId } of comments) {
|
|
252
|
+
const then = recorded.get(formerId);
|
|
253
|
+
if (then === undefined)
|
|
254
|
+
continue;
|
|
255
|
+
recorded.delete(formerId);
|
|
256
|
+
carried.add(comment.id);
|
|
257
|
+
const here = held.get(comment.id);
|
|
258
|
+
const asWritten = here && {
|
|
259
|
+
...here,
|
|
260
|
+
id: formerId,
|
|
261
|
+
inReplyTo: here.inReplyTo === null ? null : (formerIds.get(here.inReplyTo) ?? here.inReplyTo),
|
|
262
|
+
};
|
|
263
|
+
const untouched = asWritten !== undefined && sha256(canonical(asWritten)) === then;
|
|
264
|
+
recorded.set(comment.id, untouched ? sha256(canonical(here)) : then);
|
|
265
|
+
}
|
|
266
|
+
return { recorded, carried };
|
|
267
|
+
}
|
|
268
|
+
function canonical(value) {
|
|
269
|
+
return JSON.stringify(value, (_key, inner) => inner !== null && typeof inner === 'object' && !Array.isArray(inner)
|
|
270
|
+
? Object.fromEntries(Object.entries(inner).sort(([a], [b]) => (a < b ? -1 : 1)))
|
|
271
|
+
: inner);
|
|
272
|
+
}
|
|
273
|
+
function contentPath(given) {
|
|
274
|
+
const normalised = path.posix.normalize(given);
|
|
275
|
+
if (path.posix.isAbsolute(normalised) || normalised.startsWith('../') || normalised === '..') {
|
|
276
|
+
throw new Error(`${given} is outside the content directory; an importer writes only inside it.`);
|
|
277
|
+
}
|
|
278
|
+
return normalised;
|
|
279
|
+
}
|
|
280
|
+
async function readIfPresent(file) {
|
|
281
|
+
try {
|
|
282
|
+
return await readFile(file);
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
if (error.code === 'ENOENT')
|
|
286
|
+
return undefined;
|
|
287
|
+
throw error;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
async function writeAtomically(file, contents) {
|
|
291
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
292
|
+
const temporary = `${file}.${process.pid.toString(36)}.tmp`;
|
|
293
|
+
await writeFile(temporary, contents);
|
|
294
|
+
await rename(temporary, file);
|
|
295
|
+
}
|
|
296
|
+
function sha256(contents) {
|
|
297
|
+
return createHash('sha256').update(contents).digest('hex');
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=content-import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-import.js","sourceRoot":"","sources":["../src/content-import.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAYH,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC9F,YAAY,EACV,qBAAqB,EACrB,WAAW,EACX,2BAA2B,EAC3B,0BAA0B,EAC1B,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,WAAW,EACX,2BAA2B,EAC3B,aAAa,EACb,0BAA0B,EAC1B,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,GACf,MAAM,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { definePlugin } from '@geekity/cms/plugin';
|
|
11
11
|
import { importCommand } from './command.js';
|
|
12
|
+
import { contentImportCommand } from './content-command.js';
|
|
12
13
|
import { wordPressFederation } from './federation.js';
|
|
14
|
+
import { WORDPRESS_UPLOADS_ROUTE, wordPressUploadsRedirect } from './media-redirect.js';
|
|
13
15
|
import { wordPressRecords } from './records.js';
|
|
14
16
|
import { wordPressScreen } from './screen.js';
|
|
15
17
|
import { VERSION } from './version.js';
|
|
@@ -20,13 +22,16 @@ export default definePlugin({
|
|
|
20
22
|
version: VERSION,
|
|
21
23
|
label: 'WordPress',
|
|
22
24
|
description: 'Answers the inbox and collection paths the WordPress ActivityPub plugin published, ' +
|
|
23
|
-
'for a site that moved here from it, until every follower has refetched the actor
|
|
25
|
+
'for a site that moved here from it, until every follower has refetched the actor, ' +
|
|
26
|
+
'and sends its old /wp-content/uploads/ media URLs to the copies under /uploads/.',
|
|
24
27
|
hostApi: 1,
|
|
25
28
|
register(host) {
|
|
26
29
|
const records = wordPressRecords(host.data);
|
|
27
30
|
host.federation(wordPressFederation(records));
|
|
28
31
|
host.screen(wordPressScreen(records));
|
|
29
32
|
host.command(importCommand(records));
|
|
33
|
+
host.command(contentImportCommand(host.data));
|
|
34
|
+
host.get(WORDPRESS_UPLOADS_ROUTE, wordPressUploadsRedirect(host.data));
|
|
30
35
|
},
|
|
31
36
|
});
|
|
32
37
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAQ9F,OAAO,EACL,WAAW,EACX,2BAA2B,EAC3B,aAAa,EACb,0BAA0B,EAC1B,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,cAAc,CAAC;AAQtB,eAAe,YAAY,CAAC;IAC1B,IAAI,EAAE,2BAA2B;IACjC,OAAO,EAAE,OAAO;IAChB,KAAK,EAAE,WAAW;IAClB,WAAW,EACT,qFAAqF;QACrF,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AACxF,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAQ9F,OAAO,EACL,WAAW,EACX,2BAA2B,EAC3B,aAAa,EACb,0BAA0B,EAC1B,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,cAAc,CAAC;AAQtB,eAAe,YAAY,CAAC;IAC1B,IAAI,EAAE,2BAA2B;IACjC,OAAO,EAAE,OAAO;IAChB,KAAK,EAAE,WAAW;IAClB,WAAW,EACT,qFAAqF;QACrF,oFAAoF;QACpF,kFAAkF;IACpF,OAAO,EAAE,CAAC;IACV,QAAQ,CAAC,IAAI;QACX,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC;QAC9C,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;QACtC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;QACrC,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9C,IAAI,CAAC,GAAG,CAAC,uBAAuB,EAAE,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACzE,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-import.d.ts","sourceRoot":"","sources":["../src/media-import.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAKV,iBAAiB,EAClB,MAAM,qBAAqB,CAAC;AAS7B,eAAO,MAAM,WAAW,EAAE,iBAGzB,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { IMPORT_REDIRECTS_FILE } from './content-import.js';
|
|
4
|
+
import { uploadUrl, wordPressMedia } from './media.js';
|
|
5
|
+
import { homeOf } from './posts-import.js';
|
|
6
|
+
const MEDIA_FILE = '_data/media.json';
|
|
7
|
+
export const attachments = {
|
|
8
|
+
postTypes: ['attachment'],
|
|
9
|
+
import: importAttachments,
|
|
10
|
+
};
|
|
11
|
+
async function importAttachments(exported, context) {
|
|
12
|
+
const media = wordPressMedia(exported, context.options['origins']);
|
|
13
|
+
const given = context.options['uploads'];
|
|
14
|
+
if (given === true) {
|
|
15
|
+
throw new Error('--uploads needs the directory, a copy of wp-content/uploads.');
|
|
16
|
+
}
|
|
17
|
+
const wanted = new Map(media.originals);
|
|
18
|
+
for (const item of exported.items) {
|
|
19
|
+
if (item.type !== 'post' && item.type !== 'page')
|
|
20
|
+
continue;
|
|
21
|
+
for (const upload of media.references(item.content)) {
|
|
22
|
+
const original = media.originalOf(upload);
|
|
23
|
+
if (!wanted.has(original))
|
|
24
|
+
wanted.set(original, item);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const files = [];
|
|
28
|
+
const notes = [];
|
|
29
|
+
const entries = [];
|
|
30
|
+
const uploadsDir = given === undefined ? undefined : path.resolve(context.cwd, given);
|
|
31
|
+
const home = homeOf(exported);
|
|
32
|
+
for (const [upload, item] of wanted) {
|
|
33
|
+
if (uploadsDir === undefined) {
|
|
34
|
+
notes.push({
|
|
35
|
+
item,
|
|
36
|
+
outcome: 'warned',
|
|
37
|
+
why: 'not copied: give --uploads <directory>, a copy of wp-content/uploads',
|
|
38
|
+
});
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const bytes = await readIfPresent(path.join(uploadsDir, ...upload.split('/')));
|
|
42
|
+
if (bytes === undefined) {
|
|
43
|
+
notes.push({
|
|
44
|
+
item,
|
|
45
|
+
outcome: 'warned',
|
|
46
|
+
why: `${upload} is not in ${uploadsDir}; ${uploadUrl(upload)} will not answer until it is there`,
|
|
47
|
+
});
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const check = context.site.checkUpload(path.posix.basename(upload), bytes);
|
|
51
|
+
if (!check.accepted) {
|
|
52
|
+
notes.push({ item, outcome: 'warned', why: `${upload} was not copied: ${check.why}` });
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
files.push({ item, path: `uploads/${upload}`, contents: check.bytes });
|
|
56
|
+
if (item.type !== 'attachment')
|
|
57
|
+
continue;
|
|
58
|
+
const alt = item.meta.find((meta) => meta.key === '_wp_attachment_image_alt')?.value.trim();
|
|
59
|
+
if (alt !== undefined && alt !== '') {
|
|
60
|
+
entries.push({ item, file: MEDIA_FILE, key: upload, value: { alt } });
|
|
61
|
+
}
|
|
62
|
+
for (const from of attachmentPages(item, home)) {
|
|
63
|
+
entries.push({ item, file: IMPORT_REDIRECTS_FILE, key: from, value: uploadUrl(upload) });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return { files, notes, entries };
|
|
67
|
+
}
|
|
68
|
+
function attachmentPages(item, home) {
|
|
69
|
+
const pages = new Set([
|
|
70
|
+
`${home.pathname}?attachment_id=${String(item.id)}`,
|
|
71
|
+
`${home.pathname}?p=${String(item.id)}`,
|
|
72
|
+
]);
|
|
73
|
+
const link = URL.parse(item.link);
|
|
74
|
+
if (link !== null && link.host === new URL(home.href).host && link.pathname !== home.pathname) {
|
|
75
|
+
pages.add(`${link.pathname}${link.search}`);
|
|
76
|
+
}
|
|
77
|
+
return [...pages];
|
|
78
|
+
}
|
|
79
|
+
async function readIfPresent(file) {
|
|
80
|
+
try {
|
|
81
|
+
return new Uint8Array(await readFile(file));
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (error.code === 'ENOENT')
|
|
85
|
+
return undefined;
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=media-import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-import.js","sourceRoot":"","sources":["../src/media-import.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { PluginDataFolder, PluginRouteHandler } from '@geekity/cms/plugin';
|
|
2
|
+
export declare const WORDPRESS_UPLOADS_ROUTE = "/wp-content/uploads/*";
|
|
3
|
+
export declare function wordPressUploadsRedirect(data: PluginDataFolder): PluginRouteHandler;
|
|
4
|
+
//# sourceMappingURL=media-redirect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-redirect.d.ts","sourceRoot":"","sources":["../src/media-redirect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAKhF,eAAO,MAAM,uBAAuB,0BAA0B,CAAC;AAE/D,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,gBAAgB,GAAG,kBAAkB,CAyBnF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { IMPORT_RECORD_FILE } from './content-import.js';
|
|
2
|
+
import { decoded, uploadUrl, variantCandidates, WORDPRESS_UPLOADS } from './media.js';
|
|
3
|
+
export const WORDPRESS_UPLOADS_ROUTE = `${WORDPRESS_UPLOADS}*`;
|
|
4
|
+
export function wordPressUploadsRedirect(data) {
|
|
5
|
+
let cached = {
|
|
6
|
+
text: undefined,
|
|
7
|
+
copied: new Set(),
|
|
8
|
+
};
|
|
9
|
+
const copied = () => {
|
|
10
|
+
const text = data.read(IMPORT_RECORD_FILE);
|
|
11
|
+
if (cached.text !== text) {
|
|
12
|
+
const files = text === undefined ? {} : JSON.parse(text).files;
|
|
13
|
+
cached = { text, copied: new Set(Object.keys(files ?? {})) };
|
|
14
|
+
}
|
|
15
|
+
return cached.copied;
|
|
16
|
+
};
|
|
17
|
+
return ({ request }) => {
|
|
18
|
+
const url = new URL(request.url);
|
|
19
|
+
const upload = decoded(url.pathname.slice(WORDPRESS_UPLOADS.length));
|
|
20
|
+
const record = copied();
|
|
21
|
+
const target = variantCandidates(upload).find((candidate) => record.has(`uploads/${candidate}`)) ?? upload;
|
|
22
|
+
return new Response(null, {
|
|
23
|
+
status: 301,
|
|
24
|
+
headers: { location: `${uploadUrl(target)}${url.search}` },
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=media-redirect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-redirect.js","sourceRoot":"","sources":["../src/media-redirect.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEtF,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,iBAAiB,GAAG,CAAC;AAE/D,MAAM,UAAU,wBAAwB,CAAC,IAAsB;IAC7D,IAAI,MAAM,GAA8D;QACtE,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,IAAI,GAAG,EAAE;KAClB,CAAC;IACF,MAAM,MAAM,GAAG,GAAwB,EAAE;QACvC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QAC3C,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAwB,CAAC,KAAK,CAAC;YACvF,MAAM,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QAC/D,CAAC;QACD,OAAO,MAAM,CAAC,MAAM,CAAC;IACvB,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE;QACrB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;QACrE,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;QACxB,MAAM,MAAM,GACV,iBAAiB,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,SAAS,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC;QAC9F,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;YACxB,MAAM,EAAE,GAAG;YACX,OAAO,EAAE,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,EAAE;SAC3D,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/media.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { WordPressExport, WordPressItem } from './wxr.ts';
|
|
2
|
+
export declare const WORDPRESS_UPLOADS = "/wp-content/uploads/";
|
|
3
|
+
export type UploadPath = string;
|
|
4
|
+
export declare function variantCandidates(upload: UploadPath): UploadPath[];
|
|
5
|
+
export declare function uploadUrl(upload: UploadPath): string;
|
|
6
|
+
export interface WordPressMedia {
|
|
7
|
+
readonly originals: ReadonlyMap<UploadPath, WordPressItem>;
|
|
8
|
+
originalOf(upload: UploadPath): UploadPath;
|
|
9
|
+
references(text: string): UploadPath[];
|
|
10
|
+
rewrite(text: string, origin?: string): string;
|
|
11
|
+
}
|
|
12
|
+
export declare function wordPressMedia(exported: WordPressExport, origins: string | true | undefined): WordPressMedia;
|
|
13
|
+
export declare function decoded(text: string): string;
|
|
14
|
+
//# sourceMappingURL=media.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE/D,eAAO,MAAM,iBAAiB,yBAAyB,CAAC;AAIxD,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC;AAQhC,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,EAAE,CAUlE;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAEpD;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;IAC3D,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAAC;IAC3C,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,EAAE,CAAC;IACvC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAChD;AAED,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACjC,cAAc,CA0ChB;AA4CD,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM5C"}
|
package/dist/media.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export const WORDPRESS_UPLOADS = '/wp-content/uploads/';
|
|
2
|
+
const UPLOADS = '/uploads/';
|
|
3
|
+
/**
|
|
4
|
+
* A WordPress size variant's name: `photo-1024x575.png`, or `photo-scaled.png`
|
|
5
|
+
* for the copy WordPress serves in place of a very large original.
|
|
6
|
+
*/
|
|
7
|
+
const VARIANT = /^(.*)-(?:\d+x\d+|scaled)(\.[^./]+)$/i;
|
|
8
|
+
export function variantCandidates(upload) {
|
|
9
|
+
const candidates = [upload];
|
|
10
|
+
for (let match = VARIANT.exec(upload); match !== null; match = VARIANT.exec(candidates.at(-1) ?? '')) {
|
|
11
|
+
candidates.push(`${match[1] ?? ''}${match[2] ?? ''}`);
|
|
12
|
+
}
|
|
13
|
+
return candidates;
|
|
14
|
+
}
|
|
15
|
+
export function uploadUrl(upload) {
|
|
16
|
+
return `${UPLOADS}${encodeURI(upload)}`;
|
|
17
|
+
}
|
|
18
|
+
export function wordPressMedia(exported, origins) {
|
|
19
|
+
const originals = new Map();
|
|
20
|
+
for (const item of exported.items) {
|
|
21
|
+
if (item.type !== 'attachment')
|
|
22
|
+
continue;
|
|
23
|
+
const attached = attachedFile(item);
|
|
24
|
+
if (attached !== undefined)
|
|
25
|
+
originals.set(withoutScaled(attached), item);
|
|
26
|
+
}
|
|
27
|
+
const site = new URL(exported.site.baseSiteUrl || exported.site.link);
|
|
28
|
+
const hosts = new Set([exported.site.link, exported.site.baseSiteUrl, exported.site.baseBlogUrl]
|
|
29
|
+
.filter((url) => URL.canParse(url))
|
|
30
|
+
.map((url) => new URL(url).host.toLowerCase()));
|
|
31
|
+
for (const host of otherHosts(origins))
|
|
32
|
+
hosts.add(host);
|
|
33
|
+
const pattern = uploadsPattern(`${site.pathname.replace(/\/+$/, '')}${WORDPRESS_UPLOADS}`);
|
|
34
|
+
const originalOf = (upload) => variantCandidates(upload).find((candidate) => originals.has(candidate)) ?? upload;
|
|
35
|
+
const each = (text, found) => text.replace(pattern, (match, host, written) => {
|
|
36
|
+
if (host !== undefined && !hosts.has(host.toLowerCase()))
|
|
37
|
+
return match;
|
|
38
|
+
const trimmed = written.replace(/[.,;:!]+$/, '');
|
|
39
|
+
const upload = decoded(trimmed);
|
|
40
|
+
return `${found(upload, match)}${written.slice(trimmed.length)}`;
|
|
41
|
+
});
|
|
42
|
+
return {
|
|
43
|
+
originals,
|
|
44
|
+
originalOf,
|
|
45
|
+
references(text) {
|
|
46
|
+
const found = new Set();
|
|
47
|
+
each(text, (upload, match) => {
|
|
48
|
+
found.add(upload);
|
|
49
|
+
return match;
|
|
50
|
+
});
|
|
51
|
+
return [...found];
|
|
52
|
+
},
|
|
53
|
+
rewrite: (text, origin = '') => each(text, (upload) => `${origin}${uploadUrl(originalOf(upload))}`),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function attachedFile(item) {
|
|
57
|
+
const meta = item.meta.find((entry) => entry.key === '_wp_attached_file')?.value;
|
|
58
|
+
if (meta !== undefined && meta !== '')
|
|
59
|
+
return meta;
|
|
60
|
+
const url = item.attachmentUrl;
|
|
61
|
+
if (url === undefined || !URL.canParse(url))
|
|
62
|
+
return undefined;
|
|
63
|
+
const { pathname } = new URL(url);
|
|
64
|
+
const at = pathname.indexOf(WORDPRESS_UPLOADS);
|
|
65
|
+
return at === -1 ? undefined : decoded(pathname.slice(at + WORDPRESS_UPLOADS.length));
|
|
66
|
+
}
|
|
67
|
+
function withoutScaled(upload) {
|
|
68
|
+
return upload.replace(/-scaled(\.[^./]+)$/i, '$1');
|
|
69
|
+
}
|
|
70
|
+
function otherHosts(origins) {
|
|
71
|
+
if (origins === undefined)
|
|
72
|
+
return [];
|
|
73
|
+
if (origins === true) {
|
|
74
|
+
throw new Error('--origins needs the other origins, such as https://staging.example.com.');
|
|
75
|
+
}
|
|
76
|
+
return origins
|
|
77
|
+
.split(',')
|
|
78
|
+
.map((origin) => origin.trim())
|
|
79
|
+
.filter((origin) => origin !== '')
|
|
80
|
+
.map((origin) => {
|
|
81
|
+
const url = URL.parse(origin.includes('://') ? origin : `https://${origin}`);
|
|
82
|
+
if (url === null)
|
|
83
|
+
throw new Error(`--origins: ${origin} is not an origin.`);
|
|
84
|
+
return url.host.toLowerCase();
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const CAPTURED_HOST = String.raw `(?:https?:)?//([^/\s"'<>()]+)`;
|
|
88
|
+
const NOT_INSIDE_A_LONGER_PATH = String.raw `(?<![\w/.:-])`;
|
|
89
|
+
const CAPTURED_UPLOAD_PATH = String.raw `([^\s"'<>()\[\]\\?#]+)`;
|
|
90
|
+
function uploadsPattern(prefix) {
|
|
91
|
+
const escapedPrefix = prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
92
|
+
return new RegExp(`(?:${CAPTURED_HOST}|${NOT_INSIDE_A_LONGER_PATH})${escapedPrefix}${CAPTURED_UPLOAD_PATH}`, 'gi');
|
|
93
|
+
}
|
|
94
|
+
export function decoded(text) {
|
|
95
|
+
try {
|
|
96
|
+
return decodeURI(text);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return text;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=media.js.map
|