@homepages/template-kit 0.2.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 (165) hide show
  1. package/CHANGELOG.md +222 -0
  2. package/README.md +96 -17
  3. package/dist/asset-modules.d.ts +81 -0
  4. package/dist/base.css +9 -0
  5. package/dist/cli/check/config.js +41 -0
  6. package/dist/cli/check/css.js +131 -0
  7. package/dist/cli/check/diagnostics.js +32 -0
  8. package/dist/cli/check/index.js +87 -0
  9. package/dist/cli/check/loader.js +146 -0
  10. package/dist/cli/check/paths.js +15 -0
  11. package/dist/cli/check/relativize.js +18 -0
  12. package/dist/cli/check/render-invariants.js +24 -0
  13. package/dist/cli/check/resolve-tool.js +38 -0
  14. package/dist/cli/check/section-assets.js +58 -0
  15. package/dist/cli/check/stages/deps.js +337 -0
  16. package/dist/cli/check/stages/lint.js +46 -0
  17. package/dist/cli/check/stages/render.js +101 -0
  18. package/dist/cli/check/stages/size.js +274 -0
  19. package/dist/cli/check/stages/tree.js +206 -0
  20. package/dist/cli/check/stages/typecheck.js +46 -0
  21. package/dist/cli/check/stages/validate/catalog-exhaustiveness.js +18 -0
  22. package/dist/cli/check/stages/validate/facts.js +18 -0
  23. package/dist/cli/check/stages/validate/formatter-contract.js +14 -0
  24. package/dist/cli/check/stages/validate/group-contract.js +21 -0
  25. package/dist/cli/check/stages/validate/index.js +66 -0
  26. package/dist/cli/check/stages/validate/list-scalar-contract.js +22 -0
  27. package/dist/cli/check/stages/validate/nav-contract.js +93 -0
  28. package/dist/cli/check/stages/validate/nested-slot-contract.js +12 -0
  29. package/dist/cli/check/stages/validate/object-list-contract.js +31 -0
  30. package/dist/cli/check/stages/validate/orchestrator.js +241 -0
  31. package/dist/cli/check/stages/validate/produced-by-contract.js +18 -0
  32. package/dist/cli/check/stages/validate/row-contract.js +31 -0
  33. package/dist/cli/check/stages/validate/select-contract.js +13 -0
  34. package/dist/cli/check/stages/validate/source-contract.js +21 -0
  35. package/dist/cli/check/stages/validate/variant-contract.js +15 -0
  36. package/dist/cli/check/workspace.js +86 -0
  37. package/dist/cli/dev/build-css.js +91 -0
  38. package/dist/cli/dev/catalog.js +35 -0
  39. package/dist/cli/dev/compose-template.js +38 -0
  40. package/dist/cli/dev/content-override.js +58 -0
  41. package/dist/cli/dev/discover.js +35 -0
  42. package/dist/cli/dev/fill-state.js +75 -0
  43. package/dist/cli/dev/index.js +32 -0
  44. package/dist/cli/dev/inspect.js +38 -0
  45. package/dist/cli/dev/island-bootstrap.js +38 -0
  46. package/dist/cli/dev/island-map.js +35 -0
  47. package/dist/cli/dev/island-transform.js +31 -0
  48. package/dist/cli/dev/manifest-instances.js +31 -0
  49. package/dist/cli/dev/render-section.js +24 -0
  50. package/dist/cli/dev/screenshot-target.js +24 -0
  51. package/dist/cli/dev/section-page.js +54 -0
  52. package/dist/cli/dev/server.js +480 -0
  53. package/dist/cli/dev/slot-schema.js +12 -0
  54. package/dist/cli/dev/structure-summary.js +118 -0
  55. package/dist/cli/dev/tailwind.js +32 -0
  56. package/dist/cli/dev/vite-server.js +33 -0
  57. package/dist/cli/dev/workspace.js +63 -0
  58. package/dist/cli/link/index.js +74 -0
  59. package/dist/cli/link/overlay.js +59 -0
  60. package/dist/cli/link/watch.js +25 -0
  61. package/dist/cli/new/emit.js +48 -0
  62. package/dist/cli/new/index.js +69 -0
  63. package/dist/cli/new/scaffold/section/_Renderer.tsx +20 -0
  64. package/dist/cli/new/scaffold/section/_fill-spec.ts +18 -0
  65. package/dist/cli/new/scaffold/section/_fixtures.ts +13 -0
  66. package/dist/cli/new/scaffold/section/_schema.ts +24 -0
  67. package/dist/cli/new/scaffold/template/_manifest.json +9 -0
  68. package/dist/cli/new/scaffold/template/sections/hero/ExpandableText.tsx +45 -0
  69. package/dist/cli/new/scaffold/template/sections/hero/Renderer.tsx +40 -0
  70. package/dist/cli/new/scaffold/template/sections/hero/components/Feature.tsx +14 -0
  71. package/dist/cli/new/scaffold/template/sections/hero/fill-spec.ts +29 -0
  72. package/dist/cli/new/scaffold/template/sections/hero/fixtures.ts +41 -0
  73. package/dist/cli/new/scaffold/template/sections/hero/schema.ts +60 -0
  74. package/dist/cli/new/scaffold/template/theme.css +24 -0
  75. package/dist/cli/new/scaffold/template/theme.ts +27 -0
  76. package/dist/cli/new/scaffold-assets.js +20 -0
  77. package/dist/cli/pack/collect.js +44 -0
  78. package/dist/cli/pack/guards.js +57 -0
  79. package/dist/cli/pack/index.js +66 -0
  80. package/dist/cli/pack/manifest.js +15 -0
  81. package/dist/cli/pack/zip.js +86 -0
  82. package/dist/cli/theme/generate.js +43 -0
  83. package/dist/cli/theme/index.js +33 -0
  84. package/dist/cli/theme/load-theme.js +69 -0
  85. package/dist/cli.js +100 -16
  86. package/dist/contracts/fill-treatments.js +47 -0
  87. package/dist/design-system/theme.d.ts +30 -300
  88. package/dist/design-system/theme.js +112 -90
  89. package/dist/dev-client/assets/index-DfEfrp6P.css +1 -0
  90. package/dist/dev-client/assets/index-bpMP0b0W.js +50 -0
  91. package/dist/dev-client/index.html +13 -0
  92. package/dist/eslint/is-client-file.d.ts +4 -0
  93. package/dist/eslint/rules/no-hex.js +78 -6
  94. package/dist/eslint.js +1 -1
  95. package/dist/index.d.ts +3 -2
  96. package/dist/index.js +2 -2
  97. package/dist/islands/detect.d.ts +16 -0
  98. package/dist/islands/detect.js +22 -0
  99. package/dist/islands/discover.d.ts +5 -0
  100. package/dist/islands/discover.js +19 -0
  101. package/dist/islands/esbuild-plugin.d.ts +13 -0
  102. package/dist/islands/esbuild-plugin.js +39 -0
  103. package/dist/islands/wrap.d.ts +11 -0
  104. package/dist/islands/wrap.js +48 -0
  105. package/dist/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js +77 -0
  106. package/dist/node_modules/magic-string/dist/magic-string.es.js +939 -0
  107. package/dist/package.js +1 -1
  108. package/dist/primitives/Image.js +1 -1
  109. package/dist/schema/fill-spec.d.ts +80 -590
  110. package/dist/schema/fixture-schema.d.ts +5 -81
  111. package/dist/schema/manifest.d.ts +39 -475
  112. package/dist/schema/resolve-section-ref.js +10 -0
  113. package/dist/schema/rows.js +10 -0
  114. package/dist/schema/section-nav.js +9 -0
  115. package/dist/schema/section-schema.d.ts +51 -437
  116. package/dist/schema/slot-types.d.ts +12 -16
  117. package/dist/ssr.d.ts +31 -0
  118. package/dist/ssr.js +46 -0
  119. package/dist/styles.css +31 -88
  120. package/docs/INDEX.md +12 -3
  121. package/docs/assets.md +128 -0
  122. package/docs/check.md +124 -0
  123. package/docs/dev.md +187 -0
  124. package/docs/eslint.md +18 -8
  125. package/docs/islands.md +5 -2
  126. package/docs/llms.txt +59 -9
  127. package/docs/new.md +27 -0
  128. package/docs/overview.md +57 -0
  129. package/docs/pack.md +46 -0
  130. package/docs/primitives.md +10 -6
  131. package/docs/quickstart.md +102 -0
  132. package/docs/recipes/INDEX.md +27 -0
  133. package/docs/recipes/bind-property-fact.md +58 -0
  134. package/docs/recipes/collection-slot.md +100 -0
  135. package/docs/recipes/fill-spec-decision.md +66 -0
  136. package/docs/recipes/fixture-states.md +68 -0
  137. package/docs/recipes/image-slot-crop.md +98 -0
  138. package/docs/recipes/interactive-island.md +100 -0
  139. package/docs/recipes/organize-section-folder.md +72 -0
  140. package/docs/recipes/prepare-submission.md +55 -0
  141. package/docs/recipes/second-template.md +49 -0
  142. package/docs/recipes/select-slot.md +59 -0
  143. package/docs/recipes/static-asset.md +103 -0
  144. package/docs/recipes/third-party-package.md +90 -0
  145. package/docs/rules/INDEX.md +14 -5
  146. package/docs/rules/bundle-binary-asset.md +83 -0
  147. package/docs/rules/bundle-incomplete.md +2 -2
  148. package/docs/rules/css-reason.md +3 -3
  149. package/docs/rules/fixtures-invalid.md +96 -0
  150. package/docs/rules/license-denied.md +11 -3
  151. package/docs/rules/manifest-invalid.md +99 -0
  152. package/docs/rules/no-bare-css-import.md +8 -8
  153. package/docs/rules/no-css-import-from-render-path.md +7 -8
  154. package/docs/rules/no-hex.md +76 -10
  155. package/docs/rules/no-templates.md +87 -0
  156. package/docs/rules/parse-error.md +76 -0
  157. package/docs/rules/schema-invalid.md +96 -0
  158. package/docs/rules/size-assets.md +88 -0
  159. package/docs/rules/size-island-bundle.md +123 -0
  160. package/docs/rules/size-section-css.md +19 -14
  161. package/docs/schema-system.md +28 -28
  162. package/docs/theme-and-css.md +157 -92
  163. package/docs/vocabulary.md +98 -0
  164. package/package.json +14 -11
  165. 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 };
@@ -1,8 +1,77 @@
1
1
  import { docsPathForRule } from "../../rules/registry.js";
2
2
 
3
3
  //#region src/eslint/rules/no-hex.ts
4
- const COLOR_FN = /(?:oklch|oklab|rgba?|hsla?|color-mix)\s*\([^)]*\)/;
5
4
  const HEX = /#(?:[0-9a-fA-F]{8}|[0-9a-fA-F]{6}|[0-9a-fA-F]{4}|[0-9a-fA-F]{3})\b/g;
5
+ const SEPARATOR = /[\s_]/;
6
+ const TRIM_SEPARATORS = /^[\s_]+|[\s_]+$/g;
7
+ const PERCENTAGE = /^[+-]?(?:\d+\.?\d*|\.\d+)%$/;
8
+ const VAR_REF = /^var\((.*)\)$/s;
9
+ const NESTED_COLOR_MIX = /^color-mix[\s_]*\((.*)\)$/s;
10
+ const CUSTOM_PROPERTY_NAME = /^--[A-Za-z0-9_-]+$/;
11
+ const PALETTE_FREE_KEYWORDS = /* @__PURE__ */ new Set(["transparent", "currentcolor"]);
12
+ function splitTopLevel(text, isSeparator) {
13
+ const parts = [];
14
+ let depth = 0;
15
+ let current = "";
16
+ for (const char of text) {
17
+ if (char === "(") depth += 1;
18
+ else if (char === ")") depth -= 1;
19
+ if (depth === 0 && isSeparator(char)) {
20
+ if (current !== "") parts.push(current);
21
+ current = "";
22
+ continue;
23
+ }
24
+ current += char;
25
+ }
26
+ if (current !== "") parts.push(current);
27
+ return parts;
28
+ }
29
+ function findColorFunctionCalls(text) {
30
+ const head = /(?:oklch|oklab|rgba?|hsla?|color-mix)[\s_]*\(/g;
31
+ const calls = [];
32
+ let match;
33
+ while (match = head.exec(text)) {
34
+ const open = match.index + match[0].length - 1;
35
+ let depth = 0;
36
+ let close = -1;
37
+ for (let i = open; i < text.length; i += 1) if (text[i] === "(") depth += 1;
38
+ else if (text[i] === ")") {
39
+ depth -= 1;
40
+ if (depth === 0) {
41
+ close = i;
42
+ break;
43
+ }
44
+ }
45
+ if (close === -1) continue;
46
+ calls.push({
47
+ raw: text.slice(match.index, close + 1),
48
+ name: match[0].replace(/[\s_]*\($/, "").toLowerCase(),
49
+ args: text.slice(open + 1, close)
50
+ });
51
+ }
52
+ return calls;
53
+ }
54
+ function isThemeDerivedColor(token) {
55
+ const value = token.replace(TRIM_SEPARATORS, "");
56
+ if (PALETTE_FREE_KEYWORDS.has(value.toLowerCase())) return true;
57
+ const varRef = VAR_REF.exec(value);
58
+ if (varRef) {
59
+ const [name, ...fallback] = splitTopLevel(varRef[1] ?? "", (char) => char === ",");
60
+ if (name === void 0 || !CUSTOM_PROPERTY_NAME.test(name.replace(TRIM_SEPARATORS, ""))) return false;
61
+ return fallback.length === 0 || isThemeDerivedColor(fallback.join(","));
62
+ }
63
+ const nested = NESTED_COLOR_MIX.exec(value);
64
+ return nested !== null && isThemeDerivedColorMix(nested[1] ?? "");
65
+ }
66
+ function isThemeDerivedColorMix(args) {
67
+ const [interpolation, ...colors] = splitTopLevel(args, (char) => char === ",");
68
+ if (interpolation === void 0 || !/^in[\s_]/.test(interpolation.replace(TRIM_SEPARATORS, ""))) return false;
69
+ if (colors.length === 0) return false;
70
+ return colors.every((color) => {
71
+ const [colorToken, ...rest] = splitTopLevel(color, (char) => SEPARATOR.test(char)).filter((token) => !PERCENTAGE.test(token));
72
+ return colorToken !== void 0 && rest.length === 0 && isThemeDerivedColor(colorToken);
73
+ });
74
+ }
6
75
  const COLOR_UTILITY_PREFIXES = [
7
76
  "bg",
8
77
  "text",
@@ -74,16 +143,19 @@ const noHex = {
74
143
  url: docsPathForRule("no-hex")
75
144
  },
76
145
  schema: [],
77
- messages: { hardCodedColor: "`{{value}}` is a hard-coded colour — a section carries no palette of its own, or it breaks the first time another template reuses it. Fix: use a semantic token utility (e.g. `bg-surface-alt`, `text-ink`); if the value is genuinely brand decoration with no token, add it to the template's `theme.ts` palette and use the `--tr-color-<name>` utility it compiles to." }
146
+ messages: {
147
+ hardCodedColor: "`{{value}}` is a hard-coded colour — a section carries no palette of its own, or it breaks the first time another template reuses it. Fix: use one of your theme's colour utilities; if the value is genuinely brand decoration with no token yet, add it to `colors` in the template's `theme.ts` and use the `bg-<name>` / `text-<name>` utility it compiles to.",
148
+ hardCodedColorFunction: "`{{value}}` bakes a colour into a section — a section carries no palette of its own, or it breaks the first time another template reuses it. Fix: derive the colour from the theme instead. A `color-mix()` over theme vars is allowed (e.g. `color-mix(in srgb, var(--tr-color-primary) 80%, var(--tr-color-surface))`, or mix toward `transparent` for a translucent tint) — only a hard-coded argument makes it a violation."
149
+ }
78
150
  },
79
151
  create(context) {
80
152
  function check(node, text) {
81
- const fn = COLOR_FN.exec(text);
82
- if (fn) {
153
+ for (const call of findColorFunctionCalls(text)) {
154
+ if (call.name === "color-mix" && isThemeDerivedColorMix(call.args)) continue;
83
155
  context.report({
84
156
  node,
85
- messageId: "hardCodedColor",
86
- data: { value: fn[0] }
157
+ messageId: "hardCodedColorFunction",
158
+ data: { value: call.raw }
87
159
  });
88
160
  return;
89
161
  }
package/dist/eslint.js CHANGED
@@ -55,7 +55,7 @@ const templateKitConfig = [
55
55
  "template-kit/no-raw-element": "error",
56
56
  "template-kit/image-bare-needs-reason": "error",
57
57
  "template-kit/slot-marker-literal": "error",
58
- "template-kit/no-hex": "error",
58
+ "template-kit/no-hex": "warn",
59
59
  "template-kit/no-css-import-from-render-path": "error"
60
60
  }
61
61
  },
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";
@@ -24,7 +25,7 @@ import { SlotGroup } from "./primitives/SlotGroup.js";
24
25
  import { SlotItem } from "./primitives/SlotItem.js";
25
26
  import "./primitives/index.js";
26
27
  import { ATTR_SECTION_INSTANCE_ID, ATTR_SLOT_GROUP, ATTR_SLOT_ID, ATTR_SLOT_ITEM, ATTR_SLOT_TEXT_LEAF, ATTR_TR_ENHANCE } from "./contracts/markers.js";
27
- import { COLOR_ROLE_VARS, ColorRole, FONT_ROLE_VARS, LAYOUT_VARS, RADIUS_VARS, SHADOW_VARS, TYPE_STEP_VARS, TokenTheme, TokenThemeSchema, compileThemeToCss } from "./design-system/theme.js";
28
+ import { TokenNameSchema, TokenTheme, TokenThemeSchema, compileThemeToCss } from "./design-system/theme.js";
28
29
  //#region src/index.d.ts
29
30
  /**
30
31
  * The published version of this kit.
@@ -37,4 +38,4 @@ import { COLOR_ROLE_VARS, ColorRole, FONT_ROLE_VARS, LAYOUT_VARS, RADIUS_VARS, S
37
38
  */
38
39
  declare const KIT_VERSION: string;
39
40
  //#endregion
40
- export { ATTR_ISLAND_ID, ATTR_ISLAND_KEY, ATTR_ISLAND_PROPS, ATTR_SECTION_INSTANCE_ID, ATTR_SLOT_GROUP, ATTR_SLOT_ID, ATTR_SLOT_ITEM, ATTR_SLOT_TEXT_LEAF, ATTR_TR_ENHANCE, type AddressParts, COLLECTIONS, COLOR_ROLE_VARS, CONTENT_SLOT_TYPES, type CollectionField, type CollectionIdKey, type CollectionOwnership, type ColorRole, ContentSlot, type ContentSlotType, type Coordinates, DECISION_TYPES, DERIVED_INPUT_FIELDS, Decision, type DecisionType, type DerivedInputName, FONT_ROLE_VARS, FORMATTER_NAMES, FillSpec, type FillSpecShape, type FixtureModule, FixtureModuleShape, type FixtureOverride, type FloorplanAssignment, type FormatterName, type FrameConfig, ISLAND_ELEMENT, Image, type ImageCollectionValue, type ImageValue, IslandComponent, IslandEditorOptions, IslandMarker, type IslandMarkerProps, IslandModule, IslandProps, IslandPropsError, KIT_VERSION, LAYOUT_VARS, OptionDef, type OptionValue, type PicturePlan, type PictureSourceSpec, type PoiRow, type PropertyFacts, RADIUS_VARS, REFERRAL_FIELDS, REFERRAL_IMAGE_FIELDS, REF_SLOT_TYPES, type RefSlotType, type ReferralField, type ReferralImageField, type ResolvedFixture, type ResponsiveImage, type ResponsiveSource, SHADOW_VARS, SLOT_TYPES, SLOT_TYPE_DESCRIPTORS, SOURCE_FIELDS, Section, SectionMeta, type SectionMetaShape, type SectionNav, type SectionProps, SectionSchema, type SectionSchemaResolved, type SectionSchemaShape, ShieldMode, Slot, SlotDef, type Slot$1 as SlotDefinition, SlotGroup, SlotGroupDef, type SlotGroupDefShape, SlotItem, type SlotType, type SlotValue, type SourceFieldContainer, type SourceFieldDescriptor, type SourceFieldName, type SourceFieldValueType, TRANSFORMS, TRANSFORM_NAMES, TYPE_STEP_VARS, TemplateManifest, type TemplateManifestShape, type TokenTheme, TokenThemeSchema, type TransformDescriptor, type TransformFn, type TransformInput, type TransformName, type TransformUnit, type UnitRow, VERIFICATION_WIDTHS, type VariantSelectorFn, type VariantsMeta, type VerificationWidth, type WritebackTarget, buildFixtureSet, buildInvariantFixtures, collectionForSlot, compileThemeToCss, defaultForSlotType, derived, deserializeIslandProps, direct, islandId, makeResolveVariants, serializeIslandProps, slotDefTypes };
41
+ export { ATTR_ISLAND_ID, ATTR_ISLAND_KEY, ATTR_ISLAND_PROPS, ATTR_SECTION_INSTANCE_ID, ATTR_SLOT_GROUP, ATTR_SLOT_ID, ATTR_SLOT_ITEM, ATTR_SLOT_TEXT_LEAF, ATTR_TR_ENHANCE, type AddressParts, COLLECTIONS, CONTENT_SLOT_TYPES, type CollectionField, type CollectionIdKey, type CollectionOwnership, ContentSlot, type ContentSlotType, type Coordinates, DECISION_TYPES, DERIVED_INPUT_FIELDS, Decision, type DecisionType, type DerivedInputName, FORMATTER_NAMES, FillSpec, type FillSpecShape, type FixtureModule, FixtureModuleShape, type FixtureOverride, type FloorplanAssignment, type FormatterName, type FrameConfig, ISLAND_ELEMENT, Image, type ImageCollectionValue, type ImageValue, IslandComponent, IslandEditorOptions, IslandMarker, type IslandMarkerProps, IslandModule, IslandProps, IslandPropsError, KIT_VERSION, OptionDef, type OptionValue, type PicturePlan, type PictureSourceSpec, type PoiRow, type PropertyFacts, REFERRAL_FIELDS, REFERRAL_IMAGE_FIELDS, REF_SLOT_TYPES, type RefSlotType, type ReferralField, type ReferralImageField, type ResolvedFixture, type ResponsiveImage, type ResponsiveSource, SLOT_TYPES, SLOT_TYPE_DESCRIPTORS, SOURCE_FIELDS, Section, SectionMeta, type SectionMetaShape, type SectionNav, type SectionProps, SectionSchema, type SectionSchemaResolved, type SectionSchemaShape, ShieldMode, Slot, SlotDef, type Slot$1 as SlotDefinition, SlotGroup, SlotGroupDef, type SlotGroupDefShape, SlotItem, type SlotType, type SlotValue, type SourceFieldContainer, type SourceFieldDescriptor, type SourceFieldName, type SourceFieldValueType, TRANSFORMS, TRANSFORM_NAMES, TemplateManifest, type TemplateManifestShape, TokenNameSchema, type TokenTheme, TokenThemeSchema, type TransformDescriptor, type TransformFn, type TransformInput, type TransformName, type TransformUnit, type UnitRow, VERIFICATION_WIDTHS, type VariantSelectorFn, type VariantsMeta, type VerificationWidth, type WritebackTarget, buildFixtureSet, buildInvariantFixtures, collectionForSlot, compileThemeToCss, defaultForSlotType, derived, deserializeIslandProps, direct, islandId, makeResolveVariants, serializeIslandProps, slotDefTypes };
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ import { Section } from "./primitives/Section.js";
17
17
  import { Slot } from "./primitives/Slot.js";
18
18
  import { SlotGroup } from "./primitives/SlotGroup.js";
19
19
  import { SlotItem } from "./primitives/SlotItem.js";
20
- import { COLOR_ROLE_VARS, FONT_ROLE_VARS, LAYOUT_VARS, RADIUS_VARS, SHADOW_VARS, TYPE_STEP_VARS, TokenThemeSchema, compileThemeToCss } from "./design-system/theme.js";
20
+ import { TokenNameSchema, TokenThemeSchema, compileThemeToCss } from "./design-system/theme.js";
21
21
 
22
22
  //#region src/index.ts
23
23
  /**
@@ -32,4 +32,4 @@ import { COLOR_ROLE_VARS, FONT_ROLE_VARS, LAYOUT_VARS, RADIUS_VARS, SHADOW_VARS,
32
32
  const KIT_VERSION = version;
33
33
 
34
34
  //#endregion
35
- export { ATTR_ISLAND_ID, ATTR_ISLAND_KEY, ATTR_ISLAND_PROPS, ATTR_SECTION_INSTANCE_ID, ATTR_SLOT_GROUP, ATTR_SLOT_ID, ATTR_SLOT_ITEM, ATTR_SLOT_TEXT_LEAF, ATTR_TR_ENHANCE, COLLECTIONS, COLOR_ROLE_VARS, CONTENT_SLOT_TYPES, ContentSlot, DECISION_TYPES, DERIVED_INPUT_FIELDS, Decision, FONT_ROLE_VARS, FORMATTER_NAMES, FillSpec, FixtureModuleShape, ISLAND_ELEMENT, Image, IslandMarker, IslandPropsError, KIT_VERSION, LAYOUT_VARS, OptionDef, RADIUS_VARS, REFERRAL_FIELDS, REFERRAL_IMAGE_FIELDS, REF_SLOT_TYPES, SHADOW_VARS, SLOT_TYPES, SLOT_TYPE_DESCRIPTORS, SOURCE_FIELDS, Section, SectionMeta, SectionSchema, Slot, SlotDef, SlotGroup, SlotGroupDef, SlotItem, TRANSFORMS, TRANSFORM_NAMES, TYPE_STEP_VARS, TemplateManifest, TokenThemeSchema, VERIFICATION_WIDTHS, buildFixtureSet, buildInvariantFixtures, collectionForSlot, compileThemeToCss, defaultForSlotType, derived, deserializeIslandProps, direct, islandId, makeResolveVariants, serializeIslandProps, slotDefTypes };
35
+ export { ATTR_ISLAND_ID, ATTR_ISLAND_KEY, ATTR_ISLAND_PROPS, ATTR_SECTION_INSTANCE_ID, ATTR_SLOT_GROUP, ATTR_SLOT_ID, ATTR_SLOT_ITEM, ATTR_SLOT_TEXT_LEAF, ATTR_TR_ENHANCE, COLLECTIONS, CONTENT_SLOT_TYPES, ContentSlot, DECISION_TYPES, DERIVED_INPUT_FIELDS, Decision, FORMATTER_NAMES, FillSpec, FixtureModuleShape, ISLAND_ELEMENT, Image, IslandMarker, IslandPropsError, KIT_VERSION, OptionDef, REFERRAL_FIELDS, REFERRAL_IMAGE_FIELDS, REF_SLOT_TYPES, SLOT_TYPES, SLOT_TYPE_DESCRIPTORS, SOURCE_FIELDS, Section, SectionMeta, SectionSchema, Slot, SlotDef, SlotGroup, SlotGroupDef, SlotItem, TRANSFORMS, TRANSFORM_NAMES, TemplateManifest, TokenNameSchema, TokenThemeSchema, VERIFICATION_WIDTHS, buildFixtureSet, buildInvariantFixtures, collectionForSlot, compileThemeToCss, defaultForSlotType, derived, deserializeIslandProps, direct, islandId, makeResolveVariants, serializeIslandProps, slotDefTypes };
@@ -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 };