kizlo 0.9.0 → 0.10.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/LICENSE +201 -21
- package/dist/cli/commands/_setup.js +121 -77
- package/dist/cli/commands/_wiring.js +5 -4
- package/dist/cli/commands/create.js +32 -25
- package/dist/cli/commands/dev.js +63 -71
- package/dist/cli/commands/init.js +93 -103
- package/dist/cli/commands/test.js +34 -34
- package/dist/cli/daemon/config.js +18 -24
- package/dist/cli/presets/base.js +4 -2
- package/dist/cli/presets/nextjs.js +0 -1
- package/dist/cli/presets/template.js +76 -2
- package/dist/cli/utils.js +41 -19
- package/dist/cli/wp/compose/override.js +3 -3
- package/dist/cli/wp/compose/override.test.ts +2 -2
- package/dist/cli/wp/compose/override.ts +3 -3
- package/dist/cli/wp/constants.d.ts +10 -4
- package/dist/cli/wp/constants.js +10 -4
- package/dist/cli/wp/dev.js +13 -58
- package/dist/cli/wp/docker.d.ts +16 -4
- package/dist/cli/wp/docker.js +20 -7
- package/dist/cli/wp/settings.js +1 -1
- package/dist/cli/wp/setup.js +1 -1
- package/dist/cli/wp/stack.js +4 -4
- package/dist/config.d.ts +20 -23
- package/dist/index.d.ts +2 -2
- package/dist/integrations/nextjs/kizlo.server.js +1 -1
- package/dist/integrations/nextjs/utils.js +2 -2
- package/dist/kizlo.d.ts +16 -16
- package/dist/kizlo.js +19 -15
- package/dist/package.js +1 -1
- package/dist/router.d.ts +5 -5
- package/dist/test/server.js +1 -1
- package/dist/test.d.ts +3 -3
- package/dist/test.js +3 -3
- package/package.json +3 -3
- package/dist/cli/wp/byo.js +0 -113
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { availablePackageManagers, detectInvokingPackageManager, ensureGitignored, frameworkCreateArgs, getVersion, installArgs, runCommandAsync, runCommandCaptured } from "../utils.js";
|
|
1
|
+
import { availablePackageManagers, detectInvokingPackageManager, ensureGitignored, frameworkCreateArgs, installArgs, runCommandAsync, runCommandCaptured } from "../utils.js";
|
|
3
2
|
import { getPreset } from "../presets/index.js";
|
|
4
3
|
import { fetchTemplate } from "../presets/source.js";
|
|
5
|
-
import { adaptFile, changesFor, fileEntries, isExample, patchEntries, readManifest } from "../presets/template.js";
|
|
6
|
-
import { collectConnectionInteractively, nextStepsLines, orCancel, pickAppPort, setupLocalWordPress, syncRemote, validate, withApiPath, writeEnv } from "./_setup.js";
|
|
4
|
+
import { adaptFile, changesFor, fileEntries, isExample, minCliError, patchEntries, readManifest, resolveDependencies } from "../presets/template.js";
|
|
5
|
+
import { collectConnectionInteractively, nextStepsLines, orCancel, pickAppPort, readEnvExample, resolveEnvKeys, setupLocalWordPress, syncRemote, validate, withApiPath, writeEnv } from "./_setup.js";
|
|
7
6
|
import { applyLayoutPatches, buildScaffoldContext, kizloConfigTemplate, reportScaffold, scaffoldFile, writeGeneratedContract } from "./_wiring.js";
|
|
8
7
|
import z from "zod/v4";
|
|
9
8
|
import path from "node:path";
|
|
@@ -29,16 +28,23 @@ const PACKAGE_MANAGERS = [
|
|
|
29
28
|
"bun"
|
|
30
29
|
];
|
|
31
30
|
/**
|
|
32
|
-
* Pin
|
|
33
|
-
*
|
|
34
|
-
* the running CLI's version in-repo before the first stamp. The
|
|
35
|
-
* installed — the getting-started note tells the user to run install.
|
|
31
|
+
* Pin Kizlo's dependencies in the freshly bootstrapped `package.json`. The template is authoritative
|
|
32
|
+
* for the versions (its manifest's `dependencies`/`devDependencies`, stamped at release), falling back
|
|
33
|
+
* to the running CLI's version for `kizlo` in-repo before the first stamp. The deps are only recorded,
|
|
34
|
+
* not installed — the getting-started note tells the user to run install.
|
|
36
35
|
*/
|
|
37
|
-
function
|
|
36
|
+
function recordDependencies(dir, manifest) {
|
|
38
37
|
const pkgPath = path.join(dir, "package.json");
|
|
39
38
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
40
|
-
|
|
41
|
-
pkg.dependencies
|
|
39
|
+
const { dependencies, devDependencies } = resolveDependencies(manifest);
|
|
40
|
+
pkg.dependencies = {
|
|
41
|
+
...pkg.dependencies,
|
|
42
|
+
...dependencies
|
|
43
|
+
};
|
|
44
|
+
if (Object.keys(devDependencies).length) pkg.devDependencies = {
|
|
45
|
+
...pkg.devDependencies,
|
|
46
|
+
...devDependencies
|
|
47
|
+
};
|
|
42
48
|
fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, " ")}\n`);
|
|
43
49
|
}
|
|
44
50
|
/**
|
|
@@ -59,7 +65,7 @@ function bootstrapArgs(manifest, pm, name) {
|
|
|
59
65
|
* tsconfig, the root layout); this drives the manifest — the same engine `init` uses — to record the
|
|
60
66
|
* `kizlo` dependency, write `kizlo.config.ts`, scaffold the manifest's files (the `base` plumbing and
|
|
61
67
|
* the `create` layout/styles always, the `example`-flagged demo pages only when `includeExamples`),
|
|
62
|
-
* seed the generated contract, and ignore `.env`. On a fresh app Kizlo owns the layout, so `create`
|
|
68
|
+
* seed the generated contract, and ignore `.env` and the `.kizlo/` working dir (which holds the local WordPress install). On a fresh app Kizlo owns the layout, so `create`
|
|
63
69
|
* writes it whole (already SEO-wired) rather than patching — `applyLayoutPatches` runs over `create`'s
|
|
64
70
|
* patches, which are none today. The target directory layout comes straight from the manifest's
|
|
65
71
|
* conventions, which the bootstrap flags are chosen to match, so files land where they expect. Fresh
|
|
@@ -73,12 +79,12 @@ async function applyManifestWiring(dir, templateDir, manifest, opts) {
|
|
|
73
79
|
alias,
|
|
74
80
|
clientUrl: void 0
|
|
75
81
|
});
|
|
76
|
-
|
|
82
|
+
recordDependencies(dir, manifest);
|
|
77
83
|
const changes = changesFor(manifest, "create").filter((change) => opts.includeExamples || !isExample(change));
|
|
78
84
|
const files = [{
|
|
79
85
|
label: "Kizlo config",
|
|
80
86
|
relPath: "kizlo.config.ts",
|
|
81
|
-
contents: kizloConfigTemplate(kizloDir, alias, opts.
|
|
87
|
+
contents: kizloConfigTemplate(kizloDir, alias, opts.localDev)
|
|
82
88
|
}, ...fileEntries(changes).map((entry) => adaptFile(templateDir, entry, manifest.conventions, scaffold))];
|
|
83
89
|
for (const file of files) reportScaffold(file, await scaffoldFile(dir, file, {
|
|
84
90
|
force: true,
|
|
@@ -86,6 +92,7 @@ async function applyManifestWiring(dir, templateDir, manifest, opts) {
|
|
|
86
92
|
}), false);
|
|
87
93
|
writeGeneratedContract(dir, path.join(kizloDir, "server"));
|
|
88
94
|
ensureGitignored(dir, ".env");
|
|
95
|
+
ensureGitignored(dir, ".kizlo/");
|
|
89
96
|
applyLayoutPatches(dir, patchEntries(changes), manifest.conventions, scaffold);
|
|
90
97
|
}
|
|
91
98
|
const create = defineCommand({
|
|
@@ -156,16 +163,12 @@ const create = defineCommand({
|
|
|
156
163
|
})),
|
|
157
164
|
initialValue: defaultPm
|
|
158
165
|
}));
|
|
166
|
+
const conn = await collectConnectionInteractively(preset, { baseUrl: `http://localhost:${await pickAppPort()}` });
|
|
167
|
+
if (preset.apiPath && conn.baseUrl) conn.baseUrl = withApiPath(conn.baseUrl, preset.apiPath);
|
|
159
168
|
const includeExamples = orCancel(await p.confirm({
|
|
160
|
-
message: "Add
|
|
169
|
+
message: "Add examples?",
|
|
161
170
|
initialValue: true
|
|
162
171
|
}));
|
|
163
|
-
const conn = await collectConnectionInteractively(preset, { baseUrl: `http://localhost:${await pickAppPort()}` });
|
|
164
|
-
if (preset.apiPath && conn.baseUrl) conn.baseUrl = withApiPath(conn.baseUrl, preset.apiPath);
|
|
165
|
-
if (conn.mode === "local" && !await dockerAvailable()) {
|
|
166
|
-
p.cancel("Docker isn't available — start Docker (or install it) and re-run, or choose “Use my own WordPress”.");
|
|
167
|
-
process.exit(1);
|
|
168
|
-
}
|
|
169
172
|
const fetched = await fetchTemplate(template);
|
|
170
173
|
const fail = (message) => {
|
|
171
174
|
fetched.cleanup();
|
|
@@ -173,6 +176,8 @@ const create = defineCommand({
|
|
|
173
176
|
process.exit(1);
|
|
174
177
|
};
|
|
175
178
|
const manifest = readManifest(fetched.dir);
|
|
179
|
+
const minErr = minCliError(manifest);
|
|
180
|
+
if (minErr) fail(minErr);
|
|
176
181
|
const bootstrap = bootstrapArgs(manifest, pm, name);
|
|
177
182
|
if (!bootstrap) {
|
|
178
183
|
fetched.cleanup();
|
|
@@ -187,10 +192,11 @@ const create = defineCommand({
|
|
|
187
192
|
if (scaffold.output) p.log.error(scaffold.output);
|
|
188
193
|
fail(`${preset.label} setup failed — see the output above and try again.`);
|
|
189
194
|
}
|
|
195
|
+
const exampleTemplate = readEnvExample(fetched.dir);
|
|
190
196
|
try {
|
|
191
197
|
await applyManifestWiring(dir, fetched.dir, manifest, {
|
|
192
|
-
|
|
193
|
-
|
|
198
|
+
includeExamples,
|
|
199
|
+
localDev: conn.mode === "local"
|
|
194
200
|
});
|
|
195
201
|
} catch (error) {
|
|
196
202
|
fail(error instanceof Error ? error.message : String(error));
|
|
@@ -208,9 +214,10 @@ const create = defineCommand({
|
|
|
208
214
|
if (!depsInstalled) p.log.warn(`Install them yourself: cd ${name} && ${installArgs(pm).join(" ")}`);
|
|
209
215
|
}
|
|
210
216
|
await setupLocalWordPress(dir, conn);
|
|
211
|
-
await writeEnv(dir, preset, conn, {
|
|
217
|
+
await writeEnv(dir, resolveEnvKeys(preset, manifest), conn, {
|
|
212
218
|
force: true,
|
|
213
|
-
yes: false
|
|
219
|
+
yes: false,
|
|
220
|
+
exampleTemplate
|
|
214
221
|
});
|
|
215
222
|
await syncRemote(conn);
|
|
216
223
|
p.note([
|
package/dist/cli/commands/dev.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createStack } from "../wp/docker.js";
|
|
2
|
-
import { ensureGitignored, envGroups, groupDefault, mergeEnv, pickStackPort, withSpinner } from "../utils.js";
|
|
3
|
-
import {
|
|
1
|
+
import { createStack, dockerHint, dockerStatus } from "../wp/docker.js";
|
|
2
|
+
import { DEFAULT_ENV_KEYS, ensureGitignored, envGroups, groupDefault, mergeEnv, pickStackPort, withSpinner } from "../utils.js";
|
|
3
|
+
import { resolveDevConfig, usesLocalWordPress } from "../daemon/config.js";
|
|
4
4
|
import { bootstrapDev } from "../wp/dev.js";
|
|
5
5
|
import { reapOrphans, registerSession, removeProjectContainers, spawnWatchdog, unregisterSession } from "../wp/session.js";
|
|
6
6
|
import { syncSiteSettings } from "../wp/settings.js";
|
|
@@ -21,15 +21,13 @@ async function resolve$1(cwd) {
|
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
* The
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* print a plain note explaining there's nothing to do and exit cleanly. Returns true when a
|
|
28
|
-
* local stack is configured and the caller may proceed.
|
|
24
|
+
* The lifecycle subcommands (`stop`/`reset`) only act on local WordPress. A project pointing at its
|
|
25
|
+
* own WordPress (no `dev.local`) has nothing to manage, so print a plain note and exit cleanly. Returns
|
|
26
|
+
* true when local WordPress is configured and the caller may proceed.
|
|
29
27
|
*/
|
|
30
|
-
async function
|
|
31
|
-
if (await
|
|
32
|
-
note("
|
|
28
|
+
async function requireLocalWordPress(cwd) {
|
|
29
|
+
if (await usesLocalWordPress(cwd)) return true;
|
|
30
|
+
note("This project doesn't run local WordPress — set `dev.local: true` in kizlo.config.ts to add it.");
|
|
33
31
|
return false;
|
|
34
32
|
}
|
|
35
33
|
/** A dim, untagged one-liner (no consola timestamp) for the clean dev output. */
|
|
@@ -39,7 +37,7 @@ function note(text) {
|
|
|
39
37
|
}
|
|
40
38
|
/**
|
|
41
39
|
* Print the connection summary as an aligned, Next.js-style block. Shows the wp-admin URL on
|
|
42
|
-
* the loopback ("Local") and — when
|
|
40
|
+
* the loopback ("Local") and — when local WordPress is installed on a LAN address rather than
|
|
43
41
|
* localhost — on the network, so it can be opened from other devices. On a fresh install, also
|
|
44
42
|
* show the one-time wp-admin password (it's never stored, so this is the only chance to see it).
|
|
45
43
|
*/
|
|
@@ -52,57 +50,56 @@ function printSummary(info) {
|
|
|
52
50
|
const width = Math.max(...rows.map(([label]) => label.length)) + 1;
|
|
53
51
|
const lines = ["", ...rows.map(([label, value]) => ` ${dim}- ${`${label}:`.padEnd(width)}${reset} ${cyan}${value}${reset}`)];
|
|
54
52
|
if (info.secrets) lines.push("", ` ${dim}admin login:${reset} ${info.username} / ${info.secrets.password}`, ` ${dim}save the password now — it's shown only once${reset}`);
|
|
55
|
-
else if (info.imported) lines.push("", ` ${dim}log in with your imported site's own credentials${reset}`);
|
|
56
53
|
if (info.seeded > 0) lines.push(` ${dim}seeded ${info.seeded} fixture${info.seeded === 1 ? "" : "s"}${reset}`);
|
|
57
54
|
process.stdout.write(`${lines.join("\n")}\n`);
|
|
58
55
|
}
|
|
59
56
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* touched by `kizlo dev`, so pointing them at a real site survives booting
|
|
57
|
+
* Local-WordPress connection keys a fresh install must rewrite (the rest of `.env` is untouched). The
|
|
58
|
+
* remote `KIZLO_WP_*` / `KIZLO_WP_SECRET` keys live under their own names and are never
|
|
59
|
+
* touched by `kizlo dev`, so pointing them at a real site survives booting local WordPress.
|
|
63
60
|
*/
|
|
64
61
|
const WP_ENV_KEYS = [
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
62
|
+
"KIZLO_LOCAL_WP_URL",
|
|
63
|
+
"KIZLO_LOCAL_WP_USERNAME",
|
|
64
|
+
"KIZLO_LOCAL_WP_APP_PASSWORD"
|
|
68
65
|
];
|
|
69
66
|
/** Read a key's value from raw `.env` content (first match, trimmed); undefined when absent or empty. */
|
|
70
67
|
function readEnvValue(content, key) {
|
|
71
68
|
return content.match(new RegExp(`^\\s*${key}\\s*=\\s*(.*)$`, "m"))?.[1]?.trim() || void 0;
|
|
72
69
|
}
|
|
73
70
|
/**
|
|
74
|
-
* The Kizlo backend URL from `.env` — the one key ending in `
|
|
75
|
-
* or a framework-prefixed `
|
|
71
|
+
* The Kizlo backend URL from `.env` — the one key ending in `KIZLO_API_URL` (`KIZLO_API_URL`,
|
|
72
|
+
* or a framework-prefixed `NEXT_PUBLIC_KIZLO_API_URL`). It's the handler mount the plugin delivers
|
|
76
73
|
* webhook events to; its origin is the site's canonical URL. User-managed (init writes it), so dev
|
|
77
74
|
* only reads it. Undefined before `kizlo init` has populated `.env`.
|
|
78
75
|
*/
|
|
79
76
|
function readBaseUrl(content) {
|
|
80
|
-
return content.match(/^\s*[A-Z_]*
|
|
77
|
+
return content.match(/^\s*[A-Z_]*KIZLO_API_URL\s*=\s*(.*)$/m)?.[1]?.trim() || void 0;
|
|
81
78
|
}
|
|
82
79
|
/**
|
|
83
|
-
* Rewrite the
|
|
80
|
+
* Rewrite the local WordPress connection in `.env` after a fresh install. A reset (or first boot)
|
|
84
81
|
* wipes the database, so the previous application password — and possibly the port — no longer
|
|
85
82
|
* match; without this the app keeps the stale credentials and REST auth fails. Only the three
|
|
86
83
|
* connection keys are overwritten; everything else in `.env` is preserved, and the file is created
|
|
87
84
|
* if absent. Gated by the caller on a minted `appPassword`, so a warm resume never churns it.
|
|
88
85
|
*
|
|
89
|
-
* `
|
|
90
|
-
* value the user already set is preserved — booting
|
|
91
|
-
* resolved
|
|
92
|
-
* returned so the caller can sync them into the plugin.
|
|
86
|
+
* `KIZLO_CONNECT=local` and `KIZLO_LOCAL_WP_SECRET` are written too but kept out of the overwrite set,
|
|
87
|
+
* so a value the user already set is preserved — booting local WordPress only fills them when absent.
|
|
88
|
+
* The resolved local secret (the existing one, or a freshly minted one) and the Kizlo server base URL
|
|
89
|
+
* are returned so the caller can sync them into the plugin.
|
|
93
90
|
*/
|
|
94
91
|
function updateWpEnv(cfg, info) {
|
|
95
92
|
if (!info.appPassword) return void 0;
|
|
96
93
|
const envPath = join(cfg.configDir, ".env");
|
|
97
94
|
const existing = existsSync(envPath) ? readFileSync(envPath, "utf8") : "";
|
|
98
|
-
const siteSecret = readEnvValue(existing, "
|
|
95
|
+
const siteSecret = readEnvValue(existing, "KIZLO_LOCAL_WP_SECRET") ?? randomBytes(32).toString("hex");
|
|
99
96
|
const { content } = mergeEnv(existing, {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}, new Set(WP_ENV_KEYS), envGroups(
|
|
97
|
+
KIZLO_LOCAL_WP_URL: info.url,
|
|
98
|
+
KIZLO_LOCAL_WP_USERNAME: info.username,
|
|
99
|
+
KIZLO_LOCAL_WP_APP_PASSWORD: info.appPassword,
|
|
100
|
+
KIZLO_LOCAL_WP_SECRET: siteSecret,
|
|
101
|
+
KIZLO_CONNECT: "local"
|
|
102
|
+
}, new Set(WP_ENV_KEYS), envGroups(DEFAULT_ENV_KEYS));
|
|
106
103
|
writeFileSync(envPath, content);
|
|
107
104
|
return {
|
|
108
105
|
siteSecret,
|
|
@@ -110,19 +107,19 @@ function updateWpEnv(cfg, info) {
|
|
|
110
107
|
};
|
|
111
108
|
}
|
|
112
109
|
/**
|
|
113
|
-
* Re-point `
|
|
110
|
+
* Re-point `KIZLO_LOCAL_WP_URL` at the current URL on a warm resume, so a LAN address that
|
|
114
111
|
* changed since the last session (a new DHCP lease, a different network) doesn't leave the app talking
|
|
115
112
|
* to a dead IP. Only this one key is rewritten — the credentials and secret from the fresh install are
|
|
116
113
|
* preserved — and it's a no-op when `.env` is absent (nothing provisioned yet) or already current.
|
|
117
114
|
* The full fresh-install write goes through {@link updateWpEnv} instead; this covers the resume path it
|
|
118
115
|
* skips. Returns true when it changed the file. The app still needs a restart to read the new value.
|
|
119
116
|
*/
|
|
120
|
-
function
|
|
117
|
+
function syncLocalUrl(cfg, url) {
|
|
121
118
|
const envPath = join(cfg.configDir, ".env");
|
|
122
119
|
if (!existsSync(envPath)) return false;
|
|
123
120
|
const existing = readFileSync(envPath, "utf8");
|
|
124
|
-
if (readEnvValue(existing, "
|
|
125
|
-
const { content } = mergeEnv(existing, {
|
|
121
|
+
if (readEnvValue(existing, "KIZLO_LOCAL_WP_URL") === url) return false;
|
|
122
|
+
const { content } = mergeEnv(existing, { KIZLO_LOCAL_WP_URL: url }, new Set(["KIZLO_LOCAL_WP_URL"]), envGroups(DEFAULT_ENV_KEYS));
|
|
126
123
|
writeFileSync(envPath, content);
|
|
127
124
|
return true;
|
|
128
125
|
}
|
|
@@ -134,7 +131,7 @@ function syncDevUrl(cfg, url) {
|
|
|
134
131
|
* `next dev` child server — nothing stops them automatically when we die. A dying process
|
|
135
132
|
* also can't be trusted to finish async work (a closed terminal breaks its stdout
|
|
136
133
|
* mid-write), so this process does no docker work on the way out. It only needs to
|
|
137
|
-
* *exit*; the watchdog, watching our PID from its own session, stops
|
|
134
|
+
* *exit*; the watchdog, watching our PID from its own session, stops it the moment
|
|
138
135
|
* we're gone — by Ctrl+C, SIGTERM, a closed tab, SIGKILL, or a crash alike.
|
|
139
136
|
*
|
|
140
137
|
* We trigger that exit two ways, because neither alone is enough: signal handlers (for
|
|
@@ -150,7 +147,7 @@ function armForegroundTeardown(cfg) {
|
|
|
150
147
|
exiting = true;
|
|
151
148
|
unregisterSession(cfg.project);
|
|
152
149
|
try {
|
|
153
|
-
note(`Stopping
|
|
150
|
+
note(`Stopping local WordPress (${reason})…`);
|
|
154
151
|
} catch {}
|
|
155
152
|
process.exit(0);
|
|
156
153
|
};
|
|
@@ -169,7 +166,7 @@ function armForegroundTeardown(cfg) {
|
|
|
169
166
|
}
|
|
170
167
|
}
|
|
171
168
|
/**
|
|
172
|
-
* Boot
|
|
169
|
+
* Boot local WordPress and run in the foreground until exit — the path behind bare
|
|
173
170
|
* `kizlo dev`. Arms teardown first (so a mid-startup cancel still
|
|
174
171
|
* stops partial containers), shows a timed spinner, prints the summary + stop hint, then
|
|
175
172
|
* parks. A referenced timer holds the event loop open — an unresolved promise alone would
|
|
@@ -196,7 +193,7 @@ async function startForeground(cfg) {
|
|
|
196
193
|
if (dbPort !== cfg.dbPort) note(`Database port ${cfg.dbPort} is in use — using ${dbPort} instead.`);
|
|
197
194
|
armForegroundTeardown(ready);
|
|
198
195
|
const start = Date.now();
|
|
199
|
-
const creds = await withSpinner("Starting WordPress
|
|
196
|
+
const creds = await withSpinner("Starting local WordPress", () => bootstrapDev(ready));
|
|
200
197
|
if (creds.appPassword) {
|
|
201
198
|
const env = updateWpEnv(ready, creds);
|
|
202
199
|
note("Updated .env with the new WordPress credentials");
|
|
@@ -212,12 +209,12 @@ async function startForeground(cfg) {
|
|
|
212
209
|
});
|
|
213
210
|
if (!sync.ok) note(`Could not sync the site settings to WordPress (${sync.error}) — make sure the kizlo plugin is active, then set them from the Kizlo settings.`);
|
|
214
211
|
}
|
|
215
|
-
} else if (
|
|
212
|
+
} else if (syncLocalUrl(ready, creds.url)) note("Updated .env WordPress URL to the current network address — restart your app to pick it up");
|
|
216
213
|
printSummary(creds);
|
|
217
214
|
const { green, dim, bold, reset } = palette();
|
|
218
215
|
const ms = Date.now() - start;
|
|
219
216
|
const took = ms < 1e3 ? `${ms}ms` : `${(ms / 1e3).toFixed(1)}s`;
|
|
220
|
-
process.stdout.write(`\n ${dim}Press ${reset}${bold}Ctrl+C${reset}${dim} (or close the terminal) to stop
|
|
217
|
+
process.stdout.write(`\n ${dim}Press ${reset}${bold}Ctrl+C${reset}${dim} (or close the terminal) to stop local WordPress${reset}\n`);
|
|
221
218
|
process.stdout.write(`\n ${green}✓${reset} Ready in ${took}\n\n`);
|
|
222
219
|
const stopWatcher = await startWatcher(ready.configDir);
|
|
223
220
|
if (stopWatcher) process.on("exit", stopWatcher);
|
|
@@ -225,8 +222,8 @@ async function startForeground(cfg) {
|
|
|
225
222
|
await new Promise(() => {});
|
|
226
223
|
}
|
|
227
224
|
/**
|
|
228
|
-
* Run the contract watcher in the foreground until exit — the path bare `kizlo dev` takes when
|
|
229
|
-
*
|
|
225
|
+
* Run the contract watcher in the foreground until exit — the path bare `kizlo dev` takes when local
|
|
226
|
+
* WordPress isn't configured (a project pointing at its own WordPress). Generates once, then watches
|
|
230
227
|
* the server files and regenerates on save. Returns immediately when another watcher already holds
|
|
231
228
|
* the lock; otherwise the persistent watcher keeps the process alive on its own.
|
|
232
229
|
*/
|
|
@@ -244,44 +241,39 @@ async function watchOnly(cwd) {
|
|
|
244
241
|
});
|
|
245
242
|
}
|
|
246
243
|
/**
|
|
247
|
-
* Start (or resume)
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
244
|
+
* Start (or resume) local WordPress. Backs bare `kizlo dev`. Always runs in the foreground and stops
|
|
245
|
+
* it on exit, so it never outlives the session — the subcommands (`stop`/`reset`) manage everything
|
|
246
|
+
* else. Without local WordPress configured there's nothing to boot, so it runs the contract watcher
|
|
247
|
+
* alone.
|
|
251
248
|
*/
|
|
252
249
|
async function bringUp() {
|
|
253
250
|
const cwd = process.cwd();
|
|
254
|
-
if (!await
|
|
251
|
+
if (!await usesLocalWordPress(cwd)) {
|
|
252
|
+
note("This project doesn't run local WordPress — running the contract watcher only. Set `dev.local` in kizlo.config.ts to add it.");
|
|
255
253
|
await watchOnly(cwd);
|
|
256
254
|
return;
|
|
257
255
|
}
|
|
256
|
+
const status = await dockerStatus();
|
|
257
|
+
if (status !== "running") {
|
|
258
|
+
note(dockerHint(status));
|
|
259
|
+
process.exit(1);
|
|
260
|
+
}
|
|
258
261
|
const reaped = await reapOrphans();
|
|
259
|
-
if (reaped.length) note(`Stopped ${reaped.length} orphaned
|
|
262
|
+
if (reaped.length) note(`Stopped ${reaped.length} orphaned local WordPress${reaped.length === 1 ? "" : "s"}: ${reaped.join(", ")}`);
|
|
260
263
|
const cfg = await resolveDevConfig(cwd);
|
|
261
|
-
|
|
264
|
+
ensureGitignored(cfg.configDir, ".kizlo/");
|
|
262
265
|
await startForeground(cfg);
|
|
263
266
|
}
|
|
264
267
|
const subCommands = {
|
|
265
268
|
stop: defineCommand({
|
|
266
269
|
meta: {
|
|
267
270
|
name: "stop",
|
|
268
|
-
description: "Stop
|
|
269
|
-
},
|
|
270
|
-
async run() {
|
|
271
|
-
if (!await requireDevStack(process.cwd())) return;
|
|
272
|
-
const { stack } = await resolve$1(process.cwd());
|
|
273
|
-
await withSpinner("Stopping WordPress dev stack", () => stack.composeStop(), "Stack stopped (volumes kept)");
|
|
274
|
-
}
|
|
275
|
-
}),
|
|
276
|
-
down: defineCommand({
|
|
277
|
-
meta: {
|
|
278
|
-
name: "down",
|
|
279
|
-
description: "Stop and remove the dev stack containers (keeps the database and files)"
|
|
271
|
+
description: "Stop local WordPress, keeping the database volume"
|
|
280
272
|
},
|
|
281
273
|
async run() {
|
|
282
|
-
if (!await
|
|
274
|
+
if (!await requireLocalWordPress(process.cwd())) return;
|
|
283
275
|
const { stack } = await resolve$1(process.cwd());
|
|
284
|
-
await withSpinner("
|
|
276
|
+
await withSpinner("Stopping local WordPress", () => stack.composeStop(), "Local WordPress stopped (volumes kept)");
|
|
285
277
|
}
|
|
286
278
|
}),
|
|
287
279
|
reset: defineCommand({
|
|
@@ -290,21 +282,21 @@ const subCommands = {
|
|
|
290
282
|
description: "Wipe the database and the install folder so the next `kizlo dev` rebuilds fresh"
|
|
291
283
|
},
|
|
292
284
|
async run() {
|
|
293
|
-
if (!await
|
|
285
|
+
if (!await requireLocalWordPress(process.cwd())) return;
|
|
294
286
|
const { cfg, stack } = await resolve$1(process.cwd());
|
|
295
|
-
await withSpinner("Wiping WordPress
|
|
287
|
+
await withSpinner("Wiping local WordPress", () => stack.composeDown({ volumes: true }), "Local WordPress reset successfully");
|
|
296
288
|
rmSync(cfg.wordpressDir, {
|
|
297
289
|
recursive: true,
|
|
298
290
|
force: true
|
|
299
291
|
});
|
|
300
|
-
note("Run `kizlo dev` to rebuild
|
|
292
|
+
note("Run `kizlo dev` to rebuild fresh.");
|
|
301
293
|
}
|
|
302
294
|
})
|
|
303
295
|
};
|
|
304
296
|
const dev = defineCommand({
|
|
305
297
|
meta: {
|
|
306
298
|
name: "dev",
|
|
307
|
-
description: "Run
|
|
299
|
+
description: "Run local WordPress and the contract watcher (stop | reset)"
|
|
308
300
|
},
|
|
309
301
|
subCommands,
|
|
310
302
|
run: groupDefault(Object.keys(subCommands), bringUp)
|