@homepages/template-kit 0.3.0 → 0.4.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 (111) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +39 -4
  3. package/dist/asset-modules.d.ts +81 -0
  4. package/dist/cli/check/config.js +5 -1
  5. package/dist/cli/check/css.js +27 -13
  6. package/dist/cli/check/loader.js +4 -0
  7. package/dist/cli/check/section-assets.js +58 -0
  8. package/dist/cli/check/stages/size.js +124 -8
  9. package/dist/cli/check/stages/tree.js +26 -27
  10. package/dist/cli/dev/build-css.js +91 -0
  11. package/dist/cli/dev/catalog.js +35 -0
  12. package/dist/cli/dev/compose-template.js +38 -0
  13. package/dist/cli/dev/content-override.js +58 -0
  14. package/dist/cli/dev/discover.js +35 -0
  15. package/dist/cli/dev/fill-state.js +75 -0
  16. package/dist/cli/dev/index.js +32 -0
  17. package/dist/cli/dev/inspect.js +38 -0
  18. package/dist/cli/dev/island-bootstrap.js +38 -0
  19. package/dist/cli/dev/island-map.js +35 -0
  20. package/dist/cli/dev/island-transform.js +31 -0
  21. package/dist/cli/dev/manifest-instances.js +31 -0
  22. package/dist/cli/dev/render-section.js +24 -0
  23. package/dist/cli/dev/screenshot-target.js +24 -0
  24. package/dist/cli/dev/section-page.js +54 -0
  25. package/dist/cli/dev/server.js +480 -0
  26. package/dist/cli/dev/slot-schema.js +12 -0
  27. package/dist/cli/dev/structure-summary.js +118 -0
  28. package/dist/cli/dev/tailwind.js +32 -0
  29. package/dist/cli/dev/vite-server.js +33 -0
  30. package/dist/cli/dev/workspace.js +63 -0
  31. package/dist/cli/link/index.js +74 -0
  32. package/dist/cli/link/overlay.js +59 -0
  33. package/dist/cli/link/watch.js +25 -0
  34. package/dist/cli/new/emit.js +48 -0
  35. package/dist/cli/new/index.js +69 -0
  36. package/dist/cli/new/scaffold/section/_Renderer.tsx +20 -0
  37. package/dist/cli/new/scaffold/section/_fill-spec.ts +18 -0
  38. package/dist/cli/new/scaffold/section/_fixtures.ts +13 -0
  39. package/dist/cli/new/scaffold/section/_schema.ts +24 -0
  40. package/dist/cli/new/scaffold/template/_manifest.json +9 -0
  41. package/dist/cli/new/scaffold/template/sections/hero/ExpandableText.tsx +45 -0
  42. package/dist/cli/new/scaffold/template/sections/hero/Renderer.tsx +40 -0
  43. package/dist/cli/new/scaffold/template/sections/hero/components/Feature.tsx +14 -0
  44. package/dist/cli/new/scaffold/template/sections/hero/fill-spec.ts +29 -0
  45. package/dist/cli/new/scaffold/template/sections/hero/fixtures.ts +41 -0
  46. package/dist/cli/new/scaffold/template/sections/hero/schema.ts +60 -0
  47. package/dist/cli/new/scaffold/template/theme.css +24 -0
  48. package/dist/cli/new/scaffold/template/theme.ts +27 -0
  49. package/dist/cli/new/scaffold-assets.js +20 -0
  50. package/dist/cli/pack/collect.js +44 -0
  51. package/dist/cli/pack/guards.js +57 -0
  52. package/dist/cli/pack/index.js +66 -0
  53. package/dist/cli/pack/manifest.js +15 -0
  54. package/dist/cli/pack/zip.js +86 -0
  55. package/dist/cli.js +80 -14
  56. package/dist/dev-client/assets/index-DfEfrp6P.css +1 -0
  57. package/dist/dev-client/assets/index-bpMP0b0W.js +50 -0
  58. package/dist/dev-client/index.html +13 -0
  59. package/dist/eslint/is-client-file.d.ts +4 -0
  60. package/dist/index.d.ts +1 -0
  61. package/dist/islands/detect.d.ts +16 -0
  62. package/dist/islands/detect.js +22 -0
  63. package/dist/islands/discover.d.ts +5 -0
  64. package/dist/islands/discover.js +19 -0
  65. package/dist/islands/esbuild-plugin.d.ts +13 -0
  66. package/dist/islands/esbuild-plugin.js +39 -0
  67. package/dist/islands/wrap.d.ts +11 -0
  68. package/dist/islands/wrap.js +48 -0
  69. package/dist/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js +77 -0
  70. package/dist/node_modules/magic-string/dist/magic-string.es.js +939 -0
  71. package/dist/package.js +1 -1
  72. package/dist/ssr.d.ts +31 -0
  73. package/dist/ssr.js +46 -0
  74. package/docs/INDEX.md +9 -1
  75. package/docs/assets.md +128 -0
  76. package/docs/check.md +6 -3
  77. package/docs/dev.md +187 -0
  78. package/docs/islands.md +5 -2
  79. package/docs/llms.txt +41 -3
  80. package/docs/new.md +27 -0
  81. package/docs/overview.md +57 -0
  82. package/docs/pack.md +46 -0
  83. package/docs/primitives.md +5 -6
  84. package/docs/quickstart.md +102 -0
  85. package/docs/recipes/INDEX.md +27 -0
  86. package/docs/recipes/bind-property-fact.md +58 -0
  87. package/docs/recipes/collection-slot.md +100 -0
  88. package/docs/recipes/fill-spec-decision.md +66 -0
  89. package/docs/recipes/fixture-states.md +68 -0
  90. package/docs/recipes/image-slot-crop.md +98 -0
  91. package/docs/recipes/interactive-island.md +100 -0
  92. package/docs/recipes/organize-section-folder.md +72 -0
  93. package/docs/recipes/prepare-submission.md +55 -0
  94. package/docs/recipes/second-template.md +49 -0
  95. package/docs/recipes/select-slot.md +59 -0
  96. package/docs/recipes/static-asset.md +103 -0
  97. package/docs/recipes/third-party-package.md +90 -0
  98. package/docs/rules/INDEX.md +4 -2
  99. package/docs/rules/bundle-binary-asset.md +33 -52
  100. package/docs/rules/bundle-incomplete.md +2 -2
  101. package/docs/rules/css-reason.md +3 -3
  102. package/docs/rules/no-bare-css-import.md +8 -8
  103. package/docs/rules/no-css-import-from-render-path.md +7 -8
  104. package/docs/rules/size-assets.md +88 -0
  105. package/docs/rules/size-island-bundle.md +123 -0
  106. package/docs/rules/size-section-css.md +19 -14
  107. package/docs/schema-system.md +28 -28
  108. package/docs/theme-and-css.md +2 -2
  109. package/docs/vocabulary.md +98 -0
  110. package/package.json +11 -3
  111. package/tsconfig.json +1 -1
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>template-kit dev</title>
7
+ <script type="module" crossorigin src="/dev-client/assets/index-bpMP0b0W.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/dev-client/assets/index-DfEfrp6P.css">
9
+ </head>
10
+ <body>
11
+ <div id="root"></div>
12
+ </body>
13
+ </html>
@@ -0,0 +1,4 @@
1
+ //#region src/eslint/is-client-file.d.ts
2
+ declare function hasClientDirective(text: string): boolean;
3
+ //#endregion
4
+ export { hasClientDirective };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { ResponsiveImage, ResponsiveSource } from "./contracts/image-descriptor.js";
2
2
  import { Coordinates, FloorplanAssignment, PropertyFacts, UnitRow } from "./schema/property-facts.js";
3
+ import "./asset-modules.js";
3
4
  import { COLLECTIONS, CONTENT_SLOT_TYPES, CollectionField, CollectionIdKey, CollectionOwnership, ContentSlotType, DECISION_TYPES, DERIVED_INPUT_FIELDS, DecisionType, DerivedInputName, FORMATTER_NAMES, FormatterName, REFERRAL_FIELDS, REFERRAL_IMAGE_FIELDS, REF_SLOT_TYPES, RefSlotType, ReferralField, ReferralImageField, SLOT_TYPES, SOURCE_FIELDS, SlotType, SourceFieldContainer, SourceFieldDescriptor, SourceFieldName, SourceFieldValueType, WritebackTarget, collectionForSlot } from "./contracts/slot-catalog.js";
4
5
  import { AddressParts, TRANSFORMS, TRANSFORM_NAMES, TransformDescriptor, TransformFn, TransformInput, TransformName, TransformUnit } from "./contracts/transforms.js";
5
6
  import { VariantSelectorFn, VariantsMeta, makeResolveVariants } from "./schema/resolve-variants.js";
@@ -0,0 +1,16 @@
1
+ import { hasClientDirective } from "../eslint/is-client-file.js";
2
+ //#region src/islands/detect.d.ts
3
+ /**
4
+ * True iff `file` is a client component — an on-disk source file whose directive
5
+ * prologue is `"use client"`. Returns false for an unreadable or absent path.
6
+ */
7
+ declare function isClientComponentFile(file: string): boolean;
8
+ /**
9
+ * Compose an island key: `<sectionKey>:<componentName>` (e.g.
10
+ * `spw-essential/floorplan:UnitFilter`). The key is what a marker in the DOM
11
+ * carries and what the browser loader resolves the client module by, so the
12
+ * emitter and the loader must derive it identically.
13
+ */
14
+ declare function islandKey(sectionKey: string, componentName: string): string;
15
+ //#endregion
16
+ export { hasClientDirective, isClientComponentFile, islandKey };
@@ -0,0 +1,22 @@
1
+ import { hasClientDirective, isClientFile } from "../eslint/is-client-file.js";
2
+
3
+ //#region src/islands/detect.ts
4
+ /**
5
+ * True iff `file` is a client component — an on-disk source file whose directive
6
+ * prologue is `"use client"`. Returns false for an unreadable or absent path.
7
+ */
8
+ function isClientComponentFile(file) {
9
+ return isClientFile(file);
10
+ }
11
+ /**
12
+ * Compose an island key: `<sectionKey>:<componentName>` (e.g.
13
+ * `spw-essential/floorplan:UnitFilter`). The key is what a marker in the DOM
14
+ * carries and what the browser loader resolves the client module by, so the
15
+ * emitter and the loader must derive it identically.
16
+ */
17
+ function islandKey(sectionKey, componentName) {
18
+ return `${sectionKey}:${componentName}`;
19
+ }
20
+
21
+ //#endregion
22
+ export { hasClientDirective, isClientComponentFile, islandKey };
@@ -0,0 +1,5 @@
1
+ //#region src/islands/discover.d.ts
2
+ /** Every `"use client"` component file anywhere under a section directory (recursive). */
3
+ declare function findSectionIslands(sectionDir: string): Promise<string[]>;
4
+ //#endregion
5
+ export { findSectionIslands };
@@ -0,0 +1,19 @@
1
+ import { isClientComponentFile } from "./detect.js";
2
+ import { extname, join } from "node:path";
3
+ import { readdir } from "node:fs/promises";
4
+
5
+ //#region src/islands/discover.ts
6
+ const COMPONENT_EXTS = /* @__PURE__ */ new Set([".tsx", ".jsx"]);
7
+ /** Every `"use client"` component file anywhere under a section directory (recursive). */
8
+ async function findSectionIslands(sectionDir) {
9
+ const out = [];
10
+ for (const entry of await readdir(sectionDir, { withFileTypes: true })) {
11
+ const full = join(sectionDir, entry.name);
12
+ if (entry.isDirectory()) out.push(...await findSectionIslands(full));
13
+ else if (COMPONENT_EXTS.has(extname(entry.name)) && isClientComponentFile(full)) out.push(full);
14
+ }
15
+ return out;
16
+ }
17
+
18
+ //#endregion
19
+ export { findSectionIslands };
@@ -0,0 +1,13 @@
1
+ import { Plugin } from "esbuild";
2
+ //#region src/islands/esbuild-plugin.d.ts
3
+ /**
4
+ * Wrap every `"use client"` `.tsx`/`.jsx` in the build graph so it renders through
5
+ * `IslandMarker`, keyed `islandKey(sectionKey, <file-basename>)`. `sectionKey` is
6
+ * fixed per bundle (one section per renderer build). Non-client modules and other
7
+ * extensions fall through to esbuild untouched.
8
+ */
9
+ declare function islandTransformEsbuildPlugin(opts: {
10
+ sectionKey: string;
11
+ }): Plugin;
12
+ //#endregion
13
+ export { islandTransformEsbuildPlugin };
@@ -0,0 +1,39 @@
1
+ import { islandKey } from "./detect.js";
2
+ import { wrapClientModuleSource } from "./wrap.js";
3
+ import { basename, extname } from "node:path";
4
+ import { readFile } from "node:fs/promises";
5
+
6
+ //#region src/islands/esbuild-plugin.ts
7
+ const LOADER = {
8
+ ".tsx": "tsx",
9
+ ".jsx": "jsx"
10
+ };
11
+ /**
12
+ * Wrap every `"use client"` `.tsx`/`.jsx` in the build graph so it renders through
13
+ * `IslandMarker`, keyed `islandKey(sectionKey, <file-basename>)`. `sectionKey` is
14
+ * fixed per bundle (one section per renderer build). Non-client modules and other
15
+ * extensions fall through to esbuild untouched.
16
+ */
17
+ function islandTransformEsbuildPlugin(opts) {
18
+ return {
19
+ name: "template-kit:island-transform-esbuild",
20
+ setup(b) {
21
+ b.onLoad({ filter: /\.(tsx|jsx)$/ }, async (args) => {
22
+ const code = await readFile(args.path, "utf8");
23
+ const wrapped = wrapClientModuleSource(code, islandKey(opts.sectionKey, basename(args.path, extname(args.path))));
24
+ const ext = extname(args.path);
25
+ if (wrapped === null) return {
26
+ contents: code,
27
+ loader: LOADER[ext]
28
+ };
29
+ return {
30
+ contents: wrapped,
31
+ loader: LOADER[ext]
32
+ };
33
+ });
34
+ }
35
+ };
36
+ }
37
+
38
+ //#endregion
39
+ export { islandTransformEsbuildPlugin };
@@ -0,0 +1,11 @@
1
+ //#region src/islands/wrap.d.ts
2
+ /**
3
+ * Rewrite a client module's source so its default export renders through
4
+ * `IslandMarker` with `islandKey`. Returns null when `code` is not a client
5
+ * component (`hasClientDirective` false) or has no default export — the caller
6
+ * then leaves the module untouched. The rebound original is `__trIslandReal`;
7
+ * it still flows through the consumer's JSX/TS transform unchanged.
8
+ */
9
+ declare function wrapClientModuleSource(code: string, islandKey: string): string | null;
10
+ //#endregion
11
+ export { wrapClientModuleSource };
@@ -0,0 +1,48 @@
1
+ import { hasClientDirective } from "../eslint/is-client-file.js";
2
+ import MagicString from "../node_modules/magic-string/dist/magic-string.es.js";
3
+
4
+ //#region src/islands/wrap.ts
5
+ const DEFAULT_EXPORT = /export\s+default\s+/;
6
+ /**
7
+ * The codemod as a `MagicString`, so a caller (the Vite plugin) can emit a
8
+ * hi-res sourcemap alongside the rewritten code. Returns null on the same terms
9
+ * as `wrapClientModuleSource` — a non-client module or one with no default
10
+ * export. The rebound original is `__trIslandReal`; it still flows through the
11
+ * consumer's JSX/TS transform unchanged.
12
+ */
13
+ function wrapClientModuleMagic(code, islandKey) {
14
+ if (!hasClientDirective(code)) return null;
15
+ if (!DEFAULT_EXPORT.test(code)) return null;
16
+ const s = new MagicString(code);
17
+ s.replace(DEFAULT_EXPORT, "const __trIslandReal = ");
18
+ s.append(wrapperSource(islandKey));
19
+ return s;
20
+ }
21
+ /**
22
+ * Rewrite a client module's source so its default export renders through
23
+ * `IslandMarker` with `islandKey`. Returns null when `code` is not a client
24
+ * component (`hasClientDirective` false) or has no default export — the caller
25
+ * then leaves the module untouched. The rebound original is `__trIslandReal`;
26
+ * it still flows through the consumer's JSX/TS transform unchanged.
27
+ */
28
+ function wrapClientModuleSource(code, islandKey) {
29
+ return wrapClientModuleMagic(code, islandKey)?.toString() ?? null;
30
+ }
31
+ function wrapperSource(islandKey) {
32
+ return `
33
+ ;
34
+ import { createElement as __trCreateElement, useId as __trUseId } from "react";
35
+ import { IslandMarker as __TrIslandMarker } from "@homepages/template-kit";
36
+ export default function __trIslandWrapper(__trProps) {
37
+ const __trIslandId = __trUseId();
38
+ return __trCreateElement(
39
+ __TrIslandMarker,
40
+ { islandKey: ${JSON.stringify(islandKey)}, islandId: __trIslandId, props: __trProps },
41
+ __trCreateElement(__trIslandReal, __trProps),
42
+ );
43
+ }
44
+ `;
45
+ }
46
+
47
+ //#endregion
48
+ export { wrapClientModuleMagic, wrapClientModuleSource };
@@ -0,0 +1,77 @@
1
+ //#region node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs
2
+ var comma = ",".charCodeAt(0);
3
+ var semicolon = ";".charCodeAt(0);
4
+ var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
5
+ var intToChar = /* @__PURE__ */ new Uint8Array(64);
6
+ var charToInt = /* @__PURE__ */ new Uint8Array(128);
7
+ for (let i = 0; i < chars.length; i++) {
8
+ const c = chars.charCodeAt(i);
9
+ intToChar[i] = c;
10
+ charToInt[c] = i;
11
+ }
12
+ function encodeInteger(builder, num, relative) {
13
+ let delta = num - relative;
14
+ delta = delta < 0 ? -delta << 1 | 1 : delta << 1;
15
+ do {
16
+ let clamped = delta & 31;
17
+ delta >>>= 5;
18
+ if (delta > 0) clamped |= 32;
19
+ builder.write(intToChar[clamped]);
20
+ } while (delta > 0);
21
+ return num;
22
+ }
23
+ var bufLength = 1024 * 16;
24
+ var td = typeof TextDecoder !== "undefined" ? /* @__PURE__ */ new TextDecoder() : typeof Buffer !== "undefined" ? { decode(buf) {
25
+ return Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength).toString();
26
+ } } : { decode(buf) {
27
+ let out = "";
28
+ for (let i = 0; i < buf.length; i++) out += String.fromCharCode(buf[i]);
29
+ return out;
30
+ } };
31
+ var StringWriter = class {
32
+ constructor() {
33
+ this.pos = 0;
34
+ this.out = "";
35
+ this.buffer = new Uint8Array(bufLength);
36
+ }
37
+ write(v) {
38
+ const { buffer } = this;
39
+ buffer[this.pos++] = v;
40
+ if (this.pos === bufLength) {
41
+ this.out += td.decode(buffer);
42
+ this.pos = 0;
43
+ }
44
+ }
45
+ flush() {
46
+ const { buffer, out, pos } = this;
47
+ return pos > 0 ? out + td.decode(buffer.subarray(0, pos)) : out;
48
+ }
49
+ };
50
+ function encode(decoded) {
51
+ const writer = new StringWriter();
52
+ let sourcesIndex = 0;
53
+ let sourceLine = 0;
54
+ let sourceColumn = 0;
55
+ let namesIndex = 0;
56
+ for (let i = 0; i < decoded.length; i++) {
57
+ const line = decoded[i];
58
+ if (i > 0) writer.write(semicolon);
59
+ if (line.length === 0) continue;
60
+ let genColumn = 0;
61
+ for (let j = 0; j < line.length; j++) {
62
+ const segment = line[j];
63
+ if (j > 0) writer.write(comma);
64
+ genColumn = encodeInteger(writer, segment[0], genColumn);
65
+ if (segment.length === 1) continue;
66
+ sourcesIndex = encodeInteger(writer, segment[1], sourcesIndex);
67
+ sourceLine = encodeInteger(writer, segment[2], sourceLine);
68
+ sourceColumn = encodeInteger(writer, segment[3], sourceColumn);
69
+ if (segment.length === 4) continue;
70
+ namesIndex = encodeInteger(writer, segment[4], namesIndex);
71
+ }
72
+ }
73
+ return writer.flush();
74
+ }
75
+
76
+ //#endregion
77
+ export { encode };