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.
Files changed (3) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/index.js +59 -8
  3. 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
- // --- `import-wxr` subcommand: import a WordPress export into markdown pages ---
139
- if (positionals[0] === "import-wxr") {
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-volt",
3
- "version": "0.19.0",
3
+ "version": "0.21.0",
4
4
  "description": "Scaffold a new Volt app — no-build, signals-based UI with Socket.io hot reload.",
5
5
  "type": "module",
6
6
  "bin": {