@homepages/template-kit 0.9.0-dev-20260719224319 → 0.9.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 (129) hide show
  1. package/CHANGELOG.md +29 -59
  2. package/README.md +14 -4
  3. package/dist/contracts/fill-treatments.d.ts +23 -0
  4. package/dist/index.d.ts +2 -2
  5. package/dist/index.js +2 -2
  6. package/dist/islands/discover.js +1 -1
  7. package/dist/islands/esbuild-plugin.js +1 -1
  8. package/dist/islands/wrap.d.ts +9 -1
  9. package/dist/media/resolve-fixture-media.d.ts +22 -0
  10. package/dist/package.js +1 -1
  11. package/dist/schema/resolve-section-ref.d.ts +4 -0
  12. package/dist/schema/rows.d.ts +9 -0
  13. package/dist/schema/section-nav.d.ts +5 -1
  14. package/dist/schema/slot-types.d.ts +1 -1
  15. package/dist/ssr.d.ts +7 -2
  16. package/dist/ssr.js +6 -2
  17. package/guide/INDEX.md +5 -0
  18. package/guide/check.md +1 -0
  19. package/guide/dev.md +1 -0
  20. package/guide/llms.txt +11 -3
  21. package/guide/upgrading.md +12 -8
  22. package/package.json +4 -16
  23. package/dist/canvas-core/chrome-css.d.ts +0 -24
  24. package/dist/canvas-core/chrome-css.js +0 -403
  25. package/dist/canvas-core/chrome.d.ts +0 -49
  26. package/dist/canvas-core/chrome.js +0 -147
  27. package/dist/canvas-core/frame.d.ts +0 -20
  28. package/dist/canvas-core/frame.js +0 -23
  29. package/dist/canvas-core/hidden-tabs.d.ts +0 -16
  30. package/dist/canvas-core/hidden-tabs.js +0 -84
  31. package/dist/canvas-core/resolve-target.js +0 -55
  32. package/dist/canvas-core/scroll.d.ts +0 -10
  33. package/dist/canvas-core/scroll.js +0 -15
  34. package/dist/canvas-core/section-dom.d.ts +0 -6
  35. package/dist/canvas-core/section-dom.js +0 -43
  36. package/dist/canvas-core/srcdoc.d.ts +0 -8
  37. package/dist/canvas-core/srcdoc.js +0 -77
  38. package/dist/canvas-core/types.d.ts +0 -76
  39. package/dist/canvas-core.d.ts +0 -9
  40. package/dist/canvas-core.js +0 -9
  41. package/dist/cli/check/config.js +0 -41
  42. package/dist/cli/check/css.js +0 -131
  43. package/dist/cli/check/diagnostics.js +0 -37
  44. package/dist/cli/check/index.js +0 -94
  45. package/dist/cli/check/loader.js +0 -149
  46. package/dist/cli/check/paths.js +0 -15
  47. package/dist/cli/check/relativize.js +0 -18
  48. package/dist/cli/check/render-invariants.js +0 -24
  49. package/dist/cli/check/resolve-tool.js +0 -38
  50. package/dist/cli/check/section-assets.js +0 -58
  51. package/dist/cli/check/stages/deps.js +0 -337
  52. package/dist/cli/check/stages/lint.js +0 -46
  53. package/dist/cli/check/stages/render.js +0 -98
  54. package/dist/cli/check/stages/size.js +0 -274
  55. package/dist/cli/check/stages/tree.js +0 -206
  56. package/dist/cli/check/stages/typecheck.js +0 -46
  57. package/dist/cli/check/stages/validate/catalog-exhaustiveness.js +0 -18
  58. package/dist/cli/check/stages/validate/facts.js +0 -19
  59. package/dist/cli/check/stages/validate/formatter-contract.js +0 -14
  60. package/dist/cli/check/stages/validate/group-contract.js +0 -21
  61. package/dist/cli/check/stages/validate/index.js +0 -66
  62. package/dist/cli/check/stages/validate/list-scalar-contract.js +0 -22
  63. package/dist/cli/check/stages/validate/nav-contract.js +0 -93
  64. package/dist/cli/check/stages/validate/nested-slot-contract.js +0 -12
  65. package/dist/cli/check/stages/validate/object-list-contract.js +0 -31
  66. package/dist/cli/check/stages/validate/orchestrator.js +0 -241
  67. package/dist/cli/check/stages/validate/produced-by-contract.js +0 -18
  68. package/dist/cli/check/stages/validate/row-contract.js +0 -31
  69. package/dist/cli/check/stages/validate/select-contract.js +0 -13
  70. package/dist/cli/check/stages/validate/source-contract.js +0 -21
  71. package/dist/cli/check/stages/validate/variant-contract.js +0 -15
  72. package/dist/cli/check/workspace.js +0 -99
  73. package/dist/cli/dev/build-css.js +0 -97
  74. package/dist/cli/dev/catalog.js +0 -35
  75. package/dist/cli/dev/compose-template.js +0 -39
  76. package/dist/cli/dev/content-override.js +0 -58
  77. package/dist/cli/dev/diagnostics.js +0 -68
  78. package/dist/cli/dev/discover.js +0 -35
  79. package/dist/cli/dev/fill-state.js +0 -77
  80. package/dist/cli/dev/index.js +0 -42
  81. package/dist/cli/dev/inspect.js +0 -38
  82. package/dist/cli/dev/island-bootstrap.js +0 -38
  83. package/dist/cli/dev/island-canvas-bootstrap.js +0 -204
  84. package/dist/cli/dev/island-map.js +0 -35
  85. package/dist/cli/dev/island-transform.js +0 -31
  86. package/dist/cli/dev/manifest-instances.js +0 -31
  87. package/dist/cli/dev/render-section.js +0 -24
  88. package/dist/cli/dev/screenshot-target.js +0 -24
  89. package/dist/cli/dev/section-page.js +0 -91
  90. package/dist/cli/dev/server.js +0 -511
  91. package/dist/cli/dev/slot-schema.js +0 -12
  92. package/dist/cli/dev/structure-summary.js +0 -119
  93. package/dist/cli/dev/tailwind.js +0 -32
  94. package/dist/cli/dev/tsconfig-paths-plugin.js +0 -90
  95. package/dist/cli/dev/vite-server.js +0 -39
  96. package/dist/cli/dev/workspace.js +0 -63
  97. package/dist/cli/link/index.js +0 -74
  98. package/dist/cli/link/overlay.js +0 -59
  99. package/dist/cli/link/watch.js +0 -25
  100. package/dist/cli/new/emit.js +0 -48
  101. package/dist/cli/new/index.js +0 -69
  102. package/dist/cli/new/scaffold/section/_Renderer.tsx +0 -20
  103. package/dist/cli/new/scaffold/section/_fill-spec.ts +0 -18
  104. package/dist/cli/new/scaffold/section/_fixtures.ts +0 -13
  105. package/dist/cli/new/scaffold/section/_schema.ts +0 -24
  106. package/dist/cli/new/scaffold/template/_manifest.json +0 -9
  107. package/dist/cli/new/scaffold/template/sections/hero/ExpandableText.tsx +0 -45
  108. package/dist/cli/new/scaffold/template/sections/hero/Renderer.tsx +0 -40
  109. package/dist/cli/new/scaffold/template/sections/hero/components/Feature.tsx +0 -14
  110. package/dist/cli/new/scaffold/template/sections/hero/fill-spec.ts +0 -29
  111. package/dist/cli/new/scaffold/template/sections/hero/fixtures.ts +0 -41
  112. package/dist/cli/new/scaffold/template/sections/hero/schema.ts +0 -60
  113. package/dist/cli/new/scaffold/template/theme.css +0 -24
  114. package/dist/cli/new/scaffold/template/theme.ts +0 -27
  115. package/dist/cli/new/scaffold-assets.js +0 -20
  116. package/dist/cli/pack/collect.js +0 -44
  117. package/dist/cli/pack/guards.js +0 -57
  118. package/dist/cli/pack/index.js +0 -66
  119. package/dist/cli/pack/manifest.js +0 -15
  120. package/dist/cli/pack/zip.js +0 -86
  121. package/dist/cli/theme/generate.js +0 -43
  122. package/dist/cli/theme/index.js +0 -33
  123. package/dist/cli/theme/load-theme.js +0 -69
  124. package/dist/cli.d.ts +0 -1
  125. package/dist/cli.js +0 -138
  126. package/dist/dev-client/assets/index-CJa9G1Na.css +0 -1
  127. package/dist/dev-client/assets/index-C_1atZs4.js +0 -450
  128. package/dist/dev-client/index.html +0 -13
  129. package/dist/tokens.css +0 -21
@@ -1,97 +0,0 @@
1
- import { compileThemeToCss } from "../../design-system/theme.js";
2
- import { loadEsbuild } from "../check/resolve-tool.js";
3
- import { ASSET_LOADERS, ASSET_NAMES, ASSET_PUBLIC_PATH, sectionCssFiles } from "../check/css.js";
4
- import { runTailwind } from "./tailwind.js";
5
- import { mkdir, readFile, writeFile } from "node:fs/promises";
6
- import { basename, join, relative } from "node:path";
7
-
8
- //#region src/cli/dev/build-css.ts
9
- /**
10
- * The Tailwind entry owns `@import "tailwindcss"` exactly once; `source(none)`
11
- * disables auto content-detection so only the enumerated section @sources are
12
- * scanned; the kit's styles.css brings the token preamble and opts its own
13
- * primitives back in. Every path is emitted relative to `entryDir` (where the
14
- * entry is written) so it resolves from that location.
15
- */
16
- function buildTailwindEntry(entryDir, sectionDirs, themePartialPath) {
17
- const rel = (target) => {
18
- const path = relative(entryDir, target);
19
- return path.startsWith(".") ? path : `./${path}`;
20
- };
21
- return [
22
- `@import "tailwindcss" source(none);`,
23
- `@import "@homepages/template-kit/styles.css";`,
24
- `@import "${rel(themePartialPath)}";`,
25
- ...sectionDirs.map((d) => `@source "${rel(d)}/**/*.tsx";`),
26
- ""
27
- ].join("\n");
28
- }
29
- async function compileUtilities(root, theme, sectionDirs) {
30
- const cacheDir = join(root, ".tk-dev-cache");
31
- await mkdir(cacheDir, { recursive: true });
32
- const input = join(cacheDir, "tw-entry.css");
33
- const themePath = join(cacheDir, "tw-theme.css");
34
- const output = join(cacheDir, "tw-out.css");
35
- await writeFile(themePath, compileThemeToCss({
36
- ...theme,
37
- fontFaces: void 0
38
- }), "utf8");
39
- await writeFile(input, buildTailwindEntry(cacheDir, sectionDirs, themePath), "utf8");
40
- await runTailwind(root, {
41
- input,
42
- output
43
- });
44
- return readFile(output, "utf8");
45
- }
46
- /**
47
- * Bundle every section's hand-written CSS through esbuild's `file` loader, so
48
- * each `url()` asset ref is rewritten to `/section-assets/<name>-<hash>.<ext>`
49
- * (production's form) and the referenced bytes are captured. CSS files are
50
- * concatenated in `sectionCssFiles` order (styles.css first). Assets are keyed
51
- * by emitted filename, which is content-hashed — so the same asset referenced
52
- * from two sections dedupes to one entry.
53
- */
54
- async function compileSectionHandCss(root, sectionDirs) {
55
- const { build } = await loadEsbuild(root);
56
- const assets = /* @__PURE__ */ new Map();
57
- const parts = [];
58
- for (const dir of sectionDirs) for (const cssFile of await sectionCssFiles(dir)) {
59
- const result = await build({
60
- entryPoints: [cssFile],
61
- bundle: true,
62
- write: false,
63
- outdir: "dist",
64
- loader: {
65
- ".css": "css",
66
- ...ASSET_LOADERS
67
- },
68
- publicPath: ASSET_PUBLIC_PATH,
69
- assetNames: ASSET_NAMES,
70
- absWorkingDir: root,
71
- logLevel: "silent"
72
- });
73
- for (const file of result.outputFiles ?? []) if (file.path.endsWith(".css")) parts.push(file.text);
74
- else assets.set(basename(file.path), file.contents);
75
- }
76
- return {
77
- css: parts.join("\n"),
78
- assets
79
- };
80
- }
81
- /** theme :root/@theme custom props, the compiled Tailwind utilities, then each
82
- * section's hand-CSS with its `url()` asset refs rewritten to servable paths. */
83
- async function assembleTemplateCss(root, theme, sectionDirs) {
84
- const utilities = await compileUtilities(root, theme, sectionDirs);
85
- const sections = await compileSectionHandCss(root, sectionDirs);
86
- return {
87
- css: [
88
- theme.fontFaces,
89
- utilities,
90
- sections.css
91
- ].filter(Boolean).join("\n"),
92
- assets: sections.assets
93
- };
94
- }
95
-
96
- //#endregion
97
- export { assembleTemplateCss, buildTailwindEntry };
@@ -1,35 +0,0 @@
1
- import { VERIFICATION_WIDTHS } from "../../design-system/breakpoints.js";
2
- import { loadSectionBundle } from "../../ssr.js";
3
- import { discoverSections } from "./discover.js";
4
- import { join } from "node:path";
5
-
6
- //#region src/cli/dev/catalog.ts
7
- async function buildCatalog(vite, workspace) {
8
- const load = (dir, file) => vite.ssrLoadModule(join(dir, file));
9
- const templates = [];
10
- for (const template of workspace.templates) {
11
- const sections = [];
12
- for (const section of await discoverSections(template.dir)) try {
13
- const { invariantFixtures } = await loadSectionBundle(section.dir, load);
14
- sections.push({
15
- key: section.name,
16
- fixtures: invariantFixtures.map((f) => ({
17
- id: f.id,
18
- label: f.label
19
- }))
20
- });
21
- } catch {}
22
- templates.push({
23
- key: template.key,
24
- sections
25
- });
26
- }
27
- return {
28
- templates,
29
- breakpoints: [...VERIFICATION_WIDTHS],
30
- defaultTemplate: workspace.cwdTemplate
31
- };
32
- }
33
-
34
- //#endregion
35
- export { buildCatalog };
@@ -1,39 +0,0 @@
1
- import { ATTR_SECTION_INSTANCE_ID } from "../../contracts/markers.js";
2
- import { buildSectionIslandMap } from "./island-map.js";
3
- import { resolveManifestInstances } from "./manifest-instances.js";
4
- import { renderSection } from "./render-section.js";
5
- import { wrapSectionHtml } from "./section-page.js";
6
-
7
- //#region src/cli/dev/compose-template.ts
8
- function escapeAttr(value) {
9
- return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
10
- }
11
- function wrapInstance(instanceId, label, html) {
12
- return `<div ${ATTR_SECTION_INSTANCE_ID}="${escapeAttr(instanceId)}" data-section-label="${escapeAttr(label)}">${html}</div>`;
13
- }
14
- async function composeTemplatePage(vite, args) {
15
- const { workspace, template, islandBootstrapUrl, canvasIslandBootstrapUrl, canvas = false } = args;
16
- const instances = await resolveManifestInstances(template);
17
- const bodies = [];
18
- const islands = {};
19
- for (const inst of instances) {
20
- const { html } = await renderSection(vite, {
21
- sectionDir: inst.sectionDir,
22
- fixture: inst.fixture,
23
- override: Object.keys(inst.options).length > 0 ? { options: inst.options } : void 0
24
- });
25
- bodies.push(canvas ? wrapInstance(inst.instanceId, inst.section, html) : html);
26
- Object.assign(islands, await buildSectionIslandMap(workspace.root, workspace.templatesDir, inst.sectionDir));
27
- }
28
- return wrapSectionHtml({
29
- sectionHtml: bodies.join("\n"),
30
- cssRev: "0",
31
- template: template.key,
32
- section: "",
33
- islands,
34
- bootstrapUrl: canvas ? canvasIslandBootstrapUrl : islandBootstrapUrl
35
- });
36
- }
37
-
38
- //#endregion
39
- export { composeTemplatePage };
@@ -1,58 +0,0 @@
1
- import { z } from "zod";
2
-
3
- //#region src/cli/dev/content-override.ts
4
- const MAX_URL_OVERRIDE_BYTES = 16 * 1024;
5
- const ContentOverrideSchema = z.object({
6
- slots: z.record(z.string(), z.unknown()).optional(),
7
- options: z.record(z.string(), z.unknown()).optional(),
8
- nav: z.object({
9
- selfAnchor: z.string().optional(),
10
- anchors: z.record(z.string(), z.unknown()).optional()
11
- }).strict().optional(),
12
- variants: z.record(z.string(), z.string()).optional()
13
- }).strict();
14
- function isPlainObject(v) {
15
- return typeof v === "object" && v !== null && !Array.isArray(v);
16
- }
17
- /**
18
- * Merge `patch` onto `base`: plain objects recurse key-by-key; anything else
19
- * (scalar, array, or `null`) replaces. Keys absent from `patch` keep the `base`
20
- * value. Pure — never mutates its inputs.
21
- */
22
- function deepMerge(base, patch) {
23
- if (!isPlainObject(base) || !isPlainObject(patch)) return patch;
24
- const out = { ...base };
25
- for (const [k, v] of Object.entries(patch)) out[k] = k in base ? deepMerge(base[k], v) : v;
26
- return out;
27
- }
28
- /** Deep-merge an override onto resolved props. `undefined` returns `base` as-is. */
29
- function applyContentOverride(base, override) {
30
- if (!override) return base;
31
- return {
32
- slots: deepMerge(base.slots, override.slots ?? {}),
33
- options: deepMerge(base.options, override.options ?? {}),
34
- nav: deepMerge(base.nav, override.nav ?? {}),
35
- variants: deepMerge(base.variants, override.variants ?? {})
36
- };
37
- }
38
- /** Validate untrusted input into a `ContentOverride`. `undefined`/absent → `undefined`. */
39
- function parseContentOverride(input) {
40
- if (input === void 0 || input === null) return void 0;
41
- return ContentOverrideSchema.parse(input);
42
- }
43
- /** Decode a base64 `?overrides=` URL param, enforce the URL ceiling, and validate. */
44
- function decodeUrlOverride(raw) {
45
- if (!raw) return void 0;
46
- const text = Buffer.from(raw, "base64").toString("utf8");
47
- if (Buffer.byteLength(text, "utf8") > 16384) throw new Error(`overrides payload exceeds ${MAX_URL_OVERRIDE_BYTES} bytes`);
48
- let json;
49
- try {
50
- json = JSON.parse(text);
51
- } catch (err) {
52
- throw new Error(`invalid overrides payload: ${err instanceof Error ? err.message : String(err)}`);
53
- }
54
- return parseContentOverride(json);
55
- }
56
-
57
- //#endregion
58
- export { MAX_URL_OVERRIDE_BYTES, applyContentOverride, decodeUrlOverride, deepMerge, parseContentOverride };
@@ -1,68 +0,0 @@
1
- import { promisify } from "node:util";
2
- import { execFile } from "node:child_process";
3
-
4
- //#region src/cli/dev/diagnostics.ts
5
- const exec = promisify(execFile);
6
- const MAX_BUFFER = 1024 * 1024;
7
- const TIMEOUT_MS = 15e3;
8
- async function runDiagnostics(cmd, args) {
9
- if (cmd === null) return { available: false };
10
- const parts = cmd.trim().split(/\s+/);
11
- const program = parts[0];
12
- if (!program) return { available: false };
13
- const baseArgs = parts.slice(1);
14
- let stdout;
15
- try {
16
- stdout = (await exec(program, [
17
- ...baseArgs,
18
- "--template",
19
- args.template,
20
- "--section",
21
- args.section
22
- ], {
23
- maxBuffer: MAX_BUFFER,
24
- timeout: TIMEOUT_MS
25
- })).stdout;
26
- } catch (err) {
27
- return {
28
- available: true,
29
- error: err instanceof Error ? err.message : String(err)
30
- };
31
- }
32
- return parseViolations(stdout);
33
- }
34
- function parseViolations(stdout) {
35
- let parsed;
36
- try {
37
- parsed = JSON.parse(stdout);
38
- } catch {
39
- return {
40
- available: true,
41
- error: `diagnostics command did not print valid JSON: ${stdout.slice(0, 200)}`
42
- };
43
- }
44
- const body = parsed;
45
- if (typeof parsed !== "object" || parsed === null || !Array.isArray(body.violations)) return {
46
- available: true,
47
- error: `diagnostics command JSON is missing a "violations" array`
48
- };
49
- const violations = [];
50
- for (const raw of body.violations) {
51
- if (typeof raw !== "object" || raw === null) continue;
52
- const v = raw;
53
- if (typeof v.rule !== "string" || typeof v.message !== "string") continue;
54
- violations.push({
55
- rule: v.rule,
56
- message: v.message,
57
- ...typeof v.file === "string" ? { file: v.file } : {},
58
- ...typeof v.line === "number" ? { line: v.line } : {}
59
- });
60
- }
61
- return {
62
- available: true,
63
- violations
64
- };
65
- }
66
-
67
- //#endregion
68
- export { runDiagnostics };
@@ -1,35 +0,0 @@
1
- import { readdir } from "node:fs/promises";
2
- import { join } from "node:path";
3
- import { existsSync } from "node:fs";
4
-
5
- //#region src/cli/dev/discover.ts
6
- const REQUIRED_SECTION_FILES = [
7
- "Renderer.tsx",
8
- "schema.ts",
9
- "fill-spec.ts",
10
- "fixtures.ts"
11
- ];
12
- /**
13
- * Every subdirectory of `<templateDir>/sections` that carries all four contract
14
- * files, sorted by name. A folder missing any of the four is not a section and
15
- * is skipped (not an error — it may be a work in progress).
16
- */
17
- async function discoverSections(templateDir) {
18
- const sectionsDir = join(templateDir, "sections");
19
- if (!existsSync(sectionsDir)) return [];
20
- const entries = await readdir(sectionsDir, { withFileTypes: true });
21
- const out = [];
22
- for (const entry of entries) {
23
- if (!entry.isDirectory()) continue;
24
- const dir = join(sectionsDir, entry.name);
25
- if (REQUIRED_SECTION_FILES.every((file) => existsSync(join(dir, file)))) out.push({
26
- name: entry.name,
27
- dir
28
- });
29
- }
30
- out.sort((a, b) => a.name.localeCompare(b.name));
31
- return out;
32
- }
33
-
34
- //#endregion
35
- export { REQUIRED_SECTION_FILES, discoverSections };
@@ -1,77 +0,0 @@
1
- //#region src/cli/dev/fill-state.ts
2
- const LIST_TYPES = /* @__PURE__ */ new Set([
3
- "list",
4
- "poi_list",
5
- "image_collection",
6
- "video_collection",
7
- "object_list"
8
- ]);
9
- const ASSET_TYPES = /* @__PURE__ */ new Set(["image", "video"]);
10
- /** Whether a resolved slot value carries real content for its type. */
11
- function slotHasContent(value, type) {
12
- if (value === null || value === void 0) return false;
13
- if (LIST_TYPES.has(type)) return Array.isArray(value) && value.length > 0;
14
- if (ASSET_TYPES.has(type)) return typeof value === "object" && Boolean(value.url);
15
- if (typeof value === "string") return value.trim().length > 0;
16
- return true;
17
- }
18
- /** Every `data-slot-id` in the rendered tree. */
19
- function collectRenderedSlotIds(root) {
20
- const ids = /* @__PURE__ */ new Set();
21
- const walk = (node) => {
22
- if (node.slotId) ids.add(node.slotId);
23
- node.children.forEach(walk);
24
- };
25
- walk(root);
26
- return ids;
27
- }
28
- /**
29
- * Best-effort line number (1-based) of each `<slotId>:` declaration inside the
30
- * `slots:` block of a section's schema.ts source. A slot not located is omitted.
31
- */
32
- function schemaSlotLines(source, slotIds) {
33
- const lines = source.split("\n");
34
- const slotsAt = lines.findIndex((l) => /(^|\s)slots\s*:/.test(l));
35
- const out = {};
36
- if (slotsAt < 0) return out;
37
- for (const id of slotIds) {
38
- const re = new RegExp(`^\\s*${id}\\s*:`);
39
- const idx = lines.findIndex((l, i) => i > slotsAt && re.test(l));
40
- if (idx >= 0) out[id] = idx + 1;
41
- }
42
- return out;
43
- }
44
- function truncate(s, n = 80) {
45
- return s.length > n ? s.slice(0, n) + "…" : s;
46
- }
47
- function valuePreview(value, type) {
48
- if (LIST_TYPES.has(type)) return Array.isArray(value) ? `[${value.length} items]` : void 0;
49
- if (ASSET_TYPES.has(type)) {
50
- const url = value?.url;
51
- return url ? `${type}:${truncate(url)}` : void 0;
52
- }
53
- if (typeof value === "string") return truncate(value);
54
- if (typeof value === "number" || typeof value === "boolean") return String(value);
55
- }
56
- function computeSlotFills(args) {
57
- const { declaredSlots, resolvedSlots, renderedSlotIds, slotLines } = args;
58
- return Object.entries(declaredSlots).map(([id, def]) => {
59
- const required = def.required ?? false;
60
- const has = slotHasContent(resolvedSlots[id], def.type);
61
- const fillState = has ? "filled" : required ? "missing" : "empty";
62
- const preview = has ? valuePreview(resolvedSlots[id], def.type) : void 0;
63
- const line = slotLines[id];
64
- return {
65
- id,
66
- type: def.type,
67
- required,
68
- fillState,
69
- rendered: renderedSlotIds.has(id),
70
- ...preview !== void 0 ? { value: preview } : {},
71
- at: line ? `schema.ts:${line}` : "schema.ts"
72
- };
73
- });
74
- }
75
-
76
- //#endregion
77
- export { collectRenderedSlotIds, computeSlotFills, schemaSlotLines, slotHasContent };
@@ -1,42 +0,0 @@
1
- import { startDevServer } from "./server.js";
2
-
3
- //#region src/cli/dev/index.ts
4
- const DEFAULT_PORT = 5180;
5
- async function runDev(argv, cwd) {
6
- const port = parsePort(argv) ?? DEFAULT_PORT;
7
- const diagnosticsCmd = parseDiagnosticsCmd(argv);
8
- const server = await startDevServer({
9
- cwd,
10
- port,
11
- diagnosticsCmd
12
- });
13
- process.stdout.write(`template-kit dev — ${server.workspace.templates.length} template(s) at ${server.url}\n`);
14
- for (const template of server.workspace.templates) process.stdout.write(` ${template.key}: ${server.url}/sections/${template.key}/<section>/<fixture>\n`);
15
- if (diagnosticsCmd) process.stdout.write(` diagnostics: ${diagnosticsCmd}\n`);
16
- await new Promise((resolve) => {
17
- const stop = () => {
18
- server.close().then(resolve);
19
- };
20
- process.once("SIGINT", stop);
21
- process.once("SIGTERM", stop);
22
- });
23
- return 0;
24
- }
25
- function parsePort(argv) {
26
- const i = argv.indexOf("--port");
27
- if (i >= 0) {
28
- const raw = argv[i + 1];
29
- const n = raw !== void 0 ? Number(raw) : NaN;
30
- if (Number.isInteger(n) && n > 0) return n;
31
- }
32
- return null;
33
- }
34
- function parseDiagnosticsCmd(argv) {
35
- const i = argv.indexOf("--diagnostics-cmd");
36
- if (i < 0) return null;
37
- const value = argv[i + 1];
38
- return typeof value === "string" ? value : null;
39
- }
40
-
41
- //#endregion
42
- export { parseDiagnosticsCmd, runDev };
@@ -1,38 +0,0 @@
1
- import { renderSection } from "./render-section.js";
2
- import { collectRenderedSlotIds, computeSlotFills, schemaSlotLines } from "./fill-state.js";
3
- import { summarizeHtml } from "./structure-summary.js";
4
- import { readFile } from "node:fs/promises";
5
- import { join } from "node:path";
6
-
7
- //#region src/cli/dev/inspect.ts
8
- async function inspectSection(vite, args) {
9
- const result = await renderSection(vite, {
10
- sectionDir: args.sectionDir,
11
- fixture: args.fixture,
12
- override: args.override
13
- });
14
- const { tree, text } = summarizeHtml(result.html);
15
- const schemaSource = await readFile(join(args.sectionDir, "schema.ts"), "utf8");
16
- const slots = computeSlotFills({
17
- declaredSlots: result.schemaSlots,
18
- resolvedSlots: result.resolvedProps.slots,
19
- renderedSlotIds: collectRenderedSlotIds(tree),
20
- slotLines: schemaSlotLines(schemaSource, Object.keys(result.schemaSlots))
21
- });
22
- return {
23
- target: {
24
- template: args.template,
25
- section: args.section,
26
- fixture: args.fixture,
27
- schemaFile: `sections/${args.section}/schema.ts`
28
- },
29
- slots,
30
- options: result.resolvedProps.options,
31
- variants: result.resolvedProps.variants,
32
- nullCorpus: result.nullCorpus,
33
- ...args.withOutline ? { outline: text } : {}
34
- };
35
- }
36
-
37
- //#endregion
38
- export { inspectSection };
@@ -1,38 +0,0 @@
1
- import { mkdir, writeFile } from "node:fs/promises";
2
- import { join } from "node:path";
3
-
4
- //#region src/cli/dev/island-bootstrap.ts
5
- const CACHE_DIR = ".tk-dev-cache";
6
- const BOOTSTRAP_FILE = "island-bootstrap.js";
7
- const BOOTSTRAP_SOURCE = `import { hydrateIslands } from "@homepages/template-kit/island-runtime";
8
-
9
- const modules = import.meta.glob("/templates/**/*.{tsx,jsx}");
10
- const map = window.__TR_ISLAND_MAP || {};
11
-
12
- hydrateIslands({
13
- load: (key) => {
14
- const url = map[key];
15
- const importer = url ? modules[url] : undefined;
16
- if (!importer) return Promise.reject(new Error("template-kit dev: no island module for " + key));
17
- return importer();
18
- },
19
- onError: (error, ctx) => {
20
- console.error("template-kit dev: island failed to hydrate:", ctx.key, error);
21
- },
22
- });
23
- `;
24
- /**
25
- * Write the shared bootstrap into the workspace cache dir and return the URL the
26
- * page loads it from. It lives under the root so Vite serves + transforms it (the
27
- * bare `island-runtime` import and the glob), reached via Vite's `/@fs/` prefix.
28
- */
29
- async function writeIslandBootstrap(root) {
30
- const dir = join(root, CACHE_DIR);
31
- await mkdir(dir, { recursive: true });
32
- const abs = join(dir, BOOTSTRAP_FILE);
33
- await writeFile(abs, BOOTSTRAP_SOURCE, "utf8");
34
- return `/@fs/${abs}`;
35
- }
36
-
37
- //#endregion
38
- export { writeIslandBootstrap };