create-volt 0.19.0 → 0.21.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/CHANGELOG.md +25 -0
- package/index.js +59 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,29 @@ All notable changes to `create-volt` are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/), and this project adheres to
|
|
5
5
|
[Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [0.21.0] - 2026-06-29
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **`import-wp-db`** — import WordPress content by reading its MySQL/MariaDB
|
|
11
|
+
database directly, for when the REST API is disabled but you have DB access
|
|
12
|
+
(on the server or via an SSH tunnel). `--prefix` for non-default table
|
|
13
|
+
prefixes (validated against SQL injection); creds via `WP_DB_URL` to keep them
|
|
14
|
+
out of shell history; `mysql2` loaded lazily. Reuses the WXR converter; unit
|
|
15
|
+
tested with an injected connection. Third migration path alongside
|
|
16
|
+
`import-wp` (REST) and `import-wxr` (file).
|
|
17
|
+
|
|
18
|
+
## [0.20.0] - 2026-06-29
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- **`import-wp` — fully-automated WordPress import over the REST API.**
|
|
22
|
+
`npx create-volt import-wp <https://site>` pulls published posts + pages
|
|
23
|
+
directly (paginated) into markdown `pages/` — no export file, and **no
|
|
24
|
+
credentials for public content**. Drafts/private need an Application Password
|
|
25
|
+
via `WP_USER` + `WP_APP_PASSWORD` (Basic auth, **sent only over HTTPS, never
|
|
26
|
+
logged**). Falls back to `import-wxr` if the REST API is disabled. Reuses the
|
|
27
|
+
WXR→markdown converter; unit-tested with a mocked fetch. `/docs/migrate`
|
|
28
|
+
updated to lead with the automated path.
|
|
29
|
+
|
|
7
30
|
## [0.19.0] - 2026-06-29
|
|
8
31
|
|
|
9
32
|
### Added
|
|
@@ -278,6 +301,8 @@ All notable changes to `create-volt` are documented here. The format follows
|
|
|
278
301
|
watching and full-page hot reload. Supports `--skip-install` and `--force`,
|
|
279
302
|
and auto-detects npm / pnpm / yarn / bun for the install step.
|
|
280
303
|
|
|
304
|
+
[0.21.0]: https://github.com/MIR-2025/volt/releases/tag/v0.21.0
|
|
305
|
+
[0.20.0]: https://github.com/MIR-2025/volt/releases/tag/v0.20.0
|
|
281
306
|
[0.19.0]: https://github.com/MIR-2025/volt/releases/tag/v0.19.0
|
|
282
307
|
[0.18.0]: https://github.com/MIR-2025/volt/releases/tag/v0.18.0
|
|
283
308
|
[0.17.0]: https://github.com/MIR-2025/volt/releases/tag/v0.17.0
|
package/index.js
CHANGED
|
@@ -62,6 +62,8 @@ const positionals = [];
|
|
|
62
62
|
let portArg = null;
|
|
63
63
|
let templateArg = null;
|
|
64
64
|
let outArg = null;
|
|
65
|
+
let userArg = null;
|
|
66
|
+
let prefixArg = null;
|
|
65
67
|
for (let i = 0; i < argv.length; i++) {
|
|
66
68
|
const a = argv[i];
|
|
67
69
|
if (a === "--port") portArg = argv[++i];
|
|
@@ -70,6 +72,10 @@ for (let i = 0; i < argv.length; i++) {
|
|
|
70
72
|
else if (a.startsWith("--template=")) templateArg = a.slice("--template=".length);
|
|
71
73
|
else if (a === "--out") outArg = argv[++i];
|
|
72
74
|
else if (a.startsWith("--out=")) outArg = a.slice("--out=".length);
|
|
75
|
+
else if (a === "--user") userArg = argv[++i];
|
|
76
|
+
else if (a.startsWith("--user=")) userArg = a.slice("--user=".length);
|
|
77
|
+
else if (a === "--prefix") prefixArg = argv[++i];
|
|
78
|
+
else if (a.startsWith("--prefix=")) prefixArg = a.slice("--prefix=".length);
|
|
73
79
|
else if (a.startsWith("-")) flags.add(a);
|
|
74
80
|
else positionals.push(a);
|
|
75
81
|
}
|
|
@@ -135,14 +141,8 @@ if (positionals[0] === "config") {
|
|
|
135
141
|
process.exit(res.status ?? 0);
|
|
136
142
|
}
|
|
137
143
|
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
const xmlPath = positionals[1];
|
|
141
|
-
if (!xmlPath) die(`Usage: ${cyan("create-volt import-wxr <export.xml>")} [--out pages] [--drafts] [--force]`);
|
|
142
|
-
if (!fs.existsSync(xmlPath)) die(`No such file: ${cyan(xmlPath)}`);
|
|
143
|
-
const outDir = path.resolve(outArg || "pages");
|
|
144
|
-
const { runImport } = await import("./lib/import-wxr.js");
|
|
145
|
-
const { imported, stats } = runImport(fs.readFileSync(xmlPath, "utf8"), { drafts: flags.has("--drafts") });
|
|
144
|
+
// Write imported markdown pages to disk and print a summary (shared by both importers).
|
|
145
|
+
function emitImported(imported, stats, outDir) {
|
|
146
146
|
fs.mkdirSync(outDir, { recursive: true });
|
|
147
147
|
let written = 0;
|
|
148
148
|
let skippedExisting = 0;
|
|
@@ -160,6 +160,57 @@ if (positionals[0] === "import-wxr") {
|
|
|
160
160
|
console.log(`\n${cyan(`✓ Imported ${written}`)} page(s) → ${outDir}`);
|
|
161
161
|
console.log(dim(` source: ${stats.total} items (${types}); skipped ${stats.draftsSkipped} draft(s), ${stats.otherTypeSkipped} non-page/post item(s)${skippedExisting ? `, ${skippedExisting} already-present (use --force)` : ""}.`));
|
|
162
162
|
console.log(dim(" Enable the pages add-on to serve them: npm run dev -- --edit"));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// --- `import-wxr` subcommand: import an offline WordPress export (WXR file) ---
|
|
166
|
+
if (positionals[0] === "import-wxr") {
|
|
167
|
+
const xmlPath = positionals[1];
|
|
168
|
+
if (!xmlPath) die(`Usage: ${cyan("create-volt import-wxr <export.xml>")} [--out pages] [--drafts] [--force]`);
|
|
169
|
+
if (!fs.existsSync(xmlPath)) die(`No such file: ${cyan(xmlPath)}`);
|
|
170
|
+
const { runImport } = await import("./lib/import-wxr.js");
|
|
171
|
+
const { imported, stats } = runImport(fs.readFileSync(xmlPath, "utf8"), { drafts: flags.has("--drafts") });
|
|
172
|
+
emitImported(imported, stats, path.resolve(outArg || "pages"));
|
|
173
|
+
process.exit(0);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// --- `import-wp` subcommand: pull a live WordPress site over the REST API ---
|
|
177
|
+
if (positionals[0] === "import-wp") {
|
|
178
|
+
const site = positionals[1];
|
|
179
|
+
if (!site || !/^https?:\/\//i.test(site)) die(`Usage: ${cyan("create-volt import-wp <https://site.com>")} [--out pages] [--drafts] [--user U]\n Credentials (for drafts/private): set ${cyan("WP_APP_PASSWORD")} (an Application Password) and ${cyan("WP_USER")} or --user.`);
|
|
180
|
+
const user = userArg || process.env.WP_USER;
|
|
181
|
+
const appPassword = process.env.WP_APP_PASSWORD;
|
|
182
|
+
if ((user || appPassword) && !/^https:\/\//i.test(site)) die("Refusing to send credentials over a non-HTTPS URL.");
|
|
183
|
+
const { runImportFromWP } = await import("./lib/import-wxr.js");
|
|
184
|
+
console.log(dim(`Fetching ${site} via the WordPress REST API…${appPassword ? " (authenticated)" : ""}`));
|
|
185
|
+
let result;
|
|
186
|
+
try {
|
|
187
|
+
result = await runImportFromWP(site, { user, appPassword, drafts: flags.has("--drafts") });
|
|
188
|
+
} catch (e) {
|
|
189
|
+
die(`${e.message}\n If the REST API is disabled, export a WXR file and use ${cyan("create-volt import-wxr <export.xml>")}.`);
|
|
190
|
+
}
|
|
191
|
+
emitImported(result.imported, result.stats, path.resolve(outArg || "pages"));
|
|
192
|
+
process.exit(0);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// --- `import-wp-db` subcommand: read a WordPress MySQL database directly ---
|
|
196
|
+
if (positionals[0] === "import-wp-db") {
|
|
197
|
+
const dbUrl = positionals[1] || process.env.WP_DB_URL || process.env.DATABASE_URL;
|
|
198
|
+
if (!dbUrl) {
|
|
199
|
+
die(
|
|
200
|
+
`Usage: ${cyan("create-volt import-wp-db <mysql://user:pass@host/db>")} [--prefix wp_] [--out pages] [--drafts] [--force]\n` +
|
|
201
|
+
` Tip: set ${cyan("WP_DB_URL")} instead of passing the URL, so credentials stay out of shell history.\n` +
|
|
202
|
+
` WordPress DBs are usually firewalled to localhost — run this on the server or over an SSH tunnel. Requires ${cyan("mysql2")} (npm i mysql2).`,
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
const { runImportFromDB } = await import("./lib/import-wp-db.js");
|
|
206
|
+
console.log(dim(`Reading WordPress database (prefix ${prefixArg || "wp_"})…`));
|
|
207
|
+
let result;
|
|
208
|
+
try {
|
|
209
|
+
result = await runImportFromDB(dbUrl, { prefix: prefixArg || "wp_", drafts: flags.has("--drafts") });
|
|
210
|
+
} catch (e) {
|
|
211
|
+
die(e.message);
|
|
212
|
+
}
|
|
213
|
+
emitImported(result.imported, result.stats, path.resolve(outArg || "pages"));
|
|
163
214
|
process.exit(0);
|
|
164
215
|
}
|
|
165
216
|
|