kizlo 0.11.0 → 0.12.1
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/dist/astro/server.d.ts +9 -0
- package/dist/astro/server.js +10 -0
- package/dist/astro.d.ts +2 -0
- package/dist/astro.js +3 -0
- package/dist/cli/commands/_setup.js +10 -10
- package/dist/cli/commands/_wiring.js +13 -11
- package/dist/cli/commands/create.js +101 -71
- package/dist/cli/commands/generate.js +4 -0
- package/dist/cli/commands/init.js +110 -60
- package/dist/cli/daemon/config.js +23 -9
- package/dist/cli/daemon/generate.js +2 -3
- package/dist/cli/daemon/jiti.js +101 -0
- package/dist/cli/daemon/watch.js +7 -3
- package/dist/cli/presets/patch.js +38 -0
- package/dist/cli/presets/source.js +122 -24
- package/dist/cli/presets/template.js +47 -35
- package/dist/cli/wp/stack.js +2 -2
- package/dist/config.d.ts +1 -1
- package/dist/context.js +9 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/integrations/astro/kizlo.client.d.ts +15 -0
- package/dist/integrations/astro/kizlo.client.js +13 -0
- package/dist/integrations/astro/kizlo.server.d.ts +10 -0
- package/dist/integrations/astro/kizlo.server.js +12 -0
- package/dist/integrations/astro/manifest.d.ts +9 -0
- package/dist/integrations/astro/manifest.js +11 -0
- package/dist/integrations/astro/metadata.d.ts +64 -0
- package/dist/integrations/astro/metadata.js +71 -0
- package/dist/integrations/astro/robots.d.ts +8 -0
- package/dist/integrations/astro/robots.js +11 -0
- package/dist/integrations/astro/sitemap.d.ts +16 -0
- package/dist/integrations/astro/sitemap.js +20 -0
- package/dist/integrations/astro/utils.d.ts +19 -0
- package/dist/integrations/astro/utils.js +17 -0
- package/dist/integrations/nextjs/metadata.d.ts +8 -1
- package/dist/integrations/nextjs/metadata.js +13 -1
- package/dist/kizlo.d.ts +8 -1
- package/dist/kizlo.js +16 -13
- package/dist/nextjs/server.d.ts +3 -2
- package/dist/nextjs/server.js +3 -2
- package/dist/package.js +1 -1
- package/dist/router.d.ts +5 -5
- package/dist/seo/utils.d.ts +8 -2
- package/dist/seo/utils.js +9 -1
- package/dist/shared/constants.js +9 -1
- package/package.json +18 -2
- package/dist/cli/presets/base.js +0 -40
- package/dist/cli/presets/index.js +0 -16
- package/dist/cli/presets/nextjs.js +0 -16
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { WEB_MANIFEST_ROUTE } from "../brand/manifest.js";
|
|
2
|
+
import { renderJsonLd } from "../seo/utils.js";
|
|
3
|
+
import { KizloServerOptions, createKizlo } from "../integrations/astro/kizlo.server.js";
|
|
4
|
+
import { AstroRoute, AstroRouteContext, getServerBaseUrl } from "../integrations/astro/utils.js";
|
|
5
|
+
import { createManifestRoute } from "../integrations/astro/manifest.js";
|
|
6
|
+
import { PageHead, RootHead, ThemeColor, resolvePageHead, resolveRootHead } from "../integrations/astro/metadata.js";
|
|
7
|
+
import { createRobotsRoute } from "../integrations/astro/robots.js";
|
|
8
|
+
import { createSitemapRedirectRoute, createSitemapRoute } from "../integrations/astro/sitemap.js";
|
|
9
|
+
export { AstroRoute, AstroRouteContext, KizloServerOptions, PageHead, RootHead, ThemeColor, WEB_MANIFEST_ROUTE, createKizlo, createManifestRoute, createRobotsRoute, createSitemapRedirectRoute, createSitemapRoute, getServerBaseUrl, renderJsonLd, resolvePageHead, resolveRootHead };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { WEB_MANIFEST_ROUTE } from "../brand/manifest.js";
|
|
2
|
+
import { renderJsonLd } from "../seo/utils.js";
|
|
3
|
+
import { getServerBaseUrl } from "../integrations/astro/utils.js";
|
|
4
|
+
import { createKizlo } from "../integrations/astro/kizlo.server.js";
|
|
5
|
+
import { createManifestRoute } from "../integrations/astro/manifest.js";
|
|
6
|
+
import { resolvePageHead, resolveRootHead } from "../integrations/astro/metadata.js";
|
|
7
|
+
import { createRobotsRoute } from "../integrations/astro/robots.js";
|
|
8
|
+
import { createSitemapRedirectRoute, createSitemapRoute } from "../integrations/astro/sitemap.js";
|
|
9
|
+
|
|
10
|
+
export { WEB_MANIFEST_ROUTE, createKizlo, createManifestRoute, createRobotsRoute, createSitemapRedirectRoute, createSitemapRoute, getServerBaseUrl, renderJsonLd, resolvePageHead, resolveRootHead };
|
package/dist/astro.d.ts
ADDED
package/dist/astro.js
ADDED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { LOCAL_DIR_REL } from "../wp/constants.js";
|
|
2
2
|
import { composeStop, createStack, dockerHint, dockerStatus } from "../wp/docker.js";
|
|
3
3
|
import { DEFAULT_ENV_KEYS, ensureGitignored, envGroups, envKeysPresent, mergeEnv, pickStackPort, writeFileIfAbsent } from "../utils.js";
|
|
4
|
-
import { DEFAULT_DEV_DB_PORT, DEFAULT_DEV_PORT, resolveStackName } from "../daemon/config.js";
|
|
4
|
+
import { DEFAULT_DEV_DB_PORT, DEFAULT_DEV_PORT, resolveStackName, stackProject } from "../daemon/config.js";
|
|
5
5
|
import { createAdminAppPassword } from "../wp/bootstrap.js";
|
|
6
6
|
import { bootstrapDev } from "../wp/dev.js";
|
|
7
7
|
import { removeProjectContainers } from "../wp/session.js";
|
|
@@ -17,13 +17,13 @@ import getPort, { portNumbers } from "get-port";
|
|
|
17
17
|
|
|
18
18
|
//#region src/cli/commands/_setup.ts
|
|
19
19
|
/**
|
|
20
|
-
* The resolved `.env` key names the scaffold writes: the template's declared `env` when it has one,
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* `
|
|
20
|
+
* The resolved `.env` key names the scaffold writes: the template's declared `env` when it has one, else
|
|
21
|
+
* the {@link DEFAULT_ENV_KEYS} fallback — for an in-repo template before the first stamped release, and
|
|
22
|
+
* for the base no-template fallback (which has no manifest). Threaded into `managedEnv`, `writeEnv`, and
|
|
23
|
+
* `collectConnectionFromEnv` so the scaffold writes the names the pinned runtime reads.
|
|
24
24
|
*/
|
|
25
|
-
function resolveEnvKeys(
|
|
26
|
-
return manifest?.env ??
|
|
25
|
+
function resolveEnvKeys(manifest) {
|
|
26
|
+
return manifest?.env ?? DEFAULT_ENV_KEYS;
|
|
27
27
|
}
|
|
28
28
|
/** Remote connection keys — what a real deploy needs, and what `.env.example` always lists. */
|
|
29
29
|
function remoteKeys(envKeys) {
|
|
@@ -135,11 +135,11 @@ function pickAppPort() {
|
|
|
135
135
|
* `create` scaffolds a brand-new app that has no public URL yet, so it defaults to the local dev
|
|
136
136
|
* origin and lets the user fill in production later.
|
|
137
137
|
*/
|
|
138
|
-
async function collectConnectionInteractively(
|
|
138
|
+
async function collectConnectionInteractively(apiPath, opts = {}) {
|
|
139
139
|
let siteUrl;
|
|
140
140
|
let baseUrl;
|
|
141
141
|
if (opts.baseUrl !== void 0) baseUrl = opts.baseUrl;
|
|
142
|
-
else if (
|
|
142
|
+
else if (apiPath) baseUrl = orCancel(await p.text({
|
|
143
143
|
message: "Public app URL",
|
|
144
144
|
placeholder: "https://your-app.com",
|
|
145
145
|
validate: validate(urlString)
|
|
@@ -238,7 +238,7 @@ function collectConnectionFromEnv(envKeys) {
|
|
|
238
238
|
function devConfigFor(cwd) {
|
|
239
239
|
return {
|
|
240
240
|
configDir: cwd,
|
|
241
|
-
project:
|
|
241
|
+
project: stackProject(resolveStackName(cwd), "dev"),
|
|
242
242
|
port: DEFAULT_DEV_PORT,
|
|
243
243
|
portExplicit: false,
|
|
244
244
|
dbPort: DEFAULT_DEV_DB_PORT,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { resolveModuleImport, writeFileIfAbsent } from "../utils.js";
|
|
2
|
-
import {
|
|
3
|
-
import { findRootLayout, resolvePatch } from "../presets/template.js";
|
|
2
|
+
import { resolvePatch } from "../presets/template.js";
|
|
4
3
|
import { orCancel } from "./_setup.js";
|
|
5
4
|
import { CONTRACT_BARREL } from "../daemon/generate.js";
|
|
5
|
+
import { applyPatchToSource, patchChanged, renderPatchCode, resolvePatchTargetPath } from "../presets/patch.js";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import fs from "node:fs";
|
|
8
8
|
import * as p from "@clack/prompts";
|
|
@@ -73,17 +73,19 @@ function writeGeneratedContract(cwd, serverDirRel) {
|
|
|
73
73
|
writeFileIfAbsent(path.join(cwd, generatedDirRel, "index.ts"), CONTRACT_BARREL);
|
|
74
74
|
}
|
|
75
75
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
76
|
+
* Apply every patch a template declares, merging Kizlo wiring into files the project already owns
|
|
77
|
+
* (today: the root layout and home page SEO exports — but the loop is role-agnostic, so a new patch
|
|
78
|
+
* needs no change here). Never aborts: the target is the exact path the template declares (extension-
|
|
79
|
+
* probed so a JS project's `.js`/`.jsx` is found), and on any doubt — the file isn't at that path, or
|
|
80
|
+
* won't parse — the resolved payload is printed at the end with placement instructions rather than
|
|
81
|
+
* written to a guessed-at file. We never scan the tree to find a stand-in. A confident apply is an
|
|
82
|
+
* idempotent upsert: it replaces our exports if present, adds them if not.
|
|
81
83
|
*/
|
|
82
|
-
function
|
|
84
|
+
function applyProjectPatches(cwd, patches, conventions, scaffold) {
|
|
83
85
|
const manualSteps = [];
|
|
84
86
|
for (const entry of patches) {
|
|
85
87
|
const resolved = resolvePatch(entry, conventions, scaffold);
|
|
86
|
-
const target =
|
|
88
|
+
const target = resolvePatchTargetPath(cwd, resolved.relPath);
|
|
87
89
|
if (!target) {
|
|
88
90
|
manualSteps.push(resolved);
|
|
89
91
|
p.log.info(`Couldn't find your ${resolved.label} to wire Kizlo into — see the code to add below`);
|
|
@@ -105,8 +107,8 @@ function applyLayoutPatches(cwd, patches, conventions, scaffold) {
|
|
|
105
107
|
p.log.success(`Wired Kizlo into your ${resolved.label} (${relTarget})`);
|
|
106
108
|
} else p.log.info(`Your ${resolved.label} is already wired (${relTarget})`);
|
|
107
109
|
}
|
|
108
|
-
for (const resolved of manualSteps) p.note(renderPatchCode(resolved), `Add these to your ${resolved.label} (
|
|
110
|
+
for (const resolved of manualSteps) p.note(renderPatchCode(resolved), `Add these to your ${resolved.label} (${resolved.relPath})`);
|
|
109
111
|
}
|
|
110
112
|
|
|
111
113
|
//#endregion
|
|
112
|
-
export {
|
|
114
|
+
export { applyProjectPatches, buildScaffoldContext, kizloConfigTemplate, reportScaffold, scaffoldFile, writeGeneratedContract };
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { availablePackageManagers, detectInvokingPackageManager, ensureGitignored, frameworkCreateArgs, installArgs, runCommandAsync, runCommandCaptured } from "../utils.js";
|
|
2
|
-
import {
|
|
3
|
-
import { fetchTemplate } from "../presets/source.js";
|
|
2
|
+
import { fetchRegistry, listTemplates, resolveRegistry } from "../presets/source.js";
|
|
4
3
|
import { adaptFile, changesFor, fileEntries, minCliError, patchEntries, readManifest, resolveDependencies } from "../presets/template.js";
|
|
5
4
|
import { collectConnectionInteractively, nextStepsLines, orCancel, pickAppPort, readEnvExample, resolveEnvKeys, setupLocalWordPress, syncRemote, validate, withApiPath, writeEnv } from "./_setup.js";
|
|
6
|
-
import {
|
|
5
|
+
import { applyProjectPatches, buildScaffoldContext, kizloConfigTemplate, reportScaffold, scaffoldFile, writeGeneratedContract } from "./_wiring.js";
|
|
7
6
|
import z from "zod/v4";
|
|
8
7
|
import path from "node:path";
|
|
9
8
|
import fs from "node:fs";
|
|
@@ -11,15 +10,30 @@ import { defineCommand } from "citty";
|
|
|
11
10
|
import * as p from "@clack/prompts";
|
|
12
11
|
|
|
13
12
|
//#region src/cli/commands/create.ts
|
|
13
|
+
/** The final path segment of a project name — the app folder itself; everything before it is where it lands. */
|
|
14
|
+
function folderSegment(value) {
|
|
15
|
+
const segments = value.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
16
|
+
return segments[segments.length - 1] ?? "";
|
|
17
|
+
}
|
|
14
18
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
19
|
+
* Tidy the entered name/path for both the target dir and the display: trim, drop a leading `./`, and drop a
|
|
20
|
+
* trailing separator, so `cd`, the spinner, and the warning lines read as `templates/my-app`, not
|
|
21
|
+
* `./templates/my-app/`. Preserves `../` and absolute paths (only a leading `./` is redundant).
|
|
17
22
|
*/
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return TEMPLATES.includes(value);
|
|
23
|
+
function normalizeProjectName(value) {
|
|
24
|
+
return value.trim().replace(/^\.\/+/, "").replace(/[/\\]+$/, "");
|
|
21
25
|
}
|
|
22
|
-
|
|
26
|
+
/**
|
|
27
|
+
* A project name may be a bare folder (`my-app`) or a path to scaffold somewhere other than the current
|
|
28
|
+
* dir — relative (`apps/my-app`, `../my-app`) or absolute (`/srv/my-app`). Only the last segment is the
|
|
29
|
+
* app folder, so that's what's validated as a name; the parent dirs (which may not exist yet) are created
|
|
30
|
+
* by the framework's own CLI. `.`/`..` as the final segment, or a name with no real segment, is rejected.
|
|
31
|
+
*/
|
|
32
|
+
const projectNameSchema = z.string().trim().min(1, "Required").refine((value) => {
|
|
33
|
+
const base = folderSegment(value);
|
|
34
|
+
return base.length > 0 && base !== "." && base !== ".." && /^[A-Za-z0-9._-]+$/.test(base);
|
|
35
|
+
}, "The app folder (last path segment) can only contain letters, numbers, dots, and dashes");
|
|
36
|
+
const projectName = validate(projectNameSchema);
|
|
23
37
|
/** Package managers `create` can wire the getting-started steps for, in display order. */
|
|
24
38
|
const PACKAGE_MANAGERS = [
|
|
25
39
|
"pnpm",
|
|
@@ -37,17 +51,25 @@ function recordDependencies(dir, manifest) {
|
|
|
37
51
|
const pkgPath = path.join(dir, "package.json");
|
|
38
52
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
39
53
|
const { dependencies, devDependencies } = resolveDependencies(manifest);
|
|
40
|
-
pkg.dependencies = {
|
|
54
|
+
pkg.dependencies = sortDependencies({
|
|
41
55
|
...pkg.dependencies,
|
|
42
56
|
...dependencies
|
|
43
|
-
};
|
|
44
|
-
if (Object.keys(devDependencies).length) pkg.devDependencies = {
|
|
57
|
+
});
|
|
58
|
+
if (Object.keys(devDependencies).length) pkg.devDependencies = sortDependencies({
|
|
45
59
|
...pkg.devDependencies,
|
|
46
60
|
...devDependencies
|
|
47
|
-
};
|
|
61
|
+
});
|
|
48
62
|
fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, " ")}\n`);
|
|
49
63
|
}
|
|
50
64
|
/**
|
|
65
|
+
* Alphabetize a dependency map by package name. The framework CLI writes its deps sorted, but merging
|
|
66
|
+
* Kizlo's pins on top appends them out of order — sherif flags unsorted dependency keys, so re-sort the
|
|
67
|
+
* whole map before writing.
|
|
68
|
+
*/
|
|
69
|
+
function sortDependencies(deps) {
|
|
70
|
+
return Object.fromEntries(Object.entries(deps).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0));
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
51
73
|
* The argv `create` runs to bootstrap a fresh base app with the framework's own official CLI, built
|
|
52
74
|
* from the template manifest's `bootstrap` (initializer + flags) — the template is the single source
|
|
53
75
|
* of truth for the framework. The CLI owns only the package-manager mechanics: {@link frameworkCreateArgs}
|
|
@@ -66,7 +88,7 @@ function bootstrapArgs(manifest, pm, name) {
|
|
|
66
88
|
* `kizlo` dependency, write `kizlo.config.ts`, scaffold the manifest's files (core wiring from `base`
|
|
67
89
|
* and `create` always, the `example`-flagged demo pages only when `includeExamples`),
|
|
68
90
|
* 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`
|
|
69
|
-
* writes it whole (already SEO-wired) rather than patching — `
|
|
91
|
+
* writes it whole (already SEO-wired) rather than patching — `applyProjectPatches` runs over `create`'s
|
|
70
92
|
* patches, which are none today. The target directory layout comes straight from the manifest's
|
|
71
93
|
* conventions, which the bootstrap flags are chosen to match, so files land where they expect. Fresh
|
|
72
94
|
* files are written with `force`, silently replacing the framework defaults.
|
|
@@ -93,7 +115,7 @@ async function applyManifestWiring(dir, templateDir, manifest, opts) {
|
|
|
93
115
|
writeGeneratedContract(dir, path.join(kizloDir, "server"));
|
|
94
116
|
ensureGitignored(dir, ".env");
|
|
95
117
|
ensureGitignored(dir, ".kizlo/");
|
|
96
|
-
|
|
118
|
+
applyProjectPatches(dir, patchEntries(changes), manifest.conventions, scaffold);
|
|
97
119
|
}
|
|
98
120
|
const create = defineCommand({
|
|
99
121
|
meta: {
|
|
@@ -104,54 +126,75 @@ const create = defineCommand({
|
|
|
104
126
|
template: {
|
|
105
127
|
type: "positional",
|
|
106
128
|
required: false,
|
|
107
|
-
description:
|
|
129
|
+
description: "Template to scaffold from (discovered from the registry)"
|
|
108
130
|
},
|
|
109
131
|
name: {
|
|
110
132
|
type: "positional",
|
|
111
133
|
required: false,
|
|
112
|
-
description: "Project folder to create"
|
|
134
|
+
description: "Project folder to create — a bare name or a path (e.g. apps/my-app, ../my-app, /srv/my-app)"
|
|
135
|
+
},
|
|
136
|
+
source: {
|
|
137
|
+
type: "string",
|
|
138
|
+
description: "Where to scaffold from — a local dir or giget source, either a registry of templates or a single template (default: Kizlo's GitHub templates)"
|
|
113
139
|
}
|
|
114
140
|
},
|
|
115
141
|
async run({ args }) {
|
|
116
142
|
const cwd = process.cwd();
|
|
117
143
|
p.intro(`Let's create a new Kizlo application`);
|
|
144
|
+
const registry = resolveRegistry(args.source ? String(args.source) : void 0);
|
|
145
|
+
let fetchedRegistry;
|
|
146
|
+
try {
|
|
147
|
+
fetchedRegistry = await fetchRegistry(registry);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
p.cancel(error instanceof Error ? error.message : String(error));
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
const templates = listTemplates(fetchedRegistry.dir);
|
|
153
|
+
if (templates.length === 0) {
|
|
154
|
+
fetchedRegistry.cleanup();
|
|
155
|
+
p.cancel(`No templates found in the registry (${registry.source}). Each template needs a template.json.`);
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
const cancelFrom = (message) => {
|
|
159
|
+
fetchedRegistry.cleanup();
|
|
160
|
+
p.cancel(message);
|
|
161
|
+
process.exit(1);
|
|
162
|
+
};
|
|
118
163
|
const requested = args.template;
|
|
119
|
-
let
|
|
164
|
+
let selected;
|
|
120
165
|
if (requested) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
166
|
+
selected = templates.find((entry) => entry.id === requested);
|
|
167
|
+
if (!selected) return cancelFrom(`Unknown template "${requested}". Available: ${templates.map((entry) => entry.id).join(", ")}`);
|
|
168
|
+
} else {
|
|
169
|
+
const id = orCancel(await p.select({
|
|
170
|
+
message: "Template",
|
|
171
|
+
options: templates.map((entry) => ({
|
|
172
|
+
value: entry.id,
|
|
173
|
+
label: entry.label
|
|
174
|
+
}))
|
|
175
|
+
}));
|
|
176
|
+
selected = templates.find((entry) => entry.id === id);
|
|
177
|
+
}
|
|
178
|
+
if (!selected) return cancelFrom("No template selected.");
|
|
179
|
+
const template = selected.id;
|
|
180
|
+
const templateDir = selected.dir;
|
|
181
|
+
const label = selected.label;
|
|
182
|
+
const fail = cancelFrom;
|
|
183
|
+
const manifest = readManifest(templateDir);
|
|
184
|
+
const minErr = minCliError(manifest);
|
|
185
|
+
if (minErr) return fail(minErr);
|
|
186
|
+
const name = normalizeProjectName(args.name ? String(args.name) : orCancel(await p.text({
|
|
187
|
+
message: "What is your project named?",
|
|
135
188
|
placeholder: "my-app",
|
|
136
189
|
validate: projectName
|
|
137
|
-
}));
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
p.cancel(check);
|
|
142
|
-
process.exit(1);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
const dir = path.resolve(cwd, name);
|
|
146
|
-
if (fs.existsSync(dir)) {
|
|
147
|
-
p.cancel(`${name} already exists — pick a different name or remove it.`);
|
|
148
|
-
process.exit(1);
|
|
149
|
-
}
|
|
150
|
-
const preset = getPreset(template);
|
|
151
|
-
if (!preset) {
|
|
152
|
-
p.cancel(`No preset for template "${template}".`);
|
|
190
|
+
})));
|
|
191
|
+
const invalid = projectName(name);
|
|
192
|
+
if (invalid) {
|
|
193
|
+
p.cancel(invalid);
|
|
153
194
|
process.exit(1);
|
|
154
195
|
}
|
|
196
|
+
const dir = path.resolve(cwd, name);
|
|
197
|
+
if (fs.existsSync(dir)) return cancelFrom(`${name} already exists — pick a different name or remove it.`);
|
|
155
198
|
const installed = availablePackageManagers(PACKAGE_MANAGERS);
|
|
156
199
|
const invokingPm = detectInvokingPackageManager();
|
|
157
200
|
const defaultPm = invokingPm && installed.includes(invokingPm) ? invokingPm : installed[0];
|
|
@@ -163,45 +206,32 @@ const create = defineCommand({
|
|
|
163
206
|
})),
|
|
164
207
|
initialValue: defaultPm
|
|
165
208
|
}));
|
|
166
|
-
const conn = await collectConnectionInteractively(
|
|
167
|
-
if (
|
|
209
|
+
const conn = await collectConnectionInteractively(manifest.apiPath, { baseUrl: `http://localhost:${await pickAppPort()}` });
|
|
210
|
+
if (manifest.apiPath && conn.baseUrl) conn.baseUrl = withApiPath(conn.baseUrl, manifest.apiPath);
|
|
168
211
|
const includeExamples = orCancel(await p.confirm({
|
|
169
212
|
message: "Add examples?",
|
|
170
213
|
initialValue: true
|
|
171
214
|
}));
|
|
172
|
-
const fetched = await fetchTemplate(template);
|
|
173
|
-
const fail = (message) => {
|
|
174
|
-
fetched.cleanup();
|
|
175
|
-
p.cancel(message);
|
|
176
|
-
process.exit(1);
|
|
177
|
-
};
|
|
178
|
-
const manifest = readManifest(fetched.dir);
|
|
179
|
-
const minErr = minCliError(manifest);
|
|
180
|
-
if (minErr) fail(minErr);
|
|
181
215
|
const bootstrap = bootstrapArgs(manifest, pm, name);
|
|
182
|
-
if (!bootstrap) {
|
|
183
|
-
fetched.cleanup();
|
|
184
|
-
p.cancel(`Template "${template}" can't be scaffolded — its manifest declares no framework bootstrap.`);
|
|
185
|
-
process.exit(1);
|
|
186
|
-
}
|
|
216
|
+
if (!bootstrap) return fail(`Template "${template}" can't be scaffolded — its manifest declares no framework bootstrap.`);
|
|
187
217
|
const s = p.spinner();
|
|
188
|
-
s.start(`Creating ${name} with the ${
|
|
218
|
+
s.start(`Creating ${name} with the ${label} CLI`);
|
|
189
219
|
const scaffold = runCommandCaptured(bootstrap, cwd);
|
|
190
|
-
s.stop(scaffold.ok ? `Created ${name} with the ${
|
|
220
|
+
s.stop(scaffold.ok ? `Created ${name} with the ${label} CLI` : `${label} setup failed`);
|
|
191
221
|
if (!scaffold.ok) {
|
|
192
222
|
if (scaffold.output) p.log.error(scaffold.output);
|
|
193
|
-
fail(`${
|
|
223
|
+
fail(`${label} setup failed — see the output above and try again.`);
|
|
194
224
|
}
|
|
195
|
-
const exampleTemplate = readEnvExample(
|
|
225
|
+
const exampleTemplate = readEnvExample(templateDir);
|
|
196
226
|
try {
|
|
197
|
-
await applyManifestWiring(dir,
|
|
227
|
+
await applyManifestWiring(dir, templateDir, manifest, {
|
|
198
228
|
includeExamples,
|
|
199
229
|
localDev: conn.mode === "local"
|
|
200
230
|
});
|
|
201
231
|
} catch (error) {
|
|
202
232
|
fail(error instanceof Error ? error.message : String(error));
|
|
203
233
|
}
|
|
204
|
-
|
|
234
|
+
fetchedRegistry.cleanup();
|
|
205
235
|
let depsInstalled = false;
|
|
206
236
|
if (orCancel(await p.confirm({
|
|
207
237
|
message: "Install dependencies now?",
|
|
@@ -214,7 +244,7 @@ const create = defineCommand({
|
|
|
214
244
|
if (!depsInstalled) p.log.warn(`Install them yourself: cd ${name} && ${installArgs(pm).join(" ")}`);
|
|
215
245
|
}
|
|
216
246
|
await setupLocalWordPress(dir, conn);
|
|
217
|
-
await writeEnv(dir, resolveEnvKeys(
|
|
247
|
+
await writeEnv(dir, resolveEnvKeys(manifest), conn, {
|
|
218
248
|
force: true,
|
|
219
249
|
yes: false,
|
|
220
250
|
exampleTemplate
|
|
@@ -15,6 +15,10 @@ const generate = defineCommand({
|
|
|
15
15
|
} },
|
|
16
16
|
async run({ args }) {
|
|
17
17
|
const cfg = await resolveConfig(process.cwd(), { dir: args.dir });
|
|
18
|
+
if (!cfg) {
|
|
19
|
+
log.info("No Kizlo server directory configured, nothing to generate. Set `dir` in kizlo.config.ts or pass --dir.");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
18
22
|
let ok;
|
|
19
23
|
try {
|
|
20
24
|
ok = await generateOnce(cfg);
|