@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.
- package/CHANGELOG.md +29 -59
- package/README.md +14 -4
- package/dist/contracts/fill-treatments.d.ts +23 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/islands/discover.js +1 -1
- package/dist/islands/esbuild-plugin.js +1 -1
- package/dist/islands/wrap.d.ts +9 -1
- package/dist/media/resolve-fixture-media.d.ts +22 -0
- package/dist/package.js +1 -1
- package/dist/schema/resolve-section-ref.d.ts +4 -0
- package/dist/schema/rows.d.ts +9 -0
- package/dist/schema/section-nav.d.ts +5 -1
- package/dist/schema/slot-types.d.ts +1 -1
- package/dist/ssr.d.ts +7 -2
- package/dist/ssr.js +6 -2
- package/guide/INDEX.md +5 -0
- package/guide/check.md +1 -0
- package/guide/dev.md +1 -0
- package/guide/llms.txt +11 -3
- package/guide/upgrading.md +12 -8
- package/package.json +4 -16
- package/dist/canvas-core/chrome-css.d.ts +0 -24
- package/dist/canvas-core/chrome-css.js +0 -403
- package/dist/canvas-core/chrome.d.ts +0 -49
- package/dist/canvas-core/chrome.js +0 -147
- package/dist/canvas-core/frame.d.ts +0 -20
- package/dist/canvas-core/frame.js +0 -23
- package/dist/canvas-core/hidden-tabs.d.ts +0 -16
- package/dist/canvas-core/hidden-tabs.js +0 -84
- package/dist/canvas-core/resolve-target.js +0 -55
- package/dist/canvas-core/scroll.d.ts +0 -10
- package/dist/canvas-core/scroll.js +0 -15
- package/dist/canvas-core/section-dom.d.ts +0 -6
- package/dist/canvas-core/section-dom.js +0 -43
- package/dist/canvas-core/srcdoc.d.ts +0 -8
- package/dist/canvas-core/srcdoc.js +0 -77
- package/dist/canvas-core/types.d.ts +0 -76
- package/dist/canvas-core.d.ts +0 -9
- package/dist/canvas-core.js +0 -9
- package/dist/cli/check/config.js +0 -41
- package/dist/cli/check/css.js +0 -131
- package/dist/cli/check/diagnostics.js +0 -37
- package/dist/cli/check/index.js +0 -94
- package/dist/cli/check/loader.js +0 -149
- package/dist/cli/check/paths.js +0 -15
- package/dist/cli/check/relativize.js +0 -18
- package/dist/cli/check/render-invariants.js +0 -24
- package/dist/cli/check/resolve-tool.js +0 -38
- package/dist/cli/check/section-assets.js +0 -58
- package/dist/cli/check/stages/deps.js +0 -337
- package/dist/cli/check/stages/lint.js +0 -46
- package/dist/cli/check/stages/render.js +0 -98
- package/dist/cli/check/stages/size.js +0 -274
- package/dist/cli/check/stages/tree.js +0 -206
- package/dist/cli/check/stages/typecheck.js +0 -46
- package/dist/cli/check/stages/validate/catalog-exhaustiveness.js +0 -18
- package/dist/cli/check/stages/validate/facts.js +0 -19
- package/dist/cli/check/stages/validate/formatter-contract.js +0 -14
- package/dist/cli/check/stages/validate/group-contract.js +0 -21
- package/dist/cli/check/stages/validate/index.js +0 -66
- package/dist/cli/check/stages/validate/list-scalar-contract.js +0 -22
- package/dist/cli/check/stages/validate/nav-contract.js +0 -93
- package/dist/cli/check/stages/validate/nested-slot-contract.js +0 -12
- package/dist/cli/check/stages/validate/object-list-contract.js +0 -31
- package/dist/cli/check/stages/validate/orchestrator.js +0 -241
- package/dist/cli/check/stages/validate/produced-by-contract.js +0 -18
- package/dist/cli/check/stages/validate/row-contract.js +0 -31
- package/dist/cli/check/stages/validate/select-contract.js +0 -13
- package/dist/cli/check/stages/validate/source-contract.js +0 -21
- package/dist/cli/check/stages/validate/variant-contract.js +0 -15
- package/dist/cli/check/workspace.js +0 -99
- package/dist/cli/dev/build-css.js +0 -97
- package/dist/cli/dev/catalog.js +0 -35
- package/dist/cli/dev/compose-template.js +0 -39
- package/dist/cli/dev/content-override.js +0 -58
- package/dist/cli/dev/diagnostics.js +0 -68
- package/dist/cli/dev/discover.js +0 -35
- package/dist/cli/dev/fill-state.js +0 -77
- package/dist/cli/dev/index.js +0 -42
- package/dist/cli/dev/inspect.js +0 -38
- package/dist/cli/dev/island-bootstrap.js +0 -38
- package/dist/cli/dev/island-canvas-bootstrap.js +0 -204
- package/dist/cli/dev/island-map.js +0 -35
- package/dist/cli/dev/island-transform.js +0 -31
- package/dist/cli/dev/manifest-instances.js +0 -31
- package/dist/cli/dev/render-section.js +0 -24
- package/dist/cli/dev/screenshot-target.js +0 -24
- package/dist/cli/dev/section-page.js +0 -91
- package/dist/cli/dev/server.js +0 -511
- package/dist/cli/dev/slot-schema.js +0 -12
- package/dist/cli/dev/structure-summary.js +0 -119
- package/dist/cli/dev/tailwind.js +0 -32
- package/dist/cli/dev/tsconfig-paths-plugin.js +0 -90
- package/dist/cli/dev/vite-server.js +0 -39
- package/dist/cli/dev/workspace.js +0 -63
- package/dist/cli/link/index.js +0 -74
- package/dist/cli/link/overlay.js +0 -59
- package/dist/cli/link/watch.js +0 -25
- package/dist/cli/new/emit.js +0 -48
- package/dist/cli/new/index.js +0 -69
- package/dist/cli/new/scaffold/section/_Renderer.tsx +0 -20
- package/dist/cli/new/scaffold/section/_fill-spec.ts +0 -18
- package/dist/cli/new/scaffold/section/_fixtures.ts +0 -13
- package/dist/cli/new/scaffold/section/_schema.ts +0 -24
- package/dist/cli/new/scaffold/template/_manifest.json +0 -9
- package/dist/cli/new/scaffold/template/sections/hero/ExpandableText.tsx +0 -45
- package/dist/cli/new/scaffold/template/sections/hero/Renderer.tsx +0 -40
- package/dist/cli/new/scaffold/template/sections/hero/components/Feature.tsx +0 -14
- package/dist/cli/new/scaffold/template/sections/hero/fill-spec.ts +0 -29
- package/dist/cli/new/scaffold/template/sections/hero/fixtures.ts +0 -41
- package/dist/cli/new/scaffold/template/sections/hero/schema.ts +0 -60
- package/dist/cli/new/scaffold/template/theme.css +0 -24
- package/dist/cli/new/scaffold/template/theme.ts +0 -27
- package/dist/cli/new/scaffold-assets.js +0 -20
- package/dist/cli/pack/collect.js +0 -44
- package/dist/cli/pack/guards.js +0 -57
- package/dist/cli/pack/index.js +0 -66
- package/dist/cli/pack/manifest.js +0 -15
- package/dist/cli/pack/zip.js +0 -86
- package/dist/cli/theme/generate.js +0 -43
- package/dist/cli/theme/index.js +0 -33
- package/dist/cli/theme/load-theme.js +0 -69
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +0 -138
- package/dist/dev-client/assets/index-CJa9G1Na.css +0 -1
- package/dist/dev-client/assets/index-C_1atZs4.js +0 -450
- package/dist/dev-client/index.html +0 -13
- 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 };
|
package/dist/cli/dev/catalog.js
DELETED
|
@@ -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, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
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 };
|
package/dist/cli/dev/discover.js
DELETED
|
@@ -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 };
|
package/dist/cli/dev/index.js
DELETED
|
@@ -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 };
|
package/dist/cli/dev/inspect.js
DELETED
|
@@ -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 };
|