@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,29 +0,0 @@
1
- import type { FillSpecShape } from "@homepages/template-kit";
2
-
3
- // The fill spec drives the AI fill. `headline` is a direct-source slot
4
- // (SOURCE_FIELDS "address") baked by the engine — it needs no decision here.
5
- // `hero_image` and `blurb` each name a decision whose id matches their
6
- // `produced_by` in schema.ts.
7
- export const fillSpec = {
8
- reads: [],
9
- decisions: [
10
- {
11
- id: "hero_image_assign",
12
- type: "image-assign",
13
- produces: ["hero_image"],
14
- tag_query: "category:exterior",
15
- selection_prompt: "Pick a clear, well-lit exterior shot of the property.",
16
- },
17
- {
18
- id: "blurb_text",
19
- type: "text-block",
20
- produces: ["blurb"],
21
- length_cap: 400,
22
- // `structure` enum is single_sentence | paragraph | bullet_list — not "single_paragraph".
23
- structure: "paragraph",
24
- perspective: "third",
25
- voice_prompt: "Write a warm, concrete paragraph a buyer would want to read.",
26
- content_prompt: "Describe what makes this home appealing in 2–4 sentences.",
27
- },
28
- ],
29
- } as const satisfies FillSpecShape;
@@ -1,41 +0,0 @@
1
- import type { FixtureModule } from "@homepages/template-kit";
2
- import { luxuryMultiUnit } from "@homepages/template-kit/fixtures";
3
-
4
- // Base content the section is designed for. Nullability coverage (each optional
5
- // slot rendered empty) is derived automatically from schema.ts — no fixture
6
- // needed for it. Add a `states` entry only for a designed scenario that
7
- // combines overrides at once (a content-length or option branch the section
8
- // renders differently).
9
- const heroImage = luxuryMultiUnit.photos[19]!; // exterior facade shot
10
-
11
- const fixtures: FixtureModule = {
12
- base: {
13
- slots: {
14
- headline: luxuryMultiUnit.facts.address.display,
15
- // State the asset id, exactly as a saved page does — the preview server
16
- // resolves it to a URL and a responsive ladder, the same way the API does
17
- // when the page is published. A fixture never states a URL.
18
- hero_image: {
19
- photo_id: heroImage.id,
20
- alt: heroImage.alt,
21
- },
22
- blurb: "A 12-unit mansard Victorian in Jamaica Plain, moments from the Emerald Necklace.",
23
- },
24
- options: { align: "center" },
25
- },
26
- states: {
27
- // Left-aligned layout branch (option override).
28
- left: { options: { align: "left" } },
29
- // Long-blurb branch: exercises the island's expand/collapse path.
30
- "long-blurb": {
31
- slots: {
32
- blurb:
33
- "A 12-unit mansard Victorian in Jamaica Plain, with a terrazzo lobby, an " +
34
- "on-site gym, quartz kitchens throughout, and a top-floor penthouse with a " +
35
- "private rooftop terrace and skyline views — all moments from the Emerald Necklace.",
36
- },
37
- },
38
- },
39
- };
40
-
41
- export default fixtures;
@@ -1,60 +0,0 @@
1
- import type { SectionProps, SectionSchemaShape } from "@homepages/template-kit";
2
- import { direct } from "@homepages/template-kit";
3
-
4
- // Hero — the starter section. It exercises the whole authoring contract in one
5
- // place so the first section you read teaches the shape:
6
- //
7
- // Slots (editable content, declared here in the order the Renderer paints
8
- // them — the sidebar-order check enforces that the two agree):
9
- // hero_image image, AI-assigned from the property's photos; `crop` tells
10
- // the editor's cropper to keep a locked 16:9 frame.
11
- // headline text, fact-bound with direct("address") — seeded verbatim
12
- // from the property, no model call.
13
- // blurb text, AI-written; the island below makes it expandable.
14
- //
15
- // Options (behavioral, not editable content):
16
- // align where the text column sits — "center" (default) or "left".
17
- //
18
- // Every editable slot either declares a `source` (baked from the property
19
- // graph) or a `produced_by` that names a decision in fill-spec.ts.
20
- export const schema = {
21
- meta: {
22
- displayName: "Hero",
23
- },
24
- slots: {
25
- hero_image: {
26
- type: "image",
27
- editable_by_user: true,
28
- required: true,
29
- produced_by: "hero_image_assign",
30
- display_name: "Hero image",
31
- // The editor's cropper keeps a locked 16:9 frame; the pill reads "16:9".
32
- crop: { mode: "locked", aspect: 16 / 9, aspectLabel: "16:9" },
33
- },
34
- headline: {
35
- type: "text",
36
- size: "medium",
37
- editable_by_user: true,
38
- required: true,
39
- source: direct("address"),
40
- display_name: "Headline",
41
- },
42
- blurb: {
43
- type: "text",
44
- size: "long",
45
- editable_by_user: true,
46
- required: true,
47
- produced_by: "blurb_text",
48
- display_name: "Blurb",
49
- },
50
- },
51
- options: {
52
- // An enum option: its value type is the union of `values`. Read it in the
53
- // Renderer as `options.align` — never hand-write the props type.
54
- align: { type: "enum", values: ["center", "left"], default: "center" },
55
- },
56
- } as const satisfies SectionSchemaShape;
57
-
58
- // Renderer prop type — derived from `schema`. Do not hand-write a separate type;
59
- // the props-from-schema lint rule enforces this.
60
- export type Props = SectionProps<typeof schema>;
@@ -1,24 +0,0 @@
1
- /* Generated by `template-kit theme` from theme.ts — do not edit.
2
- Re-run `template-kit theme` after changing theme.ts. */
3
- @theme inline {
4
- --color-accent: var(--tr-color-accent);
5
- --color-background: var(--tr-color-background);
6
- --color-ink: var(--tr-color-ink);
7
- --font-body: var(--tr-font-body);
8
- --text-base: var(--tr-text-base);
9
- }
10
-
11
- :root {
12
- --tr-color-accent: #2f6df6;
13
- --tr-color-background: #ffffff;
14
- --tr-color-ink: #1a1d21;
15
- --tr-font-body: system-ui, -apple-system, sans-serif;
16
- --tr-text-base: 1rem;
17
- }
18
-
19
- body {
20
- font-family: var(--tr-font-body);
21
- color: var(--tr-color-ink);
22
- background: var(--tr-color-background);
23
- font-size: var(--tr-text-base);
24
- }
@@ -1,27 +0,0 @@
1
- import type { TokenTheme } from "@homepages/template-kit";
2
-
3
- // A template's design tokens. `template-kit theme` compiles this to theme.css;
4
- // `template-kit dev` reads it directly. The `document` refs must name declared
5
- // tokens (color/background → colors, font → fonts, fontSize → type).
6
- export const theme: TokenTheme = {
7
- colors: {
8
- ink: "#1a1d21",
9
- background: "#ffffff",
10
- accent: "#2f6df6",
11
- },
12
- fonts: {
13
- body: "system-ui, -apple-system, sans-serif",
14
- },
15
- type: {
16
- base: "1rem",
17
- },
18
- radii: {},
19
- shadows: {},
20
- layout: {},
21
- document: {
22
- font: "body",
23
- color: "ink",
24
- background: "background",
25
- fontSize: "base",
26
- },
27
- };
@@ -1,20 +0,0 @@
1
- import { dirname, join } from "node:path";
2
- import { fileURLToPath } from "node:url";
3
- import { existsSync } from "node:fs";
4
-
5
- //#region src/cli/new/scaffold-assets.ts
6
- const here = dirname(fileURLToPath(import.meta.url));
7
- function assetsRoot() {
8
- const dist = join(here, "scaffold");
9
- if (existsSync(dist)) return dist;
10
- return join(here, "..", "..", "..", "..", "create-workspace", "scaffold");
11
- }
12
- function templateAssetsDir() {
13
- return join(assetsRoot(), "template");
14
- }
15
- function sectionAssetsDir() {
16
- return join(assetsRoot(), "section");
17
- }
18
-
19
- //#endregion
20
- export { sectionAssetsDir, templateAssetsDir };
@@ -1,44 +0,0 @@
1
- import { readFile, readdir } from "node:fs/promises";
2
- import { join, relative, sep } from "node:path";
3
- import { createHash } from "node:crypto";
4
-
5
- //#region src/cli/pack/collect.ts
6
- const toPosix = (p) => p.split(sep).join("/");
7
- async function walk(dir, out) {
8
- for (const entry of await readdir(dir, { withFileTypes: true })) if (entry.isDirectory()) {
9
- if (entry.name === "node_modules") continue;
10
- await walk(join(dir, entry.name), out);
11
- } else if (entry.isFile()) out.push(join(dir, entry.name));
12
- }
13
- /**
14
- * Reads a template's regular files into sorted, hashable entries.
15
- *
16
- * Only real files and directories are walked: `node_modules` is skipped at any
17
- * depth, and symlinks are omitted (a `Dirent` is neither `isFile()` nor
18
- * `isDirectory()`), so a symlinked source file will not appear in the pack.
19
- * Templates are authored as plain trees, so this is a non-issue in practice —
20
- * but a submission that relies on a symlinked file would pack without it.
21
- */
22
- async function collectTemplateFiles(workspaceRoot, templateDir) {
23
- const absPaths = [];
24
- await walk(templateDir, absPaths);
25
- const files = await Promise.all(absPaths.map(async (abs) => ({
26
- archivePath: toPosix(relative(workspaceRoot, abs)),
27
- templatePath: toPosix(relative(templateDir, abs)),
28
- bytes: await readFile(abs)
29
- })));
30
- files.sort((a, b) => a.archivePath < b.archivePath ? -1 : a.archivePath > b.archivePath ? 1 : 0);
31
- return files;
32
- }
33
- function contentHash(files) {
34
- const ordered = [...files].sort((a, b) => a.templatePath < b.templatePath ? -1 : a.templatePath > b.templatePath ? 1 : 0);
35
- const top = createHash("sha256");
36
- for (const file of ordered) {
37
- const inner = createHash("sha256").update(file.bytes).digest("hex");
38
- top.update(file.templatePath).update("\0").update(inner).update("\n");
39
- }
40
- return `sha256:${top.digest("hex")}`;
41
- }
42
-
43
- //#endregion
44
- export { collectTemplateFiles, contentHash };
@@ -1,57 +0,0 @@
1
- import { readLinkMarker } from "../link/overlay.js";
2
- import { z } from "zod";
3
- import { readFile, stat } from "node:fs/promises";
4
- import { join } from "node:path";
5
-
6
- //#region src/cli/pack/guards.ts
7
- const depsSchema = z.object({
8
- dependencies: z.record(z.string(), z.string()).optional(),
9
- devDependencies: z.record(z.string(), z.string()).optional(),
10
- optionalDependencies: z.record(z.string(), z.string()).optional()
11
- }).passthrough();
12
- async function isFile(p) {
13
- try {
14
- return (await stat(p)).isFile();
15
- } catch {
16
- return false;
17
- }
18
- }
19
- function localProtocolDeps(pkg) {
20
- const offenders = [];
21
- for (const block of [
22
- pkg.dependencies,
23
- pkg.devDependencies,
24
- pkg.optionalDependencies
25
- ]) for (const [name, spec] of Object.entries(block ?? {})) if (spec.startsWith("file:") || spec.startsWith("link:")) offenders.push(`${name} (${spec})`);
26
- return offenders;
27
- }
28
- async function preflightGuards(workspaceRoot) {
29
- const refusals = [];
30
- if (await readLinkMarker(workspaceRoot) !== void 0) refusals.push({
31
- reason: "a linked kit overlay is active in this workspace.",
32
- fix: "Run `template-kit unlink` to restore the published kit, then pack."
33
- });
34
- if (!await isFile(join(workspaceRoot, "package-lock.json"))) refusals.push({
35
- reason: "missing lockfile (package-lock.json) at workspace root.",
36
- fix: "Run npm install and commit the lockfile — a submission without one cannot be reproduced."
37
- });
38
- let pkgJson;
39
- try {
40
- pkgJson = JSON.parse(await readFile(join(workspaceRoot, "package.json"), "utf8"));
41
- } catch {
42
- refusals.push({
43
- reason: "package.json at the workspace root is missing or not valid JSON.",
44
- fix: "Ensure the workspace root has a well-formed package.json before packing."
45
- });
46
- return refusals;
47
- }
48
- const parsed = depsSchema.safeParse(pkgJson);
49
- if (parsed.success) for (const dep of localProtocolDeps(parsed.data)) refusals.push({
50
- reason: `dependency ${dep} uses a local file:/link: protocol.`,
51
- fix: "Depend on published versions only — a submission must install from the registry."
52
- });
53
- return refusals;
54
- }
55
-
56
- //#endregion
57
- export { preflightGuards };
@@ -1,66 +0,0 @@
1
- import { findWorkspaceRoot, selectTemplates, templateFromCwd } from "../check/workspace.js";
2
- import { runCheck } from "../check/index.js";
3
- import { collectTemplateFiles, contentHash } from "./collect.js";
4
- import { preflightGuards } from "./guards.js";
5
- import { buildPackManifest } from "./manifest.js";
6
- import { buildZip } from "./zip.js";
7
- import { parseArgs } from "node:util";
8
- import { readFile, writeFile } from "node:fs/promises";
9
- import { join } from "node:path";
10
-
11
- //#region src/cli/pack/index.ts
12
- function refusal(lines) {
13
- return {
14
- code: 1,
15
- output: `template-kit pack: cannot pack.\n${lines.join("\n")}\n`
16
- };
17
- }
18
- async function runPack(argv, cwd) {
19
- const { positionals } = parseArgs({
20
- args: argv,
21
- allowPositionals: true
22
- });
23
- const root = await findWorkspaceRoot(cwd);
24
- const template = (await selectTemplates(root, positionals[0] ?? templateFromCwd(root, cwd), false, "pack"))[0];
25
- if (template === void 0) return refusal([" this workspace has no template to pack: templates/ holds no template folder."]);
26
- const preflight = await preflightGuards(root);
27
- if (preflight.length > 0) return refusal(preflight.map((r) => ` ${r.reason}\n fix: ${r.fix}`));
28
- const { code: checkCode, output: checkOut } = await runCheck([template.key], cwd);
29
- if (checkCode !== 0) return {
30
- code: 1,
31
- output: `template-kit pack: \`check\` must pass before packing.\n\n${checkOut}`
32
- };
33
- const files = await collectTemplateFiles(root, template.dir);
34
- const hash = contentHash(files);
35
- const manifest = buildPackManifest({
36
- template: template.key,
37
- contentHash: hash,
38
- packedAt: (/* @__PURE__ */ new Date()).toISOString()
39
- });
40
- const entries = [
41
- {
42
- path: "pack-manifest.json",
43
- bytes: Buffer.from(`${JSON.stringify(manifest, null, 2)}\n`, "utf8")
44
- },
45
- {
46
- path: "package.json",
47
- bytes: await readFile(join(root, "package.json"))
48
- },
49
- {
50
- path: "package-lock.json",
51
- bytes: await readFile(join(root, "package-lock.json"))
52
- },
53
- ...files.map((f) => ({
54
- path: f.archivePath,
55
- bytes: f.bytes
56
- }))
57
- ];
58
- await writeFile(join(root, `${template.key}.zip`), buildZip(entries));
59
- return {
60
- code: 0,
61
- output: `template-kit pack\n wrote ${template.key}.zip\n ${hash}\n kit ${manifest.kitVersion}`
62
- };
63
- }
64
-
65
- //#endregion
66
- export { runPack };
@@ -1,15 +0,0 @@
1
- import { KIT_VERSION } from "../../index.js";
2
-
3
- //#region src/cli/pack/manifest.ts
4
- function buildPackManifest(input) {
5
- return {
6
- kitVersion: KIT_VERSION,
7
- template: input.template,
8
- contentHash: input.contentHash,
9
- packedAt: input.packedAt,
10
- check: { ok: true }
11
- };
12
- }
13
-
14
- //#endregion
15
- export { buildPackManifest };
@@ -1,86 +0,0 @@
1
- import { deflateRawSync } from "node:zlib";
2
-
3
- //#region src/cli/pack/zip.ts
4
- const DOS_DATE = 33;
5
- const DOS_TIME = 0;
6
- const CRC_TABLE = (() => {
7
- const table = /* @__PURE__ */ new Uint32Array(256);
8
- for (let n = 0; n < 256; n++) {
9
- let c = n;
10
- for (let k = 0; k < 8; k++) c = (c & 1) !== 0 ? 3988292384 ^ c >>> 1 : c >>> 1;
11
- table[n] = c >>> 0;
12
- }
13
- return table;
14
- })();
15
- function crc32(bytes) {
16
- let c = 4294967295;
17
- for (const byte of bytes) c = CRC_TABLE[(c ^ byte) & 255] ^ c >>> 8;
18
- return (c ^ 4294967295) >>> 0;
19
- }
20
- /** A standard DEFLATE zip built from `entries`, in order. No zip64 — pack inputs
21
- * are source files, far under the 4 GiB per-entry limit. */
22
- function buildZip(entries) {
23
- const locals = [];
24
- const centrals = [];
25
- let offset = 0;
26
- for (const entry of entries) {
27
- const name = Buffer.from(entry.path, "utf8");
28
- const source = Buffer.from(entry.bytes);
29
- const crc = crc32(source);
30
- const compressed = deflateRawSync(source);
31
- const local = Buffer.alloc(30 + name.length);
32
- local.writeUInt32LE(67324752, 0);
33
- local.writeUInt16LE(20, 4);
34
- local.writeUInt16LE(2048, 6);
35
- local.writeUInt16LE(8, 8);
36
- local.writeUInt16LE(DOS_TIME, 10);
37
- local.writeUInt16LE(DOS_DATE, 12);
38
- local.writeUInt32LE(crc, 14);
39
- local.writeUInt32LE(compressed.length, 18);
40
- local.writeUInt32LE(source.length, 22);
41
- local.writeUInt16LE(name.length, 26);
42
- local.writeUInt16LE(0, 28);
43
- name.copy(local, 30);
44
- locals.push(local, compressed);
45
- const central = Buffer.alloc(46 + name.length);
46
- central.writeUInt32LE(33639248, 0);
47
- central.writeUInt16LE(788, 4);
48
- central.writeUInt16LE(20, 6);
49
- central.writeUInt16LE(2048, 8);
50
- central.writeUInt16LE(8, 10);
51
- central.writeUInt16LE(DOS_TIME, 12);
52
- central.writeUInt16LE(DOS_DATE, 14);
53
- central.writeUInt32LE(crc, 16);
54
- central.writeUInt32LE(compressed.length, 20);
55
- central.writeUInt32LE(source.length, 24);
56
- central.writeUInt16LE(name.length, 28);
57
- central.writeUInt16LE(0, 30);
58
- central.writeUInt16LE(0, 32);
59
- central.writeUInt16LE(0, 34);
60
- central.writeUInt16LE(0, 36);
61
- central.writeUInt32LE(33188 << 16 >>> 0, 38);
62
- central.writeUInt32LE(offset, 42);
63
- name.copy(central, 46);
64
- centrals.push(central);
65
- offset += local.length + compressed.length;
66
- }
67
- const localData = Buffer.concat(locals);
68
- const centralDir = Buffer.concat(centrals);
69
- const eocd = Buffer.alloc(22);
70
- eocd.writeUInt32LE(101010256, 0);
71
- eocd.writeUInt16LE(0, 4);
72
- eocd.writeUInt16LE(0, 6);
73
- eocd.writeUInt16LE(entries.length, 8);
74
- eocd.writeUInt16LE(entries.length, 10);
75
- eocd.writeUInt32LE(centralDir.length, 12);
76
- eocd.writeUInt32LE(localData.length, 16);
77
- eocd.writeUInt16LE(0, 20);
78
- return Buffer.concat([
79
- localData,
80
- centralDir,
81
- eocd
82
- ]);
83
- }
84
-
85
- //#endregion
86
- export { buildZip };
@@ -1,43 +0,0 @@
1
- import { TokenThemeSchema, compileThemeToCss } from "../../design-system/theme.js";
2
- import { loadTheme } from "./load-theme.js";
3
- import { writeFile } from "node:fs/promises";
4
- import { join } from "node:path";
5
-
6
- //#region src/cli/theme/generate.ts
7
- const OUT_FILE = "theme.css";
8
- const HEADER = "/* Generated by `template-kit theme` from theme.ts — do not edit.\n Re-run `template-kit theme` after changing theme.ts. */\n";
9
- /** theme.ts's `theme` export failed TokenThemeSchema; `issues` are `path: message` lines. */
10
- var InvalidTheme = class extends Error {
11
- path;
12
- issues;
13
- constructor(path, issues) {
14
- super(`${path} is not a valid TokenTheme:\n${issues.map((i) => ` - ${i}`).join("\n")}`);
15
- this.path = path;
16
- this.issues = issues;
17
- this.name = "InvalidTheme";
18
- }
19
- };
20
- /**
21
- * Compile `templates/<key>/theme.ts` to `templates/<key>/theme.css`. Throws
22
- * MissingThemeFile / MissingThemeExport (load-theme.ts) or InvalidTheme.
23
- */
24
- async function generateTemplateTheme(root, template) {
25
- const raw = await loadTheme(root, template);
26
- const parsed = TokenThemeSchema.safeParse(raw);
27
- if (!parsed.success) {
28
- const issues = parsed.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`);
29
- throw new InvalidTheme(join("templates", template.key, "theme.ts"), issues);
30
- }
31
- const theme = parsed.data;
32
- const outPath = join(template.dir, OUT_FILE);
33
- await writeFile(outPath, `${HEADER}${compileThemeToCss(theme)}`, "utf8");
34
- const tokenCount = Object.keys(theme.colors).length + Object.keys(theme.fonts).length + Object.keys(theme.type).length + Object.keys(theme.radii).length + Object.keys(theme.shadows).length + Object.keys(theme.layout).length;
35
- return {
36
- outPath,
37
- relPath: join("templates", template.key, OUT_FILE),
38
- tokenCount
39
- };
40
- }
41
-
42
- //#endregion
43
- export { InvalidTheme, generateTemplateTheme };
@@ -1,33 +0,0 @@
1
- import { findWorkspaceRoot, selectTemplates, templateFromCwd } from "../check/workspace.js";
2
- import { generateTemplateTheme } from "./generate.js";
3
- import { parseArgs } from "node:util";
4
-
5
- //#region src/cli/theme/index.ts
6
- async function runTheme(argv, cwd) {
7
- const { values, positionals } = parseArgs({
8
- args: argv,
9
- options: { all: {
10
- type: "boolean",
11
- default: false
12
- } },
13
- allowPositionals: true
14
- });
15
- const root = await findWorkspaceRoot(cwd);
16
- const selected = await selectTemplates(root, positionals[0] ?? (values.all ? void 0 : templateFromCwd(root, cwd)), values.all, "theme");
17
- if (selected.length === 0) return {
18
- code: 1,
19
- output: "No template to generate theme CSS for: templates/ holds no template folder."
20
- };
21
- const lines = [];
22
- for (const template of selected) {
23
- const { relPath, tokenCount } = await generateTemplateTheme(root, template);
24
- lines.push(` ${template.key} wrote ${relPath} (${tokenCount} tokens)`);
25
- }
26
- return {
27
- code: 0,
28
- output: `template-kit theme\n${lines.join("\n")}`
29
- };
30
- }
31
-
32
- //#endregion
33
- export { runTheme };
@@ -1,69 +0,0 @@
1
- import { loadEsbuild } from "../check/resolve-tool.js";
2
- import { mkdir, readFile, rm, stat } from "node:fs/promises";
3
- import { join } from "node:path";
4
- import { createHash } from "node:crypto";
5
- import { pathToFileURL } from "node:url";
6
-
7
- //#region src/cli/theme/load-theme.ts
8
- const THEME_FILE = "theme.ts";
9
- const THEME_EXPORT = "theme";
10
- /** A template has no theme.ts. `path` is workspace-relative. */
11
- var MissingThemeFile = class extends Error {
12
- path;
13
- constructor(path) {
14
- super(`${path} not found. A template's design tokens live in theme.ts — create it exporting \`${THEME_EXPORT}\` (a TokenTheme).`);
15
- this.path = path;
16
- this.name = "MissingThemeFile";
17
- }
18
- };
19
- /** theme.ts built, but exports no `theme` binding. `path` is workspace-relative. */
20
- var MissingThemeExport = class extends Error {
21
- path;
22
- constructor(path) {
23
- super(`${path} does not export \`${THEME_EXPORT}\`. It must \`export const ${THEME_EXPORT}: TokenTheme = { … }\`.`);
24
- this.path = path;
25
- this.name = "MissingThemeExport";
26
- }
27
- };
28
- const isFile = async (p) => {
29
- try {
30
- return (await stat(p)).isFile();
31
- } catch {
32
- return false;
33
- }
34
- };
35
- /**
36
- * Transpile + evaluate `templates/<key>/theme.ts` and return its `theme` export,
37
- * unvalidated (the caller parses it with TokenThemeSchema). Throws MissingThemeFile
38
- * when theme.ts is absent and MissingThemeExport when it builds without a `theme`
39
- * binding.
40
- */
41
- async function loadTheme(root, template) {
42
- const source = join(template.dir, THEME_FILE);
43
- const rel = join("templates", template.key, THEME_FILE);
44
- if (!await isFile(source)) throw new MissingThemeFile(rel);
45
- const outdir = join(root, "node_modules", ".template-kit", "theme", createHash("sha256").update(template.dir).digest("hex").slice(0, 12));
46
- await rm(outdir, {
47
- recursive: true,
48
- force: true
49
- });
50
- await mkdir(outdir, { recursive: true });
51
- const { build } = await loadEsbuild(root);
52
- await build({
53
- entryPoints: [source],
54
- outdir,
55
- bundle: true,
56
- format: "esm",
57
- platform: "neutral",
58
- packages: "external",
59
- logLevel: "silent",
60
- absWorkingDir: root
61
- });
62
- const version = createHash("sha256").update(await readFile(source)).digest("hex").slice(0, 12);
63
- const value = (await import(`${pathToFileURL(join(outdir, "theme.js")).href}?v=${version}`))[THEME_EXPORT];
64
- if (value === void 0) throw new MissingThemeExport(rel);
65
- return value;
66
- }
67
-
68
- //#endregion
69
- export { MissingThemeExport, MissingThemeFile, loadTheme };
package/dist/cli.d.ts DELETED
@@ -1 +0,0 @@
1
- export { };