kizlo 0.13.0 → 0.14.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/dist/cli/commands/_setup.js +91 -55
- package/dist/cli/commands/_tasks.js +152 -0
- package/dist/cli/commands/_wiring.js +11 -10
- package/dist/cli/commands/create.js +270 -87
- package/dist/cli/commands/init.js +122 -61
- package/dist/cli/daemon/config.js +1 -1
- package/dist/cli/presets/source.js +13 -8
- package/dist/cli/presets/template.js +255 -50
- package/dist/cli/utils.js +149 -80
- package/dist/integrations/tanstack-start/api.d.ts +15 -0
- package/dist/integrations/tanstack-start/api.js +21 -0
- package/dist/integrations/tanstack-start/kizlo.client.d.ts +20 -0
- package/dist/integrations/tanstack-start/kizlo.client.js +18 -0
- package/dist/integrations/tanstack-start/kizlo.server.d.ts +14 -0
- package/dist/integrations/tanstack-start/kizlo.server.js +15 -0
- package/dist/integrations/tanstack-start/manifest.d.ts +9 -0
- package/dist/integrations/tanstack-start/manifest.js +11 -0
- package/dist/integrations/tanstack-start/metadata.d.ts +49 -0
- package/dist/integrations/tanstack-start/metadata.js +112 -0
- package/dist/integrations/tanstack-start/robots.d.ts +8 -0
- package/dist/integrations/tanstack-start/robots.js +11 -0
- package/dist/integrations/tanstack-start/root-head.d.ts +15 -0
- package/dist/integrations/tanstack-start/root-head.js +54 -0
- package/dist/integrations/tanstack-start/sitemap.d.ts +16 -0
- package/dist/integrations/tanstack-start/sitemap.js +20 -0
- package/dist/integrations/tanstack-start/utils.d.ts +20 -0
- package/dist/integrations/tanstack-start/utils.js +17 -0
- package/dist/package.js +1 -1
- package/dist/router.d.ts +5 -5
- package/dist/tanstack-start/server.d.ts +11 -0
- package/dist/tanstack-start/server.js +12 -0
- package/dist/tanstack-start.d.ts +4 -0
- package/dist/tanstack-start.js +5 -0
- package/package.json +22 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { addDependencyArgs, aliasWithSlash, detectImportAlias, detectInvokingPackageManager, detectPackageManager, ensureGitignored, getVersion, loadEnvFiles, persistPackageManagerField, readPersistedAlias, runCommandAsync } from "../utils.js";
|
|
2
2
|
import { DEFAULT_REGISTRY, detectTemplates, fetchRegistry, isSingleTemplate, listTemplates, resolveRegistry } from "../presets/source.js";
|
|
3
|
-
import { adaptFile, changesFor, fileEntries, isOlderVersion, minCliError, patchEntries, readManifest, renderNote, resolveDependencies } from "../presets/template.js";
|
|
4
|
-
import { collectConnectionFromEnv, collectConnectionInteractively, dirPath, nextStepsNote, orCancel, readEnvExample, resolveEnvKeys, selectPackageManager,
|
|
3
|
+
import { adaptFile, changesFor, fileEntries, isOlderVersion, minCliError, patchEntries, readManifest, renderNote, resolveDependencies, unmetRequirement } from "../presets/template.js";
|
|
4
|
+
import { collectConnectionFromEnv, collectConnectionInteractively, dirPath, envConflicts, nextStepsNote, orCancel, provisionLocalWordPress, readEnvExample, resolveEnvKeys, selectPackageManager, syncRemote, validate, withApiPath, writeEnv } from "./_setup.js";
|
|
5
|
+
import { FINAL_CONFIRMATION_TEXT, StepError, confirmProceed, runChecklist, summaryNote } from "./_tasks.js";
|
|
5
6
|
import { applyProjectPatches, buildScaffoldContext, kizloConfigTemplate, printManualStep, reportScaffold, scaffoldFile, writeGeneratedContract } from "./_wiring.js";
|
|
6
7
|
import path from "node:path";
|
|
7
8
|
import fs from "node:fs";
|
|
@@ -21,24 +22,6 @@ function sameOrigin(a, b) {
|
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
|
-
* The framework's route directory in the user's project. When a template is present its
|
|
25
|
-
* `conventions.appDir` (e.g. `src/app` for Next, `src/pages` for Astro) is the expected layout; we
|
|
26
|
-
* probe that path and its no-`src` variant so a project that keeps routes at the top level is still
|
|
27
|
-
* found, falling back to whether the project has a `src` dir. With no known template appDir we keep the
|
|
28
|
-
* App Router default.
|
|
29
|
-
*/
|
|
30
|
-
function detectAppDir(cwd, hasSrcDir, templateAppDir) {
|
|
31
|
-
if (templateAppDir) {
|
|
32
|
-
const withoutSrc = templateAppDir.replace(/^src\//, "");
|
|
33
|
-
if (fs.existsSync(path.join(cwd, templateAppDir))) return templateAppDir;
|
|
34
|
-
if (fs.existsSync(path.join(cwd, withoutSrc))) return withoutSrc;
|
|
35
|
-
return hasSrcDir ? templateAppDir : withoutSrc;
|
|
36
|
-
}
|
|
37
|
-
if (fs.existsSync(path.join(cwd, "src", "app"))) return "src/app";
|
|
38
|
-
if (fs.existsSync(path.join(cwd, "app"))) return "app";
|
|
39
|
-
return hasSrcDir ? "src/app" : "app";
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
25
|
* The import alias settled without asking — the explicit `--alias` flag, else the alias a previous init
|
|
43
26
|
* persisted in kizlo.config. `undefined` when neither is set: the choice hasn't been made, so it must be
|
|
44
27
|
* prompted for (interactive) or defaulted to relative imports (`--yes`). Detection never enters here — a
|
|
@@ -105,7 +88,7 @@ function readPkg(pkgPath) {
|
|
|
105
88
|
* A *missing* `kizlo` is installed on its own earlier (with its own messaging), so it's skipped here;
|
|
106
89
|
* an existing older `kizlo` still gets the upgrade path.
|
|
107
90
|
*/
|
|
108
|
-
async function alignDependencies(cwd, pm, pkg, manifest) {
|
|
91
|
+
async function alignDependencies(cwd, pm, pkg, manifest, message, warnings) {
|
|
109
92
|
const { dependencies, devDependencies } = resolveDependencies(manifest);
|
|
110
93
|
const declared = [...Object.entries(dependencies).map(([name, want]) => ({
|
|
111
94
|
name,
|
|
@@ -116,17 +99,23 @@ async function alignDependencies(cwd, pm, pkg, manifest) {
|
|
|
116
99
|
want,
|
|
117
100
|
dev: true
|
|
118
101
|
}))];
|
|
102
|
+
let installed = 0;
|
|
103
|
+
let upgraded = 0;
|
|
119
104
|
for (const { name, want, dev } of declared) {
|
|
120
105
|
const have = pkg.dependencies?.[name] ?? pkg.devDependencies?.[name];
|
|
121
106
|
if (name === "kizlo" && !have) continue;
|
|
122
107
|
if (have && !isOlderVersion(have, want)) continue;
|
|
123
108
|
const args = addDependencyArgs(pm, `${name}@${want}`, { dev });
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
109
|
+
message(have ? `Upgrading ${name} from ${have} to ${want}` : `Installing ${name}@${want}`);
|
|
110
|
+
if (!await runCommandAsync(args, cwd, "ignore")) {
|
|
111
|
+
warnings.push(have ? `Could not upgrade ${name} automatically — install ${name}@${want} yourself` : `Could not install ${name} — run \`${args.join(" ")}\` yourself`);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (have) upgraded++;
|
|
115
|
+
else installed++;
|
|
129
116
|
}
|
|
117
|
+
const parts = [installed ? `installed ${installed}` : "", upgraded ? `upgraded ${upgraded}` : ""].filter(Boolean);
|
|
118
|
+
return parts.length ? `Aligned dependencies (${parts.join(", ")})` : "Dependencies already aligned";
|
|
130
119
|
}
|
|
131
120
|
const init = defineCommand({
|
|
132
121
|
meta: {
|
|
@@ -174,9 +163,9 @@ const init = defineCommand({
|
|
|
174
163
|
process.exit(1);
|
|
175
164
|
}
|
|
176
165
|
const pkg = readPkg(pkgPath);
|
|
177
|
-
const detectedPm = detectPackageManager(cwd);
|
|
166
|
+
const detectedPm = await detectPackageManager(cwd);
|
|
178
167
|
const pm = detectedPm ?? (yes ? detectInvokingPackageManager() ?? "npm" : await selectPackageManager("Couldn't detect this project's package manager — which should Kizlo use?"));
|
|
179
|
-
|
|
168
|
+
const shouldPersistPm = !detectedPm && !yes;
|
|
180
169
|
const hasKizlo = Boolean(pkg.dependencies?.kizlo) || Boolean(pkg.devDependencies?.kizlo);
|
|
181
170
|
const hasSrcDir = fs.existsSync(path.join(cwd, "src"));
|
|
182
171
|
if (yes) loadEnvFiles(cwd);
|
|
@@ -249,8 +238,9 @@ const init = defineCommand({
|
|
|
249
238
|
const manifest = readManifest(entry.dir);
|
|
250
239
|
const minErr = minCliError(manifest);
|
|
251
240
|
if (minErr) cancelFrom(minErr);
|
|
252
|
-
|
|
253
|
-
|
|
241
|
+
const unmet = unmetRequirement(manifest.init.requires, projectDeps, (rel) => fs.existsSync(path.join(cwd, rel)));
|
|
242
|
+
if (unmet) cancelFrom(unmet);
|
|
243
|
+
const apiPath = manifest.config.apiPath;
|
|
254
244
|
const envKeys = resolveEnvKeys(manifest);
|
|
255
245
|
const flagAlias = args.alias !== void 0 ? String(args.alias).trim() : void 0;
|
|
256
246
|
const aliasCtx = {
|
|
@@ -265,32 +255,17 @@ const init = defineCommand({
|
|
|
265
255
|
envKeys
|
|
266
256
|
}) : await collectInteractively(aliasCtx);
|
|
267
257
|
setup.alias = aliasWithSlash(setup.alias);
|
|
258
|
+
if (apiPath && setup.baseUrl) setup.baseUrl = withApiPath(setup.baseUrl, apiPath);
|
|
268
259
|
const includeExamples = yes ? true : orCancel(await p.confirm({
|
|
269
260
|
message: "Add example pages?",
|
|
270
261
|
initialValue: true
|
|
271
262
|
}));
|
|
272
|
-
if (!hasKizlo) {
|
|
273
|
-
const spec = `kizlo@${manifest.dependencies?.kizlo ?? `^${getVersion()}`}`;
|
|
274
|
-
const s = p.spinner();
|
|
275
|
-
s.start(`Installing kizlo with ${pm}`);
|
|
276
|
-
const ok = await runCommandAsync(addDependencyArgs(pm, spec), cwd, "ignore");
|
|
277
|
-
s.stop(ok ? "Installed kizlo" : "Could not install kizlo automatically");
|
|
278
|
-
if (!ok) p.log.warn(`Install it yourself: ${addDependencyArgs(pm, spec).join(" ")}`);
|
|
279
|
-
}
|
|
280
|
-
if (apiPath && setup.baseUrl) setup.baseUrl = withApiPath(setup.baseUrl, apiPath);
|
|
281
|
-
await setupLocalWordPress(cwd, setup);
|
|
282
|
-
await writeEnv(cwd, envKeys, setup, {
|
|
283
|
-
force,
|
|
284
|
-
yes,
|
|
285
|
-
exampleTemplate: readEnvExample(entry.dir)
|
|
286
|
-
});
|
|
287
|
-
await syncRemote(setup);
|
|
288
263
|
const dirRel = setup.dir.replace(/^\.\//, "").replace(/\/+$/, "");
|
|
289
264
|
const serverDirRel = path.join(dirRel, "server");
|
|
290
265
|
const clientUrl = setup.siteUrl && !sameOrigin(setup.siteUrl, setup.baseUrl) ? setup.baseUrl : void 0;
|
|
291
266
|
const scaffold = buildScaffoldContext(cwd, {
|
|
292
267
|
dirRel,
|
|
293
|
-
|
|
268
|
+
hasSrcDir,
|
|
294
269
|
alias: setup.alias,
|
|
295
270
|
clientUrl
|
|
296
271
|
});
|
|
@@ -299,22 +274,108 @@ const init = defineCommand({
|
|
|
299
274
|
relPath: "kizlo.config.ts",
|
|
300
275
|
contents: kizloConfigTemplate(dirRel, setup.alias, setup.mode === "local")
|
|
301
276
|
}];
|
|
302
|
-
|
|
303
|
-
|
|
277
|
+
for (const fileEntry of fileEntries(changesFor(manifest, "init", { includeExamples }))) files.push(adaptFile(entry.dir, fileEntry, manifest.config, scaffold));
|
|
278
|
+
const existingFiles = files.filter((file) => fs.existsSync(path.join(cwd, file.relPath)));
|
|
279
|
+
const overwritePaths = force || existingFiles.length === 0 || yes ? new Set(force ? existingFiles.map((f) => f.relPath) : []) : new Set(orCancel(await p.multiselect({
|
|
280
|
+
message: `${existingFiles.length} file(s) already exist. Select which to overwrite (space to toggle, enter to confirm).`,
|
|
281
|
+
options: existingFiles.map((f) => ({
|
|
282
|
+
value: f.relPath,
|
|
283
|
+
label: f.relPath
|
|
284
|
+
})),
|
|
285
|
+
initialValues: existingFiles.map((f) => f.relPath),
|
|
286
|
+
required: false
|
|
287
|
+
})));
|
|
288
|
+
const envKeyConflicts = envConflicts(cwd, envKeys, setup);
|
|
289
|
+
const overwriteEnv = force || envKeyConflicts.length > 0 && !yes && orCancel(await p.confirm({
|
|
290
|
+
message: `Overwrite existing .env values (${envKeyConflicts.join(", ")})?`,
|
|
291
|
+
initialValue: true
|
|
292
|
+
}));
|
|
293
|
+
summaryNote([
|
|
294
|
+
["Template", entry.label],
|
|
295
|
+
["Package manager", pm],
|
|
296
|
+
["Kizlo directory", setup.dir],
|
|
297
|
+
["Import alias", setup.alias || "relative imports"],
|
|
298
|
+
["API URL", setup.baseUrl || "fill in .env later"],
|
|
299
|
+
["WordPress", setup.mode === "local" ? "Local" : "Remote"],
|
|
300
|
+
["Examples", includeExamples ? "Yes" : "No"]
|
|
301
|
+
]);
|
|
302
|
+
if (!yes && !await confirmProceed(FINAL_CONFIRMATION_TEXT)) {
|
|
303
|
+
fetchedRegistry.cleanup();
|
|
304
|
+
p.cancel("Setup cancelled.");
|
|
305
|
+
process.exit(0);
|
|
306
|
+
}
|
|
307
|
+
if (shouldPersistPm) persistPackageManagerField(cwd, pm);
|
|
308
|
+
const warnings = [];
|
|
304
309
|
const scaffolded = [];
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
310
|
+
let gitignore = "present";
|
|
311
|
+
const kizloSpec = `kizlo@${manifest.dependencies?.kizlo ?? `^${getVersion()}`}`;
|
|
312
|
+
if (await runChecklist([
|
|
313
|
+
{
|
|
314
|
+
title: `Installing kizlo with ${pm}`,
|
|
315
|
+
enabled: !hasKizlo,
|
|
316
|
+
run: async () => {
|
|
317
|
+
if (await runCommandAsync(addDependencyArgs(pm, kizloSpec), cwd, "ignore")) return "Installed kizlo";
|
|
318
|
+
warnings.push(`Install it yourself: ${addDependencyArgs(pm, kizloSpec).join(" ")}`);
|
|
319
|
+
throw new StepError("Could not install kizlo automatically", { fatal: false });
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
title: "Aligning dependencies",
|
|
324
|
+
run: (message) => alignDependencies(cwd, pm, pkg, manifest, message, warnings)
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
title: "Setting up local WordPress (first run downloads images, this can take a while)",
|
|
328
|
+
enabled: setup.mode === "local",
|
|
329
|
+
run: async () => {
|
|
330
|
+
try {
|
|
331
|
+
const warning = await provisionLocalWordPress(cwd, setup);
|
|
332
|
+
if (warning) warnings.push(warning);
|
|
333
|
+
return "Local WordPress ready";
|
|
334
|
+
} catch (error) {
|
|
335
|
+
throw new StepError("Local WordPress setup failed", { detail: error instanceof Error ? error.message : String(error) });
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
title: "Writing environment files",
|
|
341
|
+
run: () => writeEnv(cwd, envKeys, setup, {
|
|
342
|
+
overwrite: overwriteEnv,
|
|
343
|
+
exampleTemplate: readEnvExample(entry.dir)
|
|
344
|
+
})
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
title: "Syncing settings to WordPress",
|
|
348
|
+
enabled: setup.mode === "remote" && Boolean(setup.wpUrl && setup.wpUsername && setup.wpPassword),
|
|
349
|
+
run: async () => {
|
|
350
|
+
warnings.push(...await syncRemote(setup));
|
|
351
|
+
return "Synced settings to WordPress";
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
title: "Scaffolding Kizlo files",
|
|
356
|
+
run: async () => {
|
|
357
|
+
for (const file of files) scaffolded.push({
|
|
358
|
+
file,
|
|
359
|
+
result: await scaffoldFile(cwd, file, {
|
|
360
|
+
force: overwritePaths.has(file.relPath),
|
|
361
|
+
yes: true
|
|
362
|
+
})
|
|
363
|
+
});
|
|
364
|
+
writeGeneratedContract(cwd, serverDirRel);
|
|
365
|
+
gitignore = ensureGitignored(cwd, ".env");
|
|
366
|
+
ensureGitignored(cwd, ".kizlo/");
|
|
367
|
+
const written = scaffolded.filter(({ result }) => result !== "kept").length;
|
|
368
|
+
return `Scaffolded ${written} file${written === 1 ? "" : "s"}`;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
])) {
|
|
372
|
+
p.cancel("Setup failed — see the errors above.");
|
|
373
|
+
process.exit(1);
|
|
374
|
+
}
|
|
375
|
+
for (const warning of warnings) p.log.warn(warning);
|
|
315
376
|
for (const { file, result } of scaffolded) reportScaffold(file, result, yes);
|
|
316
|
-
applyProjectPatches(cwd, patchEntries(changesFor(manifest, "init")), manifest.
|
|
317
|
-
for (const note of manifest.notes
|
|
377
|
+
applyProjectPatches(cwd, patchEntries(changesFor(manifest, "init")), manifest.config, scaffold);
|
|
378
|
+
for (const note of manifest.init.notes) {
|
|
318
379
|
const rendered = renderNote(note, setup.alias);
|
|
319
380
|
printManualStep(rendered.title, rendered.body);
|
|
320
381
|
}
|
|
@@ -130,7 +130,7 @@ async function resolveTestConfig(cwd) {
|
|
|
130
130
|
portExplicit: test.port !== void 0,
|
|
131
131
|
fixtures: test.fixtures ?? [],
|
|
132
132
|
credentialsPath: credentialsPath(cwd),
|
|
133
|
-
packageManager: test.packageManager ?? detectPackageManager(configDir) ?? "npm"
|
|
133
|
+
packageManager: test.packageManager ?? await detectPackageManager(configDir) ?? "npm"
|
|
134
134
|
};
|
|
135
135
|
}
|
|
136
136
|
/**
|
|
@@ -54,7 +54,7 @@ function readMeta(manifestPath) {
|
|
|
54
54
|
const raw = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
55
55
|
return {
|
|
56
56
|
name: typeof raw.name === "string" && raw.name.trim() ? raw.name.trim() : void 0,
|
|
57
|
-
|
|
57
|
+
id: typeof raw.id === "string" && raw.id.trim() ? raw.id.trim() : void 0
|
|
58
58
|
};
|
|
59
59
|
} catch {
|
|
60
60
|
return {};
|
|
@@ -69,7 +69,7 @@ function readMeta(manifestPath) {
|
|
|
69
69
|
* break the listing.
|
|
70
70
|
* - A single template: a directory that is itself a template (`template.json` at its root) — the escape
|
|
71
71
|
* hatch for a community template shipped on its own, e.g. `--source github:acme/kizlo-astro-template`.
|
|
72
|
-
* Its id comes from the manifest's `
|
|
72
|
+
* Its id comes from the manifest's `id` (a stable name that also matches what `init`'s detected
|
|
73
73
|
* preset asks for), not the directory name, which for a downloaded repo is a random temp folder.
|
|
74
74
|
*
|
|
75
75
|
* Discovery is by scanning, not a hardcoded list, and the manifest is read only for its display fields
|
|
@@ -88,10 +88,10 @@ function listTemplates(registryDir) {
|
|
|
88
88
|
if (!fs.existsSync(registryDir)) return [];
|
|
89
89
|
if (isSingleTemplate(registryDir)) {
|
|
90
90
|
const meta = readMeta(path.join(registryDir, "template.json"));
|
|
91
|
-
const id = meta.
|
|
91
|
+
const id = meta.id ?? path.basename(registryDir);
|
|
92
92
|
return [{
|
|
93
93
|
id,
|
|
94
|
-
label: meta.name ?? meta.
|
|
94
|
+
label: meta.name ?? meta.id ?? id,
|
|
95
95
|
dir: registryDir
|
|
96
96
|
}];
|
|
97
97
|
}
|
|
@@ -104,17 +104,22 @@ function listTemplates(registryDir) {
|
|
|
104
104
|
const meta = readMeta(manifestPath);
|
|
105
105
|
entries.push({
|
|
106
106
|
id: dirent.name,
|
|
107
|
-
label: meta.name ?? meta.
|
|
107
|
+
label: meta.name ?? meta.id ?? dirent.name,
|
|
108
108
|
dir
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
111
|
return entries.sort((a, b) => a.id.localeCompare(b.id));
|
|
112
112
|
}
|
|
113
|
-
/**
|
|
113
|
+
/**
|
|
114
|
+
* A template's detection package list — the `values` of every `dep` requirement under `init.requires`,
|
|
115
|
+
* read leniently (raw JSON, no schema) so one malformed manifest can't break detection for the rest of a
|
|
116
|
+
* registry. Full schema validation happens when the template is actually applied (`readManifest`).
|
|
117
|
+
*/
|
|
114
118
|
function readDetectDeps(templateDir) {
|
|
115
119
|
try {
|
|
116
|
-
const
|
|
117
|
-
|
|
120
|
+
const requires = JSON.parse(fs.readFileSync(path.join(templateDir, "template.json"), "utf8")).init?.requires;
|
|
121
|
+
if (!Array.isArray(requires)) return [];
|
|
122
|
+
return requires.filter((req) => req?.kind === "dep" && Array.isArray(req.values)).flatMap((req) => req.values).filter((name) => typeof name === "string");
|
|
118
123
|
} catch {
|
|
119
124
|
return [];
|
|
120
125
|
}
|