@woodsportal/hubspot-kit 1.0.4 → 1.0.26

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 (217) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +4 -2
  3. package/dist/{chunk-JDGIAYHX.js → chunk-GTTIJVAR.js} +43 -3
  4. package/dist/chunk-GTTIJVAR.js.map +1 -0
  5. package/dist/cli.js +1400 -338
  6. package/dist/cli.js.map +1 -1
  7. package/dist/exec-U2OJATR3.js +12 -0
  8. package/dist/index.d.ts +20 -0
  9. package/dist/index.js +8 -1
  10. package/dist/index.js.map +1 -1
  11. package/examples/greenfield/module-hybrid-cdn/README.md +40 -3
  12. package/examples/greenfield/module-hybrid-cdn/public/fields.json +44 -1
  13. package/examples/greenfield/module-hybrid-cdn/src/app.tsx +8 -0
  14. package/examples/greenfield/module-hybrid-cdn/src/assets/css/main.dev.css +6 -1
  15. package/examples/greenfield/module-hybrid-cdn/src/cdn/app-entry.ts +32 -0
  16. package/examples/greenfield/module-hybrid-cdn/src/cdn/vendor-entry.ts +28 -0
  17. package/examples/greenfield/module-hybrid-cdn/src/components/layout/section-card.tsx +20 -0
  18. package/examples/greenfield/module-hybrid-cdn/src/components/layout/starter-nav.tsx +28 -0
  19. package/examples/greenfield/module-hybrid-cdn/src/components/layout/starter-shell.tsx +18 -0
  20. package/examples/greenfield/module-hybrid-cdn/src/config/router.ts +4 -0
  21. package/examples/greenfield/module-hybrid-cdn/src/config/routes.ts +5 -0
  22. package/examples/greenfield/module-hybrid-cdn/src/defaultData.ts +2 -7
  23. package/examples/greenfield/module-hybrid-cdn/src/features/starter/components/starter-guide-page.tsx +93 -0
  24. package/examples/greenfield/module-hybrid-cdn/src/features/starter/components/starter-home-page.tsx +21 -0
  25. package/examples/greenfield/module-hybrid-cdn/src/features/starter/components/starter-home.tsx +52 -0
  26. package/examples/greenfield/module-hybrid-cdn/src/features/starter/components/starter-single-page.tsx +17 -0
  27. package/examples/greenfield/module-hybrid-cdn/src/features/starter/content/woodscli-guide-content.ts +86 -0
  28. package/examples/greenfield/module-hybrid-cdn/src/features/starter/hooks/use-starter-preview.ts +47 -0
  29. package/examples/greenfield/module-hybrid-cdn/src/features/starter/index.ts +6 -0
  30. package/examples/greenfield/module-hybrid-cdn/src/hubspot/bootstrap.ts +10 -0
  31. package/examples/greenfield/module-hybrid-cdn/src/hubspot/default-data.ts +7 -0
  32. package/examples/greenfield/module-hybrid-cdn/src/integrations/tanstack-router/router.tsx +25 -0
  33. package/examples/greenfield/module-hybrid-cdn/src/main.tsx +4 -3
  34. package/examples/greenfield/module-hybrid-cdn/src/portal/mount-dev-preview.tsx +8 -0
  35. package/examples/greenfield/module-hybrid-cdn/src/portal/mount-portal.tsx +12 -0
  36. package/examples/greenfield/module-hybrid-cdn/src/routeTree.gen.ts +77 -0
  37. package/examples/greenfield/module-hybrid-cdn/src/routes/__root.tsx +15 -0
  38. package/examples/greenfield/module-hybrid-cdn/src/routes/guide.tsx +7 -0
  39. package/examples/greenfield/module-hybrid-cdn/src/routes/index.tsx +7 -0
  40. package/examples/greenfield/module-hybrid-cdn/tsconfig.json +20 -0
  41. package/examples/greenfield/module-monolith/public/fields.json +44 -1
  42. package/examples/greenfield/module-monolith/src/app.tsx +8 -0
  43. package/examples/greenfield/module-monolith/src/assets/css/main.dev.css +8 -0
  44. package/examples/greenfield/module-monolith/src/components/layout/section-card.tsx +20 -0
  45. package/examples/greenfield/module-monolith/src/components/layout/starter-nav.tsx +28 -0
  46. package/examples/greenfield/module-monolith/src/components/layout/starter-shell.tsx +18 -0
  47. package/examples/greenfield/module-monolith/src/config/router.ts +4 -0
  48. package/examples/greenfield/module-monolith/src/config/routes.ts +5 -0
  49. package/examples/greenfield/module-monolith/src/defaultData.ts +2 -7
  50. package/examples/greenfield/module-monolith/src/features/starter/components/starter-guide-page.tsx +93 -0
  51. package/examples/greenfield/module-monolith/src/features/starter/components/starter-home-page.tsx +21 -0
  52. package/examples/greenfield/module-monolith/src/features/starter/components/starter-home.tsx +52 -0
  53. package/examples/greenfield/module-monolith/src/features/starter/components/starter-single-page.tsx +17 -0
  54. package/examples/greenfield/module-monolith/src/features/starter/content/woodscli-guide-content.ts +86 -0
  55. package/examples/greenfield/module-monolith/src/features/starter/hooks/use-starter-preview.ts +47 -0
  56. package/examples/greenfield/module-monolith/src/features/starter/index.ts +6 -0
  57. package/examples/greenfield/module-monolith/src/hubspot/default-data.ts +7 -0
  58. package/examples/greenfield/module-monolith/src/hubspot/types.ts +17 -0
  59. package/examples/greenfield/module-monolith/src/integrations/tanstack-router/router.tsx +25 -0
  60. package/examples/greenfield/module-monolith/src/main.tsx +5 -0
  61. package/examples/greenfield/module-monolith/src/portal/mount-dev-preview.tsx +8 -0
  62. package/examples/greenfield/module-monolith/src/portal/mount-portal.tsx +12 -0
  63. package/examples/greenfield/module-monolith/src/routeTree.gen.ts +77 -0
  64. package/examples/greenfield/module-monolith/src/routes/__root.tsx +15 -0
  65. package/examples/greenfield/module-monolith/src/routes/guide.tsx +7 -0
  66. package/examples/greenfield/module-monolith/src/routes/index.tsx +7 -0
  67. package/examples/greenfield/module-monolith/tsconfig.json +20 -0
  68. package/package.json +4 -1
  69. package/scripts/check-hubspot-bundle-size.mjs +5 -2
  70. package/scripts/ensure-wphs-workspace.mjs +2 -1
  71. package/scripts/hubspot-module-postbuild.mjs +1 -1
  72. package/scripts/read-woodsportal-config.mjs +4 -1
  73. package/scripts/run-hubspot-module-postbuild.mjs +9 -4
  74. package/scripts/vite/bootstrap-shim-resolver.js +47 -0
  75. package/scripts/vite/ensure-dev-kit-shims.js +7 -1
  76. package/scripts/vite/react-dev-plugin-options.js +2 -1
  77. package/scripts/watch-hubspot.mjs +107 -17
  78. package/scripts/wphs-paths.mjs +1 -0
  79. package/scripts/write-audit-artifacts.mjs +2 -1
  80. package/src/dev/bootstrap-greenfield.tsx +59 -0
  81. package/src/dev/bootstrap.tsx +46 -0
  82. package/src/dev-module-config/__tests__/fixtures/fields.json +3014 -0
  83. package/src/dev-module-config/__tests__/fixtures/golden-module.fixture.ts +102 -0
  84. package/src/dev-module-config/__tests__/module-config-storage.test.ts +40 -0
  85. package/src/dev-module-config/__tests__/module-config-transform.test.ts +55 -0
  86. package/src/dev-module-config/__tests__/normalize-repeater-items.test.ts +46 -0
  87. package/src/dev-module-config/dev-module-display-name.ts +5 -0
  88. package/src/dev-module-config/runtime/module-config-bridge.ts +27 -0
  89. package/src/dev-module-config/runtime/module-config-runtime.ts +185 -0
  90. package/src/dev-module-config/schema/build-default-item.ts +7 -0
  91. package/src/dev-module-config/schema/deep-merge.ts +27 -0
  92. package/src/dev-module-config/schema/extract-defaults.ts +62 -0
  93. package/src/dev-module-config/schema/normalize-choices.ts +18 -0
  94. package/src/dev-module-config/schema/normalize-repeater-items.ts +31 -0
  95. package/src/dev-module-config/schema/parse-fields-json.ts +2 -0
  96. package/src/dev-module-config/schema/types.ts +75 -0
  97. package/src/dev-module-config/schema/validate-module-config.ts +81 -0
  98. package/src/dev-module-config/schema/visibility.ts +130 -0
  99. package/src/dev-module-config/storage/config-json.ts +46 -0
  100. package/src/dev-module-config/storage/workspace-store.ts +139 -0
  101. package/src/dev-module-config/transform/build-hubspot-data.ts +409 -0
  102. package/src/dev-module-config/ui/FieldRenderer.tsx +353 -0
  103. package/src/dev-module-config/ui/ModuleConfigAnimatedPresence.tsx +72 -0
  104. package/src/dev-module-config/ui/ModuleConfigCollapsible.tsx +43 -0
  105. package/src/dev-module-config/ui/ModuleConfigDockSideIcon.tsx +27 -0
  106. package/src/dev-module-config/ui/ModuleConfigOverlay.tsx +134 -0
  107. package/src/dev-module-config/ui/ModuleConfigPersistenceStatus.tsx +89 -0
  108. package/src/dev-module-config/ui/ModuleConfigShell.tsx +355 -0
  109. package/src/dev-module-config/ui/ModuleConfigThemeToggle.tsx +32 -0
  110. package/src/dev-module-config/ui/ModuleConfigUiContext.tsx +65 -0
  111. package/src/dev-module-config/ui/editors/DeveloperOptionsEditor.tsx +61 -0
  112. package/src/dev-module-config/ui/editors/MenuOptionsEditor.tsx +103 -0
  113. package/src/dev-module-config/ui/hubspot/HsColorField.tsx +106 -0
  114. package/src/dev-module-config/ui/hubspot/HsColorPickerPopover.tsx +210 -0
  115. package/src/dev-module-config/ui/hubspot/HsFieldLabel.tsx +24 -0
  116. package/src/dev-module-config/ui/hubspot/HsFieldWidgets.tsx +248 -0
  117. package/src/dev-module-config/ui/hubspot/HsImageField.tsx +112 -0
  118. package/src/dev-module-config/ui/hubspot/HsOccurrenceGroupEditor.tsx +243 -0
  119. package/src/dev-module-config/ui/hubspot/HsRepeaterList.tsx +178 -0
  120. package/src/dev-module-config/ui/hubspot/HsSectionHeader.tsx +20 -0
  121. package/src/dev-module-config/ui/hubspot/HsToggle.tsx +39 -0
  122. package/src/dev-module-config/ui/hubspot/HsToggleRow.tsx +30 -0
  123. package/src/dev-module-config/ui/hubspot/hs-color-palette.ts +11 -0
  124. package/src/dev-module-config/ui/hubspot/module-config-typography.ts +26 -0
  125. package/src/dev-module-config/ui/module-config-motion.css +134 -0
  126. package/src/dev-module-config/ui/module-config-motion.ts +28 -0
  127. package/src/dev-module-config/ui/module-config-overlay-side.ts +29 -0
  128. package/src/dev-module-config/ui/module-config-surfaces.ts +27 -0
  129. package/src/dev-module-config/ui/module-config-theme.css +489 -0
  130. package/src/dev-module-config/ui/module-config-toolbar-theme.ts +35 -0
  131. package/src/dev-module-config/ui/navigation/ModuleConfigNavContext.tsx +194 -0
  132. package/src/dev-module-config/ui/use-dev-module-config-live-revision.ts +22 -0
  133. package/src/dev-module-config/ui/use-hubspot-live-exports.ts +27 -0
  134. package/src/dev-module-config/ui/use-module-config.ts +124 -0
  135. package/src/routing/build-api-routes.ts +37 -0
  136. package/src/routing/route-format.ts +19 -0
  137. package/templates/module-hybrid-cdn/README.md +53 -3
  138. package/templates/module-hybrid-cdn/index.html +12 -1
  139. package/templates/module-hybrid-cdn/package.json +6 -2
  140. package/templates/module-hybrid-cdn/public/fields.json +44 -1
  141. package/templates/module-hybrid-cdn/src/app.tsx +8 -0
  142. package/templates/module-hybrid-cdn/src/assets/css/main.dev.css +6 -1
  143. package/templates/module-hybrid-cdn/src/cdn/app-entry.ts +32 -0
  144. package/templates/module-hybrid-cdn/src/cdn/vendor-entry.ts +28 -0
  145. package/templates/module-hybrid-cdn/src/components/layout/section-card.tsx +20 -0
  146. package/templates/module-hybrid-cdn/src/components/layout/starter-nav.tsx +28 -0
  147. package/templates/module-hybrid-cdn/src/components/layout/starter-shell.tsx +18 -0
  148. package/templates/module-hybrid-cdn/src/config/router.ts +4 -0
  149. package/templates/module-hybrid-cdn/src/config/routes.ts +5 -0
  150. package/templates/module-hybrid-cdn/src/defaultData.ts +2 -7
  151. package/templates/module-hybrid-cdn/src/features/starter/components/starter-guide-page.tsx +93 -0
  152. package/templates/module-hybrid-cdn/src/features/starter/components/starter-home-page.tsx +21 -0
  153. package/templates/module-hybrid-cdn/src/features/starter/components/starter-home.tsx +52 -0
  154. package/templates/module-hybrid-cdn/src/features/starter/components/starter-single-page.tsx +17 -0
  155. package/templates/module-hybrid-cdn/src/features/starter/content/woodscli-guide-content.ts +86 -0
  156. package/templates/module-hybrid-cdn/src/features/starter/hooks/starter-preview-values.ts +42 -0
  157. package/templates/module-hybrid-cdn/src/features/starter/hooks/use-starter-preview.dev.ts +17 -0
  158. package/templates/module-hybrid-cdn/src/features/starter/hooks/use-starter-preview.ts +30 -0
  159. package/templates/module-hybrid-cdn/src/features/starter/index.ts +6 -0
  160. package/templates/module-hybrid-cdn/src/hubspot/bootstrap.ts +10 -0
  161. package/templates/module-hybrid-cdn/src/hubspot/default-data.ts +7 -0
  162. package/templates/module-hybrid-cdn/src/hubspot/module-entry.tsx +9 -0
  163. package/templates/module-hybrid-cdn/src/integrations/tanstack-router/router.tsx +25 -0
  164. package/templates/module-hybrid-cdn/src/main.tsx +4 -3
  165. package/templates/module-hybrid-cdn/src/portal/mount-dev-preview.tsx +8 -0
  166. package/templates/module-hybrid-cdn/src/portal/mount-portal.tsx +12 -0
  167. package/templates/module-hybrid-cdn/src/routeTree.gen.ts +77 -0
  168. package/templates/module-hybrid-cdn/src/routes/__root.tsx +15 -0
  169. package/templates/module-hybrid-cdn/src/routes/guide.tsx +7 -0
  170. package/templates/module-hybrid-cdn/src/routes/index.tsx +7 -0
  171. package/templates/module-hybrid-cdn/tsconfig.json +20 -0
  172. package/templates/module-hybrid-cdn/vite.config.monolith.mjs +8 -0
  173. package/templates/module-monolith/README.md +59 -0
  174. package/templates/module-monolith/index.html +12 -0
  175. package/templates/module-monolith/package.json +10 -3
  176. package/templates/module-monolith/public/fields.json +44 -1
  177. package/templates/module-monolith/public/module.html +1 -0
  178. package/templates/module-monolith/src/app.tsx +8 -0
  179. package/templates/module-monolith/src/assets/css/main.dev.css +6 -1
  180. package/templates/module-monolith/src/components/layout/section-card.tsx +20 -0
  181. package/templates/module-monolith/src/components/layout/starter-nav.tsx +28 -0
  182. package/templates/module-monolith/src/components/layout/starter-shell.tsx +18 -0
  183. package/templates/module-monolith/src/config/router.ts +4 -0
  184. package/templates/module-monolith/src/config/routes.ts +5 -0
  185. package/templates/module-monolith/src/defaultData.ts +2 -7
  186. package/templates/module-monolith/src/features/starter/components/starter-guide-page.tsx +93 -0
  187. package/templates/module-monolith/src/features/starter/components/starter-home-page.tsx +21 -0
  188. package/templates/module-monolith/src/features/starter/components/starter-home.tsx +52 -0
  189. package/templates/module-monolith/src/features/starter/components/starter-single-page.tsx +17 -0
  190. package/templates/module-monolith/src/features/starter/content/woodscli-guide-content.ts +86 -0
  191. package/templates/module-monolith/src/features/starter/hooks/starter-preview-values.ts +42 -0
  192. package/templates/module-monolith/src/features/starter/hooks/use-starter-preview.dev.ts +17 -0
  193. package/templates/module-monolith/src/features/starter/hooks/use-starter-preview.ts +30 -0
  194. package/templates/module-monolith/src/features/starter/index.ts +6 -0
  195. package/templates/module-monolith/src/hubspot/default-data.ts +7 -0
  196. package/templates/module-monolith/src/hubspot/module-entry.tsx +9 -0
  197. package/templates/module-monolith/src/integrations/tanstack-router/router.tsx +25 -0
  198. package/templates/module-monolith/src/main.tsx +4 -3
  199. package/templates/module-monolith/src/portal/mount-dev-preview.tsx +8 -0
  200. package/templates/module-monolith/src/portal/mount-portal.tsx +12 -0
  201. package/templates/module-monolith/src/routeTree.gen.ts +77 -0
  202. package/templates/module-monolith/src/routes/__root.tsx +15 -0
  203. package/templates/module-monolith/src/routes/guide.tsx +7 -0
  204. package/templates/module-monolith/src/routes/index.tsx +7 -0
  205. package/templates/module-monolith/tsconfig.json +20 -0
  206. package/templates/module-monolith/vite.config.monolith.mjs +8 -0
  207. package/templates/module-monolith/woodscli.json +1 -0
  208. package/templates/shared/.env.dev.example +5 -0
  209. package/templates/shared/.env.prod.example +5 -0
  210. package/templates/shared/.env.stg.example +3 -0
  211. package/templates/shared/portal.cdn.json.example +8 -0
  212. package/templates/theme-starter/package.json +5 -2
  213. package/vite/vite.config.dev.mjs +9 -1
  214. package/vite/vite.config.monolith.mjs +22 -17
  215. package/dist/chunk-JDGIAYHX.js.map +0 -1
  216. package/dist/exec-K5C6LZP4.js +0 -10
  217. /package/dist/{exec-K5C6LZP4.js.map → exec-U2OJATR3.js.map} +0 -0
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runCommand,
4
+ runNodeScript,
5
+ runNodeScriptLongRunning
6
+ } from "./chunk-GTTIJVAR.js";
7
+ export {
8
+ runCommand,
9
+ runNodeScript,
10
+ runNodeScriptLongRunning
11
+ };
12
+ //# sourceMappingURL=exec-U2OJATR3.js.map
package/dist/index.d.ts CHANGED
@@ -52,13 +52,17 @@ declare const WoodsPortalHsSchema: z.ZodObject<{
52
52
  app: z.ZodDefault<z.ZodString>;
53
53
  vendor: z.ZodDefault<z.ZodString>;
54
54
  hubspotBootstrap: z.ZodDefault<z.ZodString>;
55
+ /** Monolith production IIFE entry (wp build --hubspot-only) */
56
+ module: z.ZodDefault<z.ZodString>;
55
57
  localDev: z.ZodDefault<z.ZodString>;
56
58
  }, "strip", z.ZodTypeAny, {
59
+ module: string;
57
60
  app: string;
58
61
  vendor: string;
59
62
  hubspotBootstrap: string;
60
63
  localDev: string;
61
64
  }, {
65
+ module?: string | undefined;
62
66
  app?: string | undefined;
63
67
  vendor?: string | undefined;
64
68
  hubspotBootstrap?: string | undefined;
@@ -87,10 +91,19 @@ declare const WoodsPortalHsSchema: z.ZodObject<{
87
91
  fieldsJson?: string | undefined;
88
92
  themeFieldsJson?: string | undefined;
89
93
  }>>;
94
+ scaffold: z.ZodOptional<z.ZodObject<{
95
+ /** hash = woodsportal-client-frontend (HubSpot CMS); browser = local clean URLs; none = single page */
96
+ router: z.ZodDefault<z.ZodEnum<["hash", "browser", "none"]>>;
97
+ }, "strip", z.ZodTypeAny, {
98
+ router: "none" | "hash" | "browser";
99
+ }, {
100
+ router?: "none" | "hash" | "browser" | undefined;
101
+ }>>;
90
102
  }, "strip", z.ZodTypeAny, {
91
103
  configVersion: number;
92
104
  projectType: "module-hybrid-cdn" | "module-monolith" | "theme-starter";
93
105
  entries: {
106
+ module: string;
94
107
  app: string;
95
108
  vendor: string;
96
109
  hubspotBootstrap: string;
@@ -121,6 +134,9 @@ declare const WoodsPortalHsSchema: z.ZodObject<{
121
134
  srcDir: string;
122
135
  themeJson: string;
123
136
  } | undefined;
137
+ scaffold?: {
138
+ router: "none" | "hash" | "browser";
139
+ } | undefined;
124
140
  }, {
125
141
  projectType: "module-hybrid-cdn" | "module-monolith" | "theme-starter";
126
142
  module: {
@@ -130,6 +146,7 @@ declare const WoodsPortalHsSchema: z.ZodObject<{
130
146
  };
131
147
  configVersion?: number | undefined;
132
148
  entries?: {
149
+ module?: string | undefined;
133
150
  app?: string | undefined;
134
151
  vendor?: string | undefined;
135
152
  hubspotBootstrap?: string | undefined;
@@ -155,6 +172,9 @@ declare const WoodsPortalHsSchema: z.ZodObject<{
155
172
  env?: {
156
173
  tiers?: ("dev" | "stg" | "prod")[] | undefined;
157
174
  } | undefined;
175
+ scaffold?: {
176
+ router?: "none" | "hash" | "browser" | undefined;
177
+ } | undefined;
158
178
  }>;
159
179
  type WoodsPortalHsConfig = z.infer<typeof WoodsPortalHsSchema>;
160
180
  declare const DEFAULT_WOODSPORTAL_HS: WoodsPortalHsConfig;
package/dist/index.js CHANGED
@@ -37,6 +37,8 @@ var WoodsPortalHsSchema = z.object({
37
37
  app: z.string().default("src/cdn/app-entry.ts"),
38
38
  vendor: z.string().default("src/cdn/vendor-entry.ts"),
39
39
  hubspotBootstrap: z.string().default("src/hubspot/bootstrap.ts"),
40
+ /** Monolith production IIFE entry (wp build --hubspot-only) */
41
+ module: z.string().default("src/hubspot/module-entry.tsx"),
40
42
  localDev: z.string().default("src/main.tsx")
41
43
  }).default({}),
42
44
  env: z.object({
@@ -47,7 +49,11 @@ var WoodsPortalHsSchema = z.object({
47
49
  adapter: z.string().default("./wphs.adapter.ts"),
48
50
  fieldsJson: z.string().default("public/fields.json"),
49
51
  themeFieldsJson: z.string().optional()
50
- }).default({})
52
+ }).default({}),
53
+ scaffold: z.object({
54
+ /** hash = woodsportal-client-frontend (HubSpot CMS); browser = local clean URLs; none = single page */
55
+ router: z.enum(["hash", "browser", "none"]).default("hash")
56
+ }).optional()
51
57
  });
52
58
  var DEFAULT_WOODSPORTAL_HS = {
53
59
  configVersion: CONFIG_VERSION,
@@ -66,6 +72,7 @@ var DEFAULT_WOODSPORTAL_HS = {
66
72
  app: "src/cdn/app-entry.ts",
67
73
  vendor: "src/cdn/vendor-entry.ts",
68
74
  hubspotBootstrap: "src/hubspot/bootstrap.ts",
75
+ module: "src/hubspot/module-entry.tsx",
69
76
  localDev: "src/main.tsx"
70
77
  },
71
78
  env: {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/schema/woodsportal-hs.ts","../src/lib/config.ts","../src/adapters/types.ts","../src/lib/generic-field-mapper.ts"],"sourcesContent":["import { z } from 'zod'\n\nexport const CONFIG_VERSION = 1\n\nexport const WPHS_WORKSPACE_DIR = '.wphs' as const\n\nexport const WoodsPortalHsSchema = z.object({\n configVersion: z.number().int().default(CONFIG_VERSION),\n projectType: z.enum(['module-hybrid-cdn', 'module-monolith', 'theme-starter']),\n module: z.object({\n name: z.string().min(1),\n uploadPath: z.string().min(1),\n outDir: z.string().default('dist/WoodsPortal.Module'),\n }),\n theme: z\n .object({\n name: z.string().min(1),\n uploadPath: z.string().min(1),\n srcDir: z.string().default('src/theme'),\n themeJson: z.string().default('src/theme/theme.json'),\n })\n .optional(),\n cdn: z\n .object({\n mode: z.enum(['jsdelivr', 'custom', 'local', 'none']).default('jsdelivr'),\n buildIdFile: z.string().default('.portal-build-id'),\n mirror: z.string().default('portal.cdn.json'),\n })\n .default({}),\n entries: z\n .object({\n app: z.string().default('src/cdn/app-entry.ts'),\n vendor: z.string().default('src/cdn/vendor-entry.ts'),\n hubspotBootstrap: z.string().default('src/hubspot/bootstrap.ts'),\n localDev: z.string().default('src/main.tsx'),\n })\n .default({}),\n env: z\n .object({\n tiers: z.array(z.enum(['dev', 'stg', 'prod'])).default(['dev', 'stg', 'prod']),\n })\n .default({}),\n dev: z\n .object({\n moduleConfigDir: z.literal(WPHS_WORKSPACE_DIR).default(WPHS_WORKSPACE_DIR),\n adapter: z.string().default('./wphs.adapter.ts'),\n fieldsJson: z.string().default('public/fields.json'),\n themeFieldsJson: z.string().optional(),\n })\n .default({}),\n})\n\nexport type WoodsPortalHsConfig = z.infer<typeof WoodsPortalHsSchema>\n\nexport const DEFAULT_WOODSPORTAL_HS: WoodsPortalHsConfig = {\n configVersion: CONFIG_VERSION,\n projectType: 'module-hybrid-cdn',\n module: {\n name: 'woodsportal-module',\n uploadPath: 'dw-modules/woodsportal-module.module',\n outDir: 'dist/WoodsPortal.Module',\n },\n cdn: {\n mode: 'jsdelivr',\n buildIdFile: '.portal-build-id',\n mirror: 'portal.cdn.json',\n },\n entries: {\n app: 'src/cdn/app-entry.ts',\n vendor: 'src/cdn/vendor-entry.ts',\n hubspotBootstrap: 'src/hubspot/bootstrap.ts',\n localDev: 'src/main.tsx',\n },\n env: {\n tiers: ['dev', 'stg', 'prod'],\n },\n dev: {\n moduleConfigDir: WPHS_WORKSPACE_DIR,\n adapter: './wphs.adapter.ts',\n fieldsJson: 'public/fields.json',\n },\n}\n\nexport function isModuleProject(config: WoodsPortalHsConfig): boolean {\n return config.projectType === 'module-hybrid-cdn' || config.projectType === 'module-monolith'\n}\n\nexport function isThemeProject(config: WoodsPortalHsConfig): boolean {\n return config.projectType === 'theme-starter'\n}\n\nexport function usesCdnPipeline(config: WoodsPortalHsConfig): boolean {\n return config.projectType === 'module-hybrid-cdn' && config.cdn.mode !== 'none'\n}\n","import { readFileSync } from 'node:fs'\n\nimport {\n resolveProjectConfig,\n WOODSCLI_CONFIG_FILE,\n} from './config-files.js'\nimport { WoodsPortalHsSchema, type WoodsPortalHsConfig } from '../schema/woodsportal-hs.js'\nimport { WpHsError, ExitCode } from './errors.js'\nimport { findProjectRoot } from './paths.js'\n\nexport type LoadedProjectConfig = {\n root: string\n config: WoodsPortalHsConfig\n configPath: string\n configFileName: string\n}\n\nexport function loadConfig(projectRoot?: string): LoadedProjectConfig {\n const root = projectRoot ?? findProjectRoot()\n const resolved = resolveProjectConfig(root)\n\n if (!resolved) {\n throw new WpHsError(`Missing ${WOODSCLI_CONFIG_FILE}`, {\n exitCode: ExitCode.USER_ERROR,\n remediation: 'Run wp init or wp adopt. Legacy woodsportal.hs.json → run wp migrate --yes.',\n })\n }\n\n let raw: unknown\n try {\n raw = JSON.parse(readFileSync(resolved.path, 'utf8'))\n } catch (cause) {\n throw new WpHsError(`Invalid JSON in ${resolved.fileName}`, {\n exitCode: ExitCode.USER_ERROR,\n cause,\n })\n }\n\n const parsed = WoodsPortalHsSchema.safeParse(raw)\n if (!parsed.success) {\n throw new WpHsError(`${resolved.fileName} validation failed: ${parsed.error.message}`, {\n exitCode: ExitCode.USER_ERROR,\n remediation: 'Run wp config validate for details.',\n })\n }\n\n return {\n root,\n config: parsed.data,\n configPath: resolved.path,\n configFileName: resolved.fileName,\n }\n}\n\nexport function validateConfigFile(projectRoot?: string): {\n valid: boolean\n errors: string[]\n config?: WoodsPortalHsConfig\n} {\n try {\n const { config } = loadConfig(projectRoot)\n return { valid: true, errors: [], config }\n } catch (error) {\n if (error instanceof WpHsError) {\n return { valid: false, errors: [error.message] }\n }\n return { valid: false, errors: [String(error)] }\n }\n}\n","/** Must match dev-module-config/schema/types.ts */\nexport const MODULE_CONFIG_CHANGED = 'woodsportal:module-config-changed'\n\nexport type ModuleConfigBuiltState = {\n built: Record<string, unknown>\n}\n\n/** WoodsCLI consumer hook — mount app + sync module-config live data. */\nexport interface HubSpotKitAdapter {\n syncLiveData(built: Record<string, unknown>): void\n getDefaultHubSpotData?(): Record<string, unknown>\n mountDevApp?(root: unknown): void | Promise<void>\n}\n\nexport function bindAdapterToModuleConfig(adapter: HubSpotKitAdapter): void {\n if (typeof globalThis.window === 'undefined') return\n globalThis.window.addEventListener(MODULE_CONFIG_CHANGED, (event: Event) => {\n const detail = (event as CustomEvent<ModuleConfigBuiltState>).detail\n adapter.syncLiveData(detail.built)\n })\n}\n\nexport function syncAdapterFromModuleState(\n adapter: HubSpotKitAdapter,\n state: ModuleConfigBuiltState,\n): void {\n adapter.syncLiveData(state.built)\n}\n\n/** Minimal adapter for greenfield templates without a full app. */\nexport const defaultHubSpotKitAdapter: HubSpotKitAdapter = {\n syncLiveData(built) {\n const w = globalThis as typeof globalThis & { __WPHS_DATA__?: Record<string, unknown> }\n w.__WPHS_DATA__ = built\n },\n getDefaultHubSpotData() {\n return {}\n },\n}\n","/**\n * Generic fields.json → flat runtime object (WoodsCLI default transform).\n */\nexport type GenericFieldDefinition = {\n name: string\n type: string\n default?: unknown\n children?: GenericFieldDefinition[]\n}\n\nexport type GenericFieldValues = Record<string, unknown>\n\nfunction flattenField(\n field: GenericFieldDefinition,\n values: GenericFieldValues,\n out: Record<string, unknown>,\n): void {\n const key = field.name\n const value = values[key]\n\n if (field.type === 'group' && field.children && value && typeof value === 'object') {\n const groupValues = value as GenericFieldValues\n for (const child of field.children) {\n flattenField(child, groupValues, out)\n }\n return\n }\n\n if (field.type === 'repeater' && Array.isArray(value)) {\n out[key] = value\n return\n }\n\n out[key] = value ?? field.default ?? null\n}\n\nexport function mapFieldsToRuntime(\n schema: GenericFieldDefinition[],\n values: GenericFieldValues,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const field of schema) {\n flattenField(field, values, out)\n }\n return out\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/schema/woodsportal-hs.ts","../src/lib/config.ts","../src/adapters/types.ts","../src/lib/generic-field-mapper.ts"],"sourcesContent":["import { z } from 'zod'\n\nexport const CONFIG_VERSION = 1\n\nexport const WPHS_WORKSPACE_DIR = '.wphs' as const\n\nexport const WoodsPortalHsSchema = z.object({\n configVersion: z.number().int().default(CONFIG_VERSION),\n projectType: z.enum(['module-hybrid-cdn', 'module-monolith', 'theme-starter']),\n module: z.object({\n name: z.string().min(1),\n uploadPath: z.string().min(1),\n outDir: z.string().default('dist/WoodsPortal.Module'),\n }),\n theme: z\n .object({\n name: z.string().min(1),\n uploadPath: z.string().min(1),\n srcDir: z.string().default('src/theme'),\n themeJson: z.string().default('src/theme/theme.json'),\n })\n .optional(),\n cdn: z\n .object({\n mode: z.enum(['jsdelivr', 'custom', 'local', 'none']).default('jsdelivr'),\n buildIdFile: z.string().default('.portal-build-id'),\n mirror: z.string().default('portal.cdn.json'),\n })\n .default({}),\n entries: z\n .object({\n app: z.string().default('src/cdn/app-entry.ts'),\n vendor: z.string().default('src/cdn/vendor-entry.ts'),\n hubspotBootstrap: z.string().default('src/hubspot/bootstrap.ts'),\n /** Monolith production IIFE entry (wp build --hubspot-only) */\n module: z.string().default('src/hubspot/module-entry.tsx'),\n localDev: z.string().default('src/main.tsx'),\n })\n .default({}),\n env: z\n .object({\n tiers: z.array(z.enum(['dev', 'stg', 'prod'])).default(['dev', 'stg', 'prod']),\n })\n .default({}),\n dev: z\n .object({\n moduleConfigDir: z.literal(WPHS_WORKSPACE_DIR).default(WPHS_WORKSPACE_DIR),\n adapter: z.string().default('./wphs.adapter.ts'),\n fieldsJson: z.string().default('public/fields.json'),\n themeFieldsJson: z.string().optional(),\n })\n .default({}),\n scaffold: z\n .object({\n /** hash = woodsportal-client-frontend (HubSpot CMS); browser = local clean URLs; none = single page */\n router: z.enum(['hash', 'browser', 'none']).default('hash'),\n })\n .optional(),\n})\n\nexport type WoodsPortalHsConfig = z.infer<typeof WoodsPortalHsSchema>\n\nexport const DEFAULT_WOODSPORTAL_HS: WoodsPortalHsConfig = {\n configVersion: CONFIG_VERSION,\n projectType: 'module-hybrid-cdn',\n module: {\n name: 'woodsportal-module',\n uploadPath: 'dw-modules/woodsportal-module.module',\n outDir: 'dist/WoodsPortal.Module',\n },\n cdn: {\n mode: 'jsdelivr',\n buildIdFile: '.portal-build-id',\n mirror: 'portal.cdn.json',\n },\n entries: {\n app: 'src/cdn/app-entry.ts',\n vendor: 'src/cdn/vendor-entry.ts',\n hubspotBootstrap: 'src/hubspot/bootstrap.ts',\n module: 'src/hubspot/module-entry.tsx',\n localDev: 'src/main.tsx',\n },\n env: {\n tiers: ['dev', 'stg', 'prod'],\n },\n dev: {\n moduleConfigDir: WPHS_WORKSPACE_DIR,\n adapter: './wphs.adapter.ts',\n fieldsJson: 'public/fields.json',\n },\n}\n\nexport function isModuleProject(config: WoodsPortalHsConfig): boolean {\n return config.projectType === 'module-hybrid-cdn' || config.projectType === 'module-monolith'\n}\n\nexport function isThemeProject(config: WoodsPortalHsConfig): boolean {\n return config.projectType === 'theme-starter'\n}\n\nexport function usesCdnPipeline(config: WoodsPortalHsConfig): boolean {\n return config.projectType === 'module-hybrid-cdn' && config.cdn.mode !== 'none'\n}\n","import { readFileSync } from 'node:fs'\n\nimport {\n resolveProjectConfig,\n WOODSCLI_CONFIG_FILE,\n} from './config-files.js'\nimport { WoodsPortalHsSchema, type WoodsPortalHsConfig } from '../schema/woodsportal-hs.js'\nimport { WpHsError, ExitCode } from './errors.js'\nimport { findProjectRoot } from './paths.js'\n\nexport type LoadedProjectConfig = {\n root: string\n config: WoodsPortalHsConfig\n configPath: string\n configFileName: string\n}\n\nexport function loadConfig(projectRoot?: string): LoadedProjectConfig {\n const root = projectRoot ?? findProjectRoot()\n const resolved = resolveProjectConfig(root)\n\n if (!resolved) {\n throw new WpHsError(`Missing ${WOODSCLI_CONFIG_FILE}`, {\n exitCode: ExitCode.USER_ERROR,\n remediation: 'Run wp init or wp adopt. Legacy woodsportal.hs.json → run wp migrate --yes.',\n })\n }\n\n let raw: unknown\n try {\n raw = JSON.parse(readFileSync(resolved.path, 'utf8'))\n } catch (cause) {\n throw new WpHsError(`Invalid JSON in ${resolved.fileName}`, {\n exitCode: ExitCode.USER_ERROR,\n cause,\n })\n }\n\n const parsed = WoodsPortalHsSchema.safeParse(raw)\n if (!parsed.success) {\n throw new WpHsError(`${resolved.fileName} validation failed: ${parsed.error.message}`, {\n exitCode: ExitCode.USER_ERROR,\n remediation: 'Run wp config validate for details.',\n })\n }\n\n return {\n root,\n config: parsed.data,\n configPath: resolved.path,\n configFileName: resolved.fileName,\n }\n}\n\nexport function validateConfigFile(projectRoot?: string): {\n valid: boolean\n errors: string[]\n config?: WoodsPortalHsConfig\n} {\n try {\n const { config } = loadConfig(projectRoot)\n return { valid: true, errors: [], config }\n } catch (error) {\n if (error instanceof WpHsError) {\n return { valid: false, errors: [error.message] }\n }\n return { valid: false, errors: [String(error)] }\n }\n}\n","/** Must match dev-module-config/schema/types.ts */\nexport const MODULE_CONFIG_CHANGED = 'woodsportal:module-config-changed'\n\nexport type ModuleConfigBuiltState = {\n built: Record<string, unknown>\n}\n\n/** WoodsCLI consumer hook — mount app + sync module-config live data. */\nexport interface HubSpotKitAdapter {\n syncLiveData(built: Record<string, unknown>): void\n getDefaultHubSpotData?(): Record<string, unknown>\n mountDevApp?(root: unknown): void | Promise<void>\n}\n\nexport function bindAdapterToModuleConfig(adapter: HubSpotKitAdapter): void {\n if (typeof globalThis.window === 'undefined') return\n globalThis.window.addEventListener(MODULE_CONFIG_CHANGED, (event: Event) => {\n const detail = (event as CustomEvent<ModuleConfigBuiltState>).detail\n adapter.syncLiveData(detail.built)\n })\n}\n\nexport function syncAdapterFromModuleState(\n adapter: HubSpotKitAdapter,\n state: ModuleConfigBuiltState,\n): void {\n adapter.syncLiveData(state.built)\n}\n\n/** Minimal adapter for greenfield templates without a full app. */\nexport const defaultHubSpotKitAdapter: HubSpotKitAdapter = {\n syncLiveData(built) {\n const w = globalThis as typeof globalThis & { __WPHS_DATA__?: Record<string, unknown> }\n w.__WPHS_DATA__ = built\n },\n getDefaultHubSpotData() {\n return {}\n },\n}\n","/**\n * Generic fields.json → flat runtime object (WoodsCLI default transform).\n */\nexport type GenericFieldDefinition = {\n name: string\n type: string\n default?: unknown\n children?: GenericFieldDefinition[]\n}\n\nexport type GenericFieldValues = Record<string, unknown>\n\nfunction flattenField(\n field: GenericFieldDefinition,\n values: GenericFieldValues,\n out: Record<string, unknown>,\n): void {\n const key = field.name\n const value = values[key]\n\n if (field.type === 'group' && field.children && value && typeof value === 'object') {\n const groupValues = value as GenericFieldValues\n for (const child of field.children) {\n flattenField(child, groupValues, out)\n }\n return\n }\n\n if (field.type === 'repeater' && Array.isArray(value)) {\n out[key] = value\n return\n }\n\n out[key] = value ?? field.default ?? null\n}\n\nexport function mapFieldsToRuntime(\n schema: GenericFieldDefinition[],\n values: GenericFieldValues,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const field of schema) {\n flattenField(field, values, out)\n }\n return out\n}\n"],"mappings":"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","names":[]}
@@ -1,9 +1,46 @@
1
- # HubSpot portal module (hybrid CDN)
1
+ # Getting started
2
2
 
3
- Scaffolded by `wp init --template module-hybrid-cdn`.
3
+ This project was scaffolded with **WoodsCLI** (`@woodsportal/hubspot-kit`).
4
+
5
+ ## Daily dev
4
6
 
5
7
  ```bash
6
- npm install
8
+ yarn install
7
9
  wp doctor
8
10
  wp dev
9
11
  ```
12
+
13
+ Open the local URL, then **Ctrl+Shift+Z** (Mac **⌃⇧Z**) to edit module fields. Click **Apply changes** to refresh the starter preview.
14
+
15
+ ## `src/` layout
16
+
17
+ | Path | Purpose |
18
+ |------|---------|
19
+ | `main.tsx` | Dev entry — loads styles and `mountDevPreview()` |
20
+ | `portal/` | Dev vs production mount (`mount-dev-preview`, `mount-portal`) |
21
+ | `routes/` | TanStack file routes — keep thin; import from `features/` |
22
+ | `features/starter/` | Demo home + developer guide (replace with your app) |
23
+ | `components/` | Shared layout and UI |
24
+ | `integrations/` | Router and third-party wiring |
25
+ | `cdn/` | Production vendor + app chunks (`wp build` / `wp deploy`) |
26
+ | `hubspot/` | HubSpot bootstrap, types, and dev fixtures |
27
+ | `config/` | Route constants and app config |
28
+ | `assets/css/` | Tailwind entry for local dev |
29
+
30
+ Kit runtime expects `src/defaultData.ts` (re-exports `hubspot/default-data.ts`).
31
+
32
+ ## Docs (in the kit package)
33
+
34
+ After `npm install`, see:
35
+
36
+ - `node_modules/@woodsportal/hubspot-kit/docs/CLI-REFERENCE.md`
37
+ - `node_modules/@woodsportal/hubspot-kit/docs/MODULES.md`
38
+ - `node_modules/@woodsportal/hubspot-kit/docs/HUBSPOT-CLI-SETUP.md`
39
+
40
+ ## HubSpot upload
41
+
42
+ ```bash
43
+ cp hubspot.config.yml.example hubspot.config.yml
44
+ # add portal + personal access key
45
+ wp upload
46
+ ```
@@ -1 +1,44 @@
1
- [{"name":"title","label":"Title","type":"text","default":"WoodsCLI module"}]
1
+ [
2
+ {
3
+ "name": "title",
4
+ "label": "Portal title",
5
+ "type": "text",
6
+ "tab": "CONTENT",
7
+ "default": "My HubSpot Portal"
8
+ },
9
+ {
10
+ "name": "tagline",
11
+ "label": "Tagline",
12
+ "type": "text",
13
+ "tab": "CONTENT",
14
+ "help_text": "Subtitle shown under the hero title in the local dev preview.",
15
+ "default": "Customize this preview with Ctrl+Shift+Z, then replace StarterPage with your portal app."
16
+ },
17
+ {
18
+ "name": "show_feature_cards",
19
+ "label": "Show feature cards",
20
+ "type": "boolean",
21
+ "display": "toggle",
22
+ "tab": "CONTENT",
23
+ "default": true
24
+ },
25
+ {
26
+ "name": "accent_color",
27
+ "label": "Accent color",
28
+ "type": "color",
29
+ "tab": "STYLE",
30
+ "default": { "color": "#0091ae", "opacity": 100 }
31
+ },
32
+ {
33
+ "name": "hero_style",
34
+ "label": "Hero style",
35
+ "type": "choice",
36
+ "tab": "STYLE",
37
+ "choices": [
38
+ ["gradient", "Gradient"],
39
+ ["minimal", "Minimal"],
40
+ ["bold", "Bold"]
41
+ ],
42
+ "default": "gradient"
43
+ }
44
+ ]
@@ -0,0 +1,8 @@
1
+ import { RouterProvider } from '@tanstack/react-router'
2
+
3
+ import { router } from '@/integrations/tanstack-router/router'
4
+
5
+ /** Root app shell — same pattern as woodsportal-client-frontend `App`. */
6
+ export function App() {
7
+ return <RouterProvider router={router} />
8
+ }
@@ -1,3 +1,8 @@
1
1
  @import 'tailwindcss';
2
2
 
3
- @source '../../.wphs/**/*.{js,jsx,ts,tsx}';
3
+ /* Kit dev-module-config — paths relative to this file (src/assets/css/) */
4
+ @source '../../../.wphs/.kit-module-config/**/*.{ts,tsx}';
5
+ @source '../../../.wphs/.kit-bootstrap.tsx';
6
+ @source '../../../.wphs/.kit-bootstrap-greenfield.tsx';
7
+ @source '../../../src/**/*.{ts,tsx}';
8
+ @source '../../../node_modules/@woodsportal/hubspot-kit/src/dev-module-config/**/*.{ts,tsx}';
@@ -0,0 +1,32 @@
1
+ /**
2
+ * CDN application chunk — mounts after vendor.js (HubSpot require_js order).
3
+ */
4
+ import { mountPortal } from '@/portal/mount-portal'
5
+
6
+ declare const __PORTAL_BUILD_ID__: string | undefined
7
+
8
+ declare global {
9
+ interface Window {
10
+ WoodsPortal?: {
11
+ mount: typeof mountPortal
12
+ version: string
13
+ }
14
+ }
15
+ }
16
+
17
+ const buildId =
18
+ typeof __PORTAL_BUILD_ID__ !== 'undefined' ? String(__PORTAL_BUILD_ID__) : 'dev'
19
+
20
+ window.WoodsPortal = {
21
+ mount: mountPortal,
22
+ version: buildId,
23
+ }
24
+
25
+ async function autoMount() {
26
+ if (!document.getElementById('app')) {
27
+ return
28
+ }
29
+ await mountPortal()
30
+ }
31
+
32
+ void autoMount()
@@ -0,0 +1,28 @@
1
+ /**
2
+ * CDN vendor chunk — React + ReactDOM on `window` for the app IIFE externals.
3
+ */
4
+ import React from 'react'
5
+ import * as ReactDOM from 'react-dom'
6
+ import * as ReactDOMClient from 'react-dom/client'
7
+ import * as jsxRuntime from 'react/jsx-runtime'
8
+ import * as jsxDevRuntime from 'react/jsx-dev-runtime'
9
+
10
+ declare global {
11
+ interface Window {
12
+ WoodsPortalVendor?: {
13
+ React: typeof React
14
+ ReactDOM: typeof ReactDOM
15
+ ReactDOMClient: typeof ReactDOMClient
16
+ jsxRuntime: typeof jsxRuntime
17
+ jsxDevRuntime: typeof jsxDevRuntime
18
+ }
19
+ }
20
+ }
21
+
22
+ window.WoodsPortalVendor = {
23
+ React,
24
+ ReactDOM,
25
+ ReactDOMClient,
26
+ jsxRuntime,
27
+ jsxDevRuntime,
28
+ }
@@ -0,0 +1,20 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ export function SectionCard({
4
+ title,
5
+ children,
6
+ className = '',
7
+ }: {
8
+ title: string
9
+ children: ReactNode
10
+ className?: string
11
+ }) {
12
+ return (
13
+ <section
14
+ className={`rounded-lg border border-[#cbd6e2] bg-white p-6 shadow-sm ${className}`}
15
+ >
16
+ <h2 className="text-base font-semibold text-[#33475b]">{title}</h2>
17
+ <div className="mt-4">{children}</div>
18
+ </section>
19
+ )
20
+ }
@@ -0,0 +1,28 @@
1
+ import { Link } from '@tanstack/react-router'
2
+
3
+ import { ROUTES } from '@/config/routes'
4
+
5
+ export function StarterNav() {
6
+ return (
7
+ <header className="border-b border-[#cbd6e2] bg-white/90 backdrop-blur">
8
+ <div className="mx-auto flex max-w-4xl items-center justify-between gap-4 px-6 py-3 sm:px-10">
9
+ <span className="text-sm font-semibold text-[#0091ae]">WoodsCLI starter</span>
10
+ <nav className="flex gap-4 text-sm font-medium">
11
+ <Link
12
+ to={ROUTES.home}
13
+ className="text-[#516f90] hover:text-[#0091ae] [&.active]:text-[#0091ae]"
14
+ activeOptions={{ exact: true }}
15
+ >
16
+ Home
17
+ </Link>
18
+ <Link
19
+ to={ROUTES.guide}
20
+ className="text-[#516f90] hover:text-[#0091ae] [&.active]:text-[#0091ae]"
21
+ >
22
+ Developer guide
23
+ </Link>
24
+ </nav>
25
+ </div>
26
+ </header>
27
+ )
28
+ }
@@ -0,0 +1,18 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ import { StarterNav } from './starter-nav'
4
+
5
+ export function StarterShell({
6
+ children,
7
+ showNav = true,
8
+ }: {
9
+ children: ReactNode
10
+ showNav?: boolean
11
+ }) {
12
+ return (
13
+ <div className="min-h-screen bg-[#eaf0f6] text-[#33475b]">
14
+ {showNav ? <StarterNav /> : null}
15
+ {children}
16
+ </div>
17
+ )
18
+ }
@@ -0,0 +1,4 @@
1
+ /** Router history — set by `wp init`. Hash matches woodsportal-client-frontend (HubSpot CMS embeds). */
2
+ export type RouterHistoryMode = 'hash' | 'browser'
3
+
4
+ export const ROUTER_HISTORY: RouterHistoryMode = 'hash'
@@ -0,0 +1,5 @@
1
+ /** Route paths — keep in sync with `src/routes/` file routes. */
2
+ export const ROUTES = {
3
+ home: '/',
4
+ guide: '/guide',
5
+ } as const
@@ -1,7 +1,2 @@
1
- /** Local dev fixtures for module-config runtime (greenfield wp init). */
2
- export const defaultData = {
3
- dataSourceSet: [],
4
- hubId: 'dev',
5
- userData: {},
6
- objectList: [] as unknown[],
7
- }
1
+ /** Kit runtime imports `@/defaultData` — re-export from the hubspot slice. */
2
+ export { devDefaultData as defaultData } from './hubspot/default-data'
@@ -0,0 +1,93 @@
1
+ import { SectionCard } from '@/components/layout/section-card'
2
+
3
+ import {
4
+ DEV_WORKFLOW,
5
+ PROJECT_LAYOUT,
6
+ WOODSCLI_ABOUT,
7
+ WOODSCLI_COMMANDS,
8
+ } from '../content/woodscli-guide-content'
9
+
10
+ export function StarterGuidePage() {
11
+ return (
12
+ <main className="mx-auto max-w-4xl space-y-8 px-6 py-10 sm:px-10">
13
+ <div>
14
+ <h1 className="text-2xl font-semibold tracking-tight">Developer guide</h1>
15
+ <p className="mt-2 text-sm leading-relaxed text-[#516f90]">
16
+ Commands, project layout, and workflow for this WoodsCLI module. Routes live in{' '}
17
+ <code className="font-mono text-xs">src/routes/</code> and delegate to{' '}
18
+ <code className="font-mono text-xs">src/features/</code>.
19
+ </p>
20
+ </div>
21
+
22
+ <SectionCard title="About WoodsCLI">
23
+ <p className="text-sm leading-relaxed text-[#516f90]">{WOODSCLI_ABOUT.tagline}</p>
24
+ <p className="mt-3 text-sm leading-relaxed text-[#516f90]">{WOODSCLI_ABOUT.docsHint}</p>
25
+ </SectionCard>
26
+
27
+ <SectionCard title="Essential commands">
28
+ <ul className="divide-y divide-[#eaf0f6]">
29
+ {WOODSCLI_COMMANDS.map(({ cmd, desc }) => (
30
+ <li key={cmd} className="flex flex-col gap-1 py-3 first:pt-0 last:pb-0 sm:flex-row sm:gap-4">
31
+ <code className="shrink-0 rounded bg-[#eaf0f6] px-2 py-1 font-mono text-xs text-[#0091ae]">
32
+ {cmd}
33
+ </code>
34
+ <span className="text-sm leading-relaxed text-[#516f90]">{desc}</span>
35
+ </li>
36
+ ))}
37
+ </ul>
38
+ </SectionCard>
39
+
40
+ <SectionCard title="Project layout">
41
+ <ul className="space-y-3">
42
+ {PROJECT_LAYOUT.map(({ path, note }) => (
43
+ <li key={path} className="text-sm leading-relaxed">
44
+ <code className="rounded bg-[#eaf0f6] px-1.5 py-0.5 font-mono text-xs text-[#33475b]">
45
+ {path}
46
+ </code>
47
+ <span className="mt-1 block text-[#516f90] sm:mt-0 sm:inline sm:before:content-['—_']">
48
+ {note}
49
+ </span>
50
+ </li>
51
+ ))}
52
+ </ul>
53
+ </SectionCard>
54
+
55
+ <SectionCard title="Developer workflow">
56
+ <ol className="space-y-4">
57
+ {DEV_WORKFLOW.map(({ title: stepTitle, body }) => (
58
+ <li key={stepTitle}>
59
+ <h3 className="text-sm font-medium text-[#33475b]">{stepTitle}</h3>
60
+ <p className="mt-1 text-sm leading-relaxed text-[#516f90]">{body}</p>
61
+ </li>
62
+ ))}
63
+ </ol>
64
+ </SectionCard>
65
+
66
+ <SectionCard title="Routing">
67
+ <p className="text-sm leading-relaxed text-[#516f90]">
68
+ This starter uses the same TanStack Router setup as{' '}
69
+ <code className="font-mono text-xs">woodsportal-client-frontend</code>: file routes under{' '}
70
+ <code className="font-mono text-xs">src/routes/</code>,{' '}
71
+ <code className="font-mono text-xs">src/app.tsx</code> with{' '}
72
+ <code className="font-mono text-xs">RouterProvider</code>, and{' '}
73
+ <strong>hash history</strong> by default (
74
+ <code className="font-mono text-xs">src/config/router.ts</code>) so routes work inside
75
+ HubSpot CMS embeds. Run <code className="font-mono text-xs">wp init</code> again on a new
76
+ folder to choose browser history or no router. Add{' '}
77
+ <code className="font-mono text-xs">src/routes/about.tsx</code> and restart{' '}
78
+ <code className="font-mono text-xs">wp dev</code> to regenerate{' '}
79
+ <code className="font-mono text-xs">routeTree.gen.ts</code>.
80
+ </p>
81
+ </SectionCard>
82
+
83
+ <SectionCard title="Customize this preview">
84
+ <p className="text-sm leading-relaxed text-[#516f90]">
85
+ Edit <code className="font-mono text-xs">public/fields.json</code> to add module fields,
86
+ then adjust <code className="font-mono text-xs">src/features/starter/</code> to bind them.
87
+ Replace the starter feature with your own under{' '}
88
+ <code className="font-mono text-xs">src/features/</code> when you outgrow the demo.
89
+ </p>
90
+ </SectionCard>
91
+ </main>
92
+ )
93
+ }
@@ -0,0 +1,21 @@
1
+ import { Link } from '@tanstack/react-router'
2
+
3
+ import { ROUTES } from '@/config/routes'
4
+
5
+ import { StarterHome } from './starter-home'
6
+
7
+ export function StarterHomePage() {
8
+ return (
9
+ <>
10
+ <StarterHome />
11
+ <div className="mx-auto max-w-4xl px-6 pb-10 sm:px-10">
12
+ <Link
13
+ to={ROUTES.guide}
14
+ className="inline-flex items-center rounded-md bg-[#0091ae] px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-[#007a8c]"
15
+ >
16
+ Open developer guide →
17
+ </Link>
18
+ </div>
19
+ </>
20
+ )
21
+ }
@@ -0,0 +1,52 @@
1
+ import { useStarterPreview } from '../hooks/use-starter-preview'
2
+ import { STARTER_FEATURES, WOODSCLI_ABOUT } from '../content/woodscli-guide-content'
3
+
4
+ function heroClass(style: ReturnType<typeof useStarterPreview>['heroStyle']): string {
5
+ if (style === 'minimal') {
6
+ return 'bg-[var(--mc-bg-shell,#f5f8fa)] text-[var(--mc-text,#33475b)] border-b border-[var(--mc-border,#cbd6e2)]'
7
+ }
8
+ if (style === 'bold') {
9
+ return 'bg-[#1c1c1f] text-white'
10
+ }
11
+ return 'bg-gradient-to-br from-[var(--starter-accent,#0091ae)] to-[#ff7a59] text-white'
12
+ }
13
+
14
+ export function StarterHome() {
15
+ const { title, tagline, showFeatureCards, accentColor, heroStyle } = useStarterPreview()
16
+
17
+ return (
18
+ <>
19
+ <div style={{ ['--starter-accent' as string]: accentColor }}>
20
+ <section className={`px-6 py-14 sm:px-10 sm:py-16 ${heroClass(heroStyle)}`}>
21
+ <div className="mx-auto max-w-4xl">
22
+ <p className="mb-2 text-xs font-semibold uppercase tracking-widest opacity-80">
23
+ {WOODSCLI_ABOUT.package} · local dev
24
+ </p>
25
+ <h1 className="text-3xl font-semibold tracking-tight sm:text-4xl">{title}</h1>
26
+ <p className="mt-4 max-w-2xl text-base leading-relaxed opacity-90 sm:text-lg">{tagline}</p>
27
+ <p className="mt-6 text-sm opacity-75">
28
+ Module config overlay:{' '}
29
+ <kbd className="rounded bg-black/10 px-1.5 py-0.5 font-mono text-xs">Ctrl+Shift+Z</kbd>
30
+ {' · '}
31
+ Mac: <kbd className="rounded bg-black/10 px-1.5 py-0.5 font-mono text-xs">⌃⇧Z</kbd>
32
+ </p>
33
+ </div>
34
+ </section>
35
+ </div>
36
+
37
+ {showFeatureCards ? (
38
+ <div className="mx-auto grid max-w-4xl gap-4 px-6 py-10 sm:grid-cols-3 sm:px-10">
39
+ {STARTER_FEATURES.map((feature) => (
40
+ <article
41
+ key={feature.title}
42
+ className="rounded-lg border border-[#cbd6e2] bg-white p-5 shadow-sm"
43
+ >
44
+ <h2 className="text-sm font-semibold text-[#0091ae]">{feature.title}</h2>
45
+ <p className="mt-2 text-sm leading-relaxed text-[#516f90]">{feature.body}</p>
46
+ </article>
47
+ ))}
48
+ </div>
49
+ ) : null}
50
+ </>
51
+ )
52
+ }
@@ -0,0 +1,17 @@
1
+ import { StarterShell } from '@/components/layout/starter-shell'
2
+
3
+ import { StarterGuidePage } from './starter-guide-page'
4
+ import { StarterHome } from './starter-home'
5
+
6
+ /** Single-page starter when `wp init --router none` (no TanStack Router). */
7
+ export function StarterSinglePage() {
8
+ return (
9
+ <StarterShell showNav={false}>
10
+ <StarterHome />
11
+ <div className="mx-auto max-w-4xl px-6 sm:px-10">
12
+ <hr className="my-10 border-[#cbd6e2]" />
13
+ </div>
14
+ <StarterGuidePage />
15
+ </StarterShell>
16
+ )
17
+ }