@composius/payload-plugin-import-wordpress 0.1.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 +152 -0
- package/dist/exports/client.d.ts +10 -0
- package/dist/exports/client.js +10 -0
- package/dist/exports/client.js.map +1 -0
- package/dist/index.d.ts +182 -0
- package/dist/index.js +1832 -0
- package/dist/index.js.map +1 -0
- package/package.json +71 -0
package/README.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# @composius/payload-plugin-import-wordpress
|
|
2
|
+
|
|
3
|
+
Imports WordPress posts into Payload via the WordPress REST API: content,
|
|
4
|
+
categories, authors, featured images and in-content images. Images are fetched
|
|
5
|
+
at their original size (and resized by your media collection), uploaded only
|
|
6
|
+
once even when reused across posts, and internal links are rewritten (with 301
|
|
7
|
+
redirects created for the rest). Imports run on the Payload jobs queue and are
|
|
8
|
+
**idempotent and resumable**, writing a full report onto each job document.
|
|
9
|
+
|
|
10
|
+
By default it targets the collections from
|
|
11
|
+
[`@composius/payload-plugin-articles`](../payload-plugin-articles) (`articles`,
|
|
12
|
+
`categories`, `authors`) and a `media` uploads collection.
|
|
13
|
+
|
|
14
|
+
## Collections
|
|
15
|
+
|
|
16
|
+
This plugin adds two collections (grouped under **WordPress import**):
|
|
17
|
+
|
|
18
|
+
| Collection | Purpose |
|
|
19
|
+
| ------------------ | ------------------------------------------------------------------------------------------- |
|
|
20
|
+
| `wp-import-jobs` | The import "form" and report surface. Creating a document starts an import run. |
|
|
21
|
+
| `wp-import-records`| Source→target mappings that make imports idempotent, resumable and image-deduplicating. |
|
|
22
|
+
|
|
23
|
+
### `wp-import-jobs` fields
|
|
24
|
+
|
|
25
|
+
The edit form is organized into one tab per import step:
|
|
26
|
+
|
|
27
|
+
| Tab | Fields |
|
|
28
|
+
| ------------------ | ------------------------------------------------------------------------------------------------------------ |
|
|
29
|
+
| Configuration | `sourceUrl`, `credentials` (`username` + `applicationPassword`, optional), `dateFrom`/`dateTo`, `limit`, `dryRun`, `resume` |
|
|
30
|
+
| Authors | `authorsReport` (read-only) — authors imported by this job. |
|
|
31
|
+
| Categories | `categoriesReport` (read-only) — categories imported, hierarchy preserved. |
|
|
32
|
+
| Media | `mediaReport` (read-only) — images uploaded + how many were reused (deduped). |
|
|
33
|
+
| Posts | `postsReport` (read-only) — posts imported and remaining. |
|
|
34
|
+
| Links & redirects | `linksReport` (read-only) — every internal link: rewritten / redirect created / unresolved. |
|
|
35
|
+
| Report | `runs` + `progress` + `errorsReport` (read-only) — run history, live counts and per-item errors. |
|
|
36
|
+
|
|
37
|
+
`status`, `startedAt` and `finishedAt` sit in the sidebar (read-only).
|
|
38
|
+
|
|
39
|
+
**Run history**: a job can run several times (resume/retry). Each run gets a
|
|
40
|
+
number; `runs` lists every run with its status, timestamps and a final progress
|
|
41
|
+
snapshot, and every reported item (post, category, author, media, link, error)
|
|
42
|
+
carries the `run` that produced it. A resume **merges** with the previous
|
|
43
|
+
reports instead of overwriting them — only entries from dry runs are dropped
|
|
44
|
+
(they were plans, not imports).
|
|
45
|
+
|
|
46
|
+
**Credentials** are optional: a WordPress username + [application password](https://make.wordpress.org/core/2020/11/05/application-passwords-integration-guide/)
|
|
47
|
+
(Users → Profile → Application Passwords). When set, requests are authenticated
|
|
48
|
+
with HTTP Basic auth, which unlocks non-public data — notably **author emails**
|
|
49
|
+
(fetched via `?context=edit`), so the `users` author strategy can use real
|
|
50
|
+
addresses instead of synthesized ones. The password is stored in plain text on
|
|
51
|
+
the job document — delete the job (or clear the field) once the import is done.
|
|
52
|
+
|
|
53
|
+
## What gets imported
|
|
54
|
+
|
|
55
|
+
- **Posts** (published only) → the target content collection, preserving the
|
|
56
|
+
WordPress slug and publish date, created as published.
|
|
57
|
+
- **Categories** → the categories collection, preserving the parent hierarchy.
|
|
58
|
+
Each post is assigned its **primary (first)** category.
|
|
59
|
+
- **Authors** → the users collection by default (matched/created by email,
|
|
60
|
+
linked via `editor`), or the authors collection, or a fixed user.
|
|
61
|
+
- **Featured + in-content images** → the media collection, original size,
|
|
62
|
+
de-duplicated across posts. When the embed is missing or a stub, the featured
|
|
63
|
+
image is re-fetched from the media endpoint; when a post has no featured image
|
|
64
|
+
at all, the first in-content image is promoted to the cover (see
|
|
65
|
+
`firstImageAsCover`), and a cover that also leads the content is removed from
|
|
66
|
+
it so the hero doesn't render twice.
|
|
67
|
+
- **SEO meta** (when the target collection has a `meta` group) — `meta.title`
|
|
68
|
+
from the post title, `meta.image` from the cover image, and
|
|
69
|
+
`meta.description` from the excerpt (see `excerptToSeoDescription`).
|
|
70
|
+
- **Links** — internal links to imported posts are rewritten; every other old
|
|
71
|
+
permalink gets a 301 redirect; anything unresolvable is listed in the report.
|
|
72
|
+
|
|
73
|
+
## Requirements
|
|
74
|
+
|
|
75
|
+
This plugin expects the following to be installed and configured in your project:
|
|
76
|
+
|
|
77
|
+
- `payload` (`^3.84.1`)
|
|
78
|
+
- `@payloadcms/richtext-lexical` (`^3.84.1`) — the target content field's editor.
|
|
79
|
+
- `@payloadcms/ui` (`^3.84.1`) and `react` (`^19.0.0`) — the masked
|
|
80
|
+
application-password input in the admin form.
|
|
81
|
+
- `@payloadcms/plugin-redirects` (`^3.84.1`) — required only when `redirects`
|
|
82
|
+
is enabled (the default); optional otherwise.
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pnpm add @composius/payload-plugin-import-wordpress @payloadcms/richtext-lexical @payloadcms/ui react @payloadcms/plugin-redirects
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
You also need a target content collection with a rich text field (e.g. the
|
|
89
|
+
`articles` collection from `@composius/payload-plugin-articles`), a `media`
|
|
90
|
+
uploads collection, and the Payload **jobs queue** enabled to run (see
|
|
91
|
+
`autoRun` below).
|
|
92
|
+
|
|
93
|
+
## Usage
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import { buildConfig } from 'payload'
|
|
97
|
+
import { ComposiusPayloadPluginArticles } from '@composius/payload-plugin-articles'
|
|
98
|
+
import { ComposiusPayloadPluginMedia } from '@composius/payload-plugin-media'
|
|
99
|
+
import { ComposiusPayloadPluginImportWordpress } from '@composius/payload-plugin-import-wordpress'
|
|
100
|
+
|
|
101
|
+
export default buildConfig({
|
|
102
|
+
// ...
|
|
103
|
+
plugins: [
|
|
104
|
+
ComposiusPayloadPluginMedia(),
|
|
105
|
+
ComposiusPayloadPluginArticles({ authors: true }),
|
|
106
|
+
ComposiusPayloadPluginImportWordpress({
|
|
107
|
+
// Auto-process queued imports so creating a job runs it.
|
|
108
|
+
autoRun: true,
|
|
109
|
+
}),
|
|
110
|
+
],
|
|
111
|
+
})
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Then, in the admin panel, create a **WordPress import** document with the site
|
|
115
|
+
URL (optionally a date range, a limit, or `dryRun`) and watch `status`,
|
|
116
|
+
`progress` and `report` update. Or trigger programmatically:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
curl -X POST /api/wp-import/start \
|
|
120
|
+
-H 'Content-Type: application/json' \
|
|
121
|
+
-b '<auth cookie>' \
|
|
122
|
+
-d '{ "sourceUrl": "https://example.com", "dryRun": true }'
|
|
123
|
+
# → { "jobId": "..." } then poll:
|
|
124
|
+
curl /api/wp-import/status/<jobId>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Options
|
|
128
|
+
|
|
129
|
+
| Option | Type | Default | Description |
|
|
130
|
+
| ------------------------- | ------------------------------------------------ | -------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
131
|
+
| `access` | per-operation access | authenticated | Access control for the jobs/records collections and the endpoints. |
|
|
132
|
+
| `collections` | `{ articles, categories, media, authors, users }`| `articles`/`categories`/`media`/`authors`/`users` | Target collection slugs. |
|
|
133
|
+
| `articleUrl` | `(slug?) => string` | `…/articles/<slug>` | Builds the front-end URL used for redirects and rewritten links. |
|
|
134
|
+
| `authorMapping` | `{ strategy, defaultUserId, syntheticEmailDomain }` | `{ strategy: 'users', syntheticEmailDomain: 'imported.invalid' }` | `users` (default), `authors`, or `fixed`. WordPress's public REST API hides author emails, so `users` synthesizes `<author-slug>@<syntheticEmailDomain>`; set your own domain, or `false` to skip creating such users (falls back to `defaultUserId` and reports the author). |
|
|
135
|
+
| `excerptToSeoDescription` | boolean | `true` | Map the WordPress excerpt onto the article SEO `meta.description`. |
|
|
136
|
+
| `firstImageAsCover` | boolean | `true` | When a post has no usable featured image, promote the first in-content image to the cover and remove it from the content. |
|
|
137
|
+
| `redirects` | boolean | `true` | Create 301 redirects and apply `@payloadcms/plugin-redirects`. |
|
|
138
|
+
| `fieldMap` | article field overrides | `title`/`slug`/`content`/`coverImage`/`category`/`publishedAt` | Article field names the importer writes to. |
|
|
139
|
+
| `autoRun` | boolean \| `{ cron, queue }` | `false` | Auto-process queued imports on a schedule (`true` = every minute). |
|
|
140
|
+
| `dryRunPageLimit` | number | `1` | REST pages a dry run samples. |
|
|
141
|
+
| `request` | `{ concurrency, timeoutMs, userAgent }` | `{ concurrency: 5, timeoutMs: 30000 }` | HTTP tuning for WordPress fetches and image downloads. |
|
|
142
|
+
| `disabled` | boolean | `false` | Keep the collections (schema consistency) but skip endpoints, redirects and auto-run. |
|
|
143
|
+
|
|
144
|
+
## Notes
|
|
145
|
+
|
|
146
|
+
- Only the **primary (first)** WordPress category is assigned to each post
|
|
147
|
+
(the `articles.category` relationship is single); the others are still created
|
|
148
|
+
in the categories collection and noted in the report.
|
|
149
|
+
- The original image is recovered by stripping WordPress's `-WxH` size suffix;
|
|
150
|
+
if the original 404s the importer falls back to the URL WordPress provided.
|
|
151
|
+
- Re-running (or toggling `resume`) skips already-imported items via
|
|
152
|
+
`wp-import-records`, so imports are safe to repeat.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { TextFieldClientComponent } from 'payload';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Renders the WordPress application-password input masked (•••) instead of as
|
|
5
|
+
* plain text, reusing Payload's own password field UI. The value is still a
|
|
6
|
+
* regular text field in the database — this only changes the input widget.
|
|
7
|
+
*/
|
|
8
|
+
declare const ApplicationPasswordFieldClient: TextFieldClientComponent;
|
|
9
|
+
|
|
10
|
+
export { ApplicationPasswordFieldClient };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
// src/components/ApplicationPasswordField.tsx
|
|
4
|
+
import { PasswordField } from "@payloadcms/ui";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
var ApplicationPasswordFieldClient = ({ field, path }) => /* @__PURE__ */ jsx(PasswordField, { autoComplete: "new-password", field, path });
|
|
7
|
+
export {
|
|
8
|
+
ApplicationPasswordFieldClient
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/ApplicationPasswordField.tsx"],"sourcesContent":["'use client'\nimport type { TextFieldClientComponent } from 'payload'\n\nimport { PasswordField } from '@payloadcms/ui'\n\n/**\n * Renders the WordPress application-password input masked (•••) instead of as\n * plain text, reusing Payload's own password field UI. The value is still a\n * regular text field in the database — this only changes the input widget.\n */\nexport const ApplicationPasswordFieldClient: TextFieldClientComponent = ({ field, path }) => (\n <PasswordField autoComplete=\"new-password\" field={field} path={path} />\n)\n"],"mappings":";;;AAGA,SAAS,qBAAqB;AAQ5B;AADK,IAAM,iCAA2D,CAAC,EAAE,OAAO,KAAK,MACrF,oBAAC,iBAAc,cAAa,gBAAe,OAAc,MAAY;","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { Access, Config } from 'payload';
|
|
2
|
+
|
|
3
|
+
type ImportAccess = {
|
|
4
|
+
create?: Access;
|
|
5
|
+
delete?: Access;
|
|
6
|
+
read?: Access;
|
|
7
|
+
update?: Access;
|
|
8
|
+
};
|
|
9
|
+
/** Slugs of the collections the import writes into. */
|
|
10
|
+
type TargetCollections = {
|
|
11
|
+
/** Target content collection for posts. @default 'articles' */
|
|
12
|
+
articles?: string;
|
|
13
|
+
/** Taxonomy collection for categories. @default 'categories' */
|
|
14
|
+
categories?: string;
|
|
15
|
+
/** Uploads collection for images. @default 'media' */
|
|
16
|
+
media?: string;
|
|
17
|
+
/** Authors collection (used when `authorMapping.strategy` is `'authors'`). @default 'authors' */
|
|
18
|
+
authors?: string;
|
|
19
|
+
/** Users collection (used when `authorMapping.strategy` is `'users'`). @default 'users' */
|
|
20
|
+
users?: string;
|
|
21
|
+
};
|
|
22
|
+
type AuthorStrategy = 'authors' | 'fixed' | 'users';
|
|
23
|
+
type AuthorMapping = {
|
|
24
|
+
/**
|
|
25
|
+
* How WordPress authors are mapped:
|
|
26
|
+
* - `users` (default): find-or-create in the users collection (matched by email),
|
|
27
|
+
* linked via the article `editor` field.
|
|
28
|
+
* - `authors`: create in the authors collection, linked via the article `author` field
|
|
29
|
+
* (requires the articles plugin to be configured with `authors: true`).
|
|
30
|
+
* - `fixed`: assign every imported article to `defaultUserId`.
|
|
31
|
+
*/
|
|
32
|
+
strategy?: AuthorStrategy;
|
|
33
|
+
/** User id assigned when `strategy` is `fixed` (or as a fallback when an author has no email). */
|
|
34
|
+
defaultUserId?: number | string;
|
|
35
|
+
/**
|
|
36
|
+
* The public WordPress REST API does not expose author emails, so with the
|
|
37
|
+
* `users` strategy an email is synthesized as `<author-slug>@<domain>`.
|
|
38
|
+
* Configure the domain here, or pass `false` to skip creating users without
|
|
39
|
+
* a real email — the article then falls back to `defaultUserId` (or no
|
|
40
|
+
* editor) and the skipped author is listed in the report.
|
|
41
|
+
* @default 'imported.invalid' (a reserved TLD that can never be a real address)
|
|
42
|
+
*/
|
|
43
|
+
syntheticEmailDomain?: false | string;
|
|
44
|
+
};
|
|
45
|
+
type AutoRunConfig = {
|
|
46
|
+
/** Cron expression for the auto-run schedule. @default '* * * * *' (every minute) */
|
|
47
|
+
cron?: string;
|
|
48
|
+
/** Queue processed by the schedule. @default 'default' */
|
|
49
|
+
queue?: string;
|
|
50
|
+
};
|
|
51
|
+
type FieldMap = {
|
|
52
|
+
/** Article field the post title maps to. @default 'title' */
|
|
53
|
+
title?: string;
|
|
54
|
+
/** Article field the post slug maps to. @default 'slug' */
|
|
55
|
+
slug?: string;
|
|
56
|
+
/** Article rich text field the post content maps to. @default 'content' */
|
|
57
|
+
content?: string;
|
|
58
|
+
/** Article upload field the featured image maps to. @default 'coverImage' */
|
|
59
|
+
coverImage?: string;
|
|
60
|
+
/** Article relationship field the primary category maps to. @default 'category' */
|
|
61
|
+
category?: string;
|
|
62
|
+
/** Article date field the publish date maps to. @default 'publishedAt' */
|
|
63
|
+
publishedAt?: string;
|
|
64
|
+
};
|
|
65
|
+
type RequestOptions = {
|
|
66
|
+
/** Per-image download concurrency. @default 5 */
|
|
67
|
+
concurrency?: number;
|
|
68
|
+
/** Per-request timeout in ms. @default 30000 */
|
|
69
|
+
timeoutMs?: number;
|
|
70
|
+
/** User-Agent header sent to WordPress. */
|
|
71
|
+
userAgent?: string;
|
|
72
|
+
};
|
|
73
|
+
type ComposiusPayloadPluginImportWordpressConfig = {
|
|
74
|
+
/**
|
|
75
|
+
* Access control for the import jobs/records collections, per operation.
|
|
76
|
+
* Defaults: `read`/`create`/`update`/`delete` require an authenticated user.
|
|
77
|
+
*/
|
|
78
|
+
access?: ImportAccess;
|
|
79
|
+
/** Target collection slugs. Defaults: articles/categories/media/authors/users. */
|
|
80
|
+
collections?: TargetCollections;
|
|
81
|
+
/**
|
|
82
|
+
* Builds the front-end URL of an imported article from its slug. Used to
|
|
83
|
+
* rewrite internal links and as the target of created redirects.
|
|
84
|
+
* @default `${NEXT_PUBLIC_SERVER_URL || 'http://localhost:3000'}/articles/${slug}`
|
|
85
|
+
*/
|
|
86
|
+
articleUrl?: (slug?: null | string) => string;
|
|
87
|
+
/** How WordPress authors are mapped. @default { strategy: 'users' } */
|
|
88
|
+
authorMapping?: AuthorMapping;
|
|
89
|
+
/** Map the WordPress excerpt onto the article SEO meta.description. @default true */
|
|
90
|
+
excerptToSeoDescription?: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* When a post has no usable featured image, promote the first in-content
|
|
93
|
+
* image to the cover field and remove it from the content (themes often lead
|
|
94
|
+
* the content with the hero image instead of setting `featured_media`).
|
|
95
|
+
* @default true
|
|
96
|
+
*/
|
|
97
|
+
firstImageAsCover?: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Create 301 redirects (via @payloadcms/plugin-redirects) from old WordPress
|
|
100
|
+
* permalinks to the new article URLs, and apply the redirects plugin.
|
|
101
|
+
* @default true
|
|
102
|
+
*/
|
|
103
|
+
redirects?: boolean;
|
|
104
|
+
/** Override the article field names the importer writes to. */
|
|
105
|
+
fieldMap?: FieldMap;
|
|
106
|
+
/**
|
|
107
|
+
* Automatically process queued imports on a schedule so creating a job runs it
|
|
108
|
+
* without an external worker. Pass `true` for an every-minute schedule or a
|
|
109
|
+
* cron/queue config. @default false (the host app runs the jobs queue).
|
|
110
|
+
*/
|
|
111
|
+
autoRun?: AutoRunConfig | boolean;
|
|
112
|
+
/** Number of REST pages a dry run samples. @default 1 */
|
|
113
|
+
dryRunPageLimit?: number;
|
|
114
|
+
/** HTTP request tuning for WordPress fetches and image downloads. */
|
|
115
|
+
request?: RequestOptions;
|
|
116
|
+
disabled?: boolean;
|
|
117
|
+
};
|
|
118
|
+
type ImportedItem = {
|
|
119
|
+
/** Number of the job run that imported this item. */
|
|
120
|
+
run?: number;
|
|
121
|
+
slug?: string;
|
|
122
|
+
sourceId: number;
|
|
123
|
+
targetId: number | string;
|
|
124
|
+
title?: string;
|
|
125
|
+
};
|
|
126
|
+
type LinkAction = 'redirect' | 'rewritten' | 'unresolved';
|
|
127
|
+
type LinkMapping = {
|
|
128
|
+
action: LinkAction;
|
|
129
|
+
from: string;
|
|
130
|
+
post?: number;
|
|
131
|
+
/** Number of the job run that handled this link. */
|
|
132
|
+
run?: number;
|
|
133
|
+
to?: string;
|
|
134
|
+
};
|
|
135
|
+
type ImportError = {
|
|
136
|
+
message: string;
|
|
137
|
+
/** Number of the job run that hit this error. */
|
|
138
|
+
run?: number;
|
|
139
|
+
scope: string;
|
|
140
|
+
sourceId?: number;
|
|
141
|
+
};
|
|
142
|
+
type ImportReport = {
|
|
143
|
+
dryRun: boolean;
|
|
144
|
+
errors: ImportError[];
|
|
145
|
+
imported: {
|
|
146
|
+
authors: ImportedItem[];
|
|
147
|
+
categories: ImportedItem[];
|
|
148
|
+
media: ImportedItem[];
|
|
149
|
+
posts: ImportedItem[];
|
|
150
|
+
};
|
|
151
|
+
links: LinkMapping[];
|
|
152
|
+
remaining: {
|
|
153
|
+
posts: number;
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
type ImportProgress = {
|
|
157
|
+
currentPhase: string;
|
|
158
|
+
cursorPage: number;
|
|
159
|
+
importedAuthors: number;
|
|
160
|
+
importedCategories: number;
|
|
161
|
+
importedMedia: number;
|
|
162
|
+
importedPosts: number;
|
|
163
|
+
linksRewritten: number;
|
|
164
|
+
linksUnresolved: number;
|
|
165
|
+
redirectsCreated: number;
|
|
166
|
+
reusedMedia: number;
|
|
167
|
+
skippedPosts: number;
|
|
168
|
+
failedPosts: number;
|
|
169
|
+
totalPosts: number;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Imports WordPress posts (via the REST API) into a target content collection —
|
|
174
|
+
* by default the `@composius/payload-plugin-articles` `articles` collection —
|
|
175
|
+
* along with their categories, authors, featured and in-content images. Images
|
|
176
|
+
* are uploaded once and resized by the media collection; internal links are
|
|
177
|
+
* rewritten and the rest get 301 redirects. Runs on the jobs queue and is
|
|
178
|
+
* idempotent + resumable, writing a full report onto each job document.
|
|
179
|
+
*/
|
|
180
|
+
declare const ComposiusPayloadPluginImportWordpress: (pluginOptions?: ComposiusPayloadPluginImportWordpressConfig) => (config: Config) => Promise<Config>;
|
|
181
|
+
|
|
182
|
+
export { ComposiusPayloadPluginImportWordpress, type ComposiusPayloadPluginImportWordpressConfig, type ImportProgress, type ImportReport };
|