@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,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
+ }
@@ -0,0 +1,86 @@
1
+ /** Static WoodsCLI developer reference — shown on the wp init starter pages. */
2
+
3
+ export const WOODSCLI_COMMANDS = [
4
+ {
5
+ cmd: 'wp dev',
6
+ desc: 'Local Vite preview + module-config overlay (this page). Alias: wp local.',
7
+ },
8
+ {
9
+ cmd: 'wp doctor',
10
+ desc: 'Validate Node, deps, .wphs/, index.html, fields.json, and HubSpot CLI setup.',
11
+ },
12
+ {
13
+ cmd: 'wp build --tier prod',
14
+ desc: 'Production build (CDN bundle and/or HubSpot module, per projectType).',
15
+ },
16
+ {
17
+ cmd: 'wp deploy --tier prod',
18
+ desc: 'Build, publish CDN artifacts, and upload the module to HubSpot.',
19
+ },
20
+ {
21
+ cmd: 'wp adopt',
22
+ desc: 'Add woodscli.json + .wphs/ to an existing module or theme repo.',
23
+ },
24
+ {
25
+ cmd: 'wp migrate --yes',
26
+ desc: 'Move legacy .dev/ or woodsportal.hs.json to .wphs/ + woodscli.json.',
27
+ },
28
+ ] as const
29
+
30
+ export const PROJECT_LAYOUT = [
31
+ { path: 'woodscli.json', note: 'Project config — module paths, entries, dev.adapter' },
32
+ { path: 'public/fields.json', note: 'HubSpot module field schema (Content / Styles tabs)' },
33
+ { path: '.wphs/', note: 'Local workspace — config, fixtures, kit symlinks (gitignore most)' },
34
+ { path: 'wphs.adapter.ts', note: 'Syncs module field values → window.__WPHS_DATA__ in dev' },
35
+ { path: 'src/config/router.ts', note: 'Hash vs browser history — set by wp init (hash = client-frontend)' },
36
+ { path: 'src/routes/', note: 'TanStack Router file routes (thin — delegate to features/)' },
37
+ { path: 'src/app.tsx', note: 'RouterProvider shell — same role as woodsportal-client-frontend App' },
38
+ { path: 'src/features/starter/', note: 'Starter demo UI — replace with your portal features' },
39
+ { path: 'src/portal/', note: 'Dev vs production mount (mount-dev-preview, mount-portal)' },
40
+ { path: 'src/integrations/', note: 'Third-party wiring (TanStack Router, SDK, etc.)' },
41
+ { path: 'src/hubspot/', note: 'HubSpot runtime types and dev fixtures' },
42
+ { path: 'src/main.tsx', note: 'Dev entry — mounts overlay + routed preview' },
43
+ { path: 'hubspot.config.yml', note: 'HubSpot CLI portal + PAT (copy from .example)' },
44
+ ] as const
45
+
46
+ export const DEV_WORKFLOW = [
47
+ {
48
+ title: '1 · Edit module fields',
49
+ body: 'Press Ctrl+Shift+Z (Mac ⌃⇧Z) to open the HubSpot-style Content / Styles panel. Changes apply to the preview after you click Apply changes.',
50
+ },
51
+ {
52
+ title: '2 · Iterate in src/',
53
+ body: 'Add features under src/features/, shared UI under src/components/, and wire HubSpot data via wphs.adapter.ts and public/fields.json.',
54
+ },
55
+ {
56
+ title: '3 · Connect HubSpot',
57
+ body: 'Copy hubspot.config.yml.example → hubspot.config.yml, add a personal access key, then wp upload or wp watch for CMS sync.',
58
+ },
59
+ {
60
+ title: '4 · Ship',
61
+ body: 'Hybrid CDN: wp deploy. Monolith: wp build --hubspot-only then wp upload. Advanced apps: wp adopt on woodsportal-client-frontend.',
62
+ },
63
+ ] as const
64
+
65
+ export const WOODSCLI_ABOUT = {
66
+ package: '@woodsportal/hubspot-kit',
67
+ tagline:
68
+ 'WoodsCLI is WoodsPortal’s dev kit for HubSpot CMS modules and themes — Vite local preview, fields.json editing, CDN + module build pipelines, and HubSpot CLI integration.',
69
+ docsHint:
70
+ 'Full command list and guides ship in node_modules/@woodsportal/hubspot-kit/docs/ (CLI-REFERENCE.md, MODULES.md, HUBSPOT-CLI-SETUP.md).',
71
+ } as const
72
+
73
+ export const STARTER_FEATURES = [
74
+ {
75
+ title: 'Module fields',
76
+ body: 'Content and Styles tabs mirror HubSpot’s module editor. Apply changes to refresh this preview.',
77
+ },
78
+ {
79
+ title: 'Local persistence',
80
+ body: 'Saved values land in .wphs/config/module-config.local.json — safe to gitignore.',
81
+ },
82
+ {
83
+ title: 'Ship when ready',
84
+ body: 'Run wp build and wp deploy, or wp adopt to merge into the full WoodsPortal client app.',
85
+ },
86
+ ] as const
@@ -0,0 +1,47 @@
1
+ import { useEffect, useState } from 'react'
2
+
3
+ import { MODULE_CONFIG_CHANGED } from '@/dev/module-config/schema/types'
4
+ import { getModuleFieldValues } from '@/dev/module-config/runtime/module-config-runtime'
5
+
6
+ export type StarterPreviewValues = {
7
+ title: string
8
+ tagline: string
9
+ showFeatureCards: boolean
10
+ accentColor: string
11
+ heroStyle: 'gradient' | 'minimal' | 'bold'
12
+ }
13
+
14
+ function normalizeHeroStyle(raw: unknown): StarterPreviewValues['heroStyle'] {
15
+ const key = String(raw ?? 'gradient').toLowerCase()
16
+ if (key === 'minimal' || key === 'bold') return key
17
+ return 'gradient'
18
+ }
19
+
20
+ function readPreviewValues(): StarterPreviewValues {
21
+ const module = getModuleFieldValues()
22
+ const accent = module.accent_color as { color?: string } | undefined
23
+
24
+ return {
25
+ title: String(module.title ?? 'My HubSpot Portal'),
26
+ tagline: String(
27
+ module.tagline ??
28
+ 'Customize this preview with Ctrl+Shift+Z, then replace this page with your portal app.',
29
+ ),
30
+ showFeatureCards: module.show_feature_cards !== false,
31
+ accentColor: accent?.color ?? '#0091ae',
32
+ heroStyle: normalizeHeroStyle(module.hero_style),
33
+ }
34
+ }
35
+
36
+ /** Live module field values for the wp init starter preview. */
37
+ export function useStarterPreview(): StarterPreviewValues {
38
+ const [values, setValues] = useState(readPreviewValues)
39
+
40
+ useEffect(() => {
41
+ const refresh = () => setValues(readPreviewValues())
42
+ window.addEventListener(MODULE_CONFIG_CHANGED, refresh)
43
+ return () => window.removeEventListener(MODULE_CONFIG_CHANGED, refresh)
44
+ }, [])
45
+
46
+ return values
47
+ }
@@ -0,0 +1,6 @@
1
+ export { StarterHomePage } from './components/starter-home-page'
2
+ export { StarterGuidePage } from './components/starter-guide-page'
3
+ export { StarterSinglePage } from './components/starter-single-page'
4
+ export { StarterHome } from './components/starter-home'
5
+ export { useStarterPreview } from './hooks/use-starter-preview'
6
+ export type { StarterPreviewValues } from './hooks/use-starter-preview'
@@ -0,0 +1,7 @@
1
+ /** Local dev fixtures merged by the module-config runtime in `wp dev`. */
2
+ export const devDefaultData = {
3
+ dataSourceSet: [],
4
+ hubId: 'dev',
5
+ userData: {},
6
+ objectList: [] as unknown[],
7
+ }
@@ -0,0 +1,17 @@
1
+ /** Runtime contract for window.hubSpotData (greenfield stub). */
2
+ export type HubSpotPortalData = {
3
+ dataSourceSet?: unknown
4
+ hubId?: string | number
5
+ apiBaseUrlOption?: string
6
+ userData?: unknown
7
+ objectList?: unknown[]
8
+ isInEditor?: boolean
9
+ }
10
+
11
+ declare global {
12
+ interface Window {
13
+ hubSpotData?: HubSpotPortalData
14
+ }
15
+ }
16
+
17
+ export {}
@@ -0,0 +1,25 @@
1
+ import {
2
+ createBrowserHistory,
3
+ createHashHistory,
4
+ createRouter,
5
+ } from '@tanstack/react-router'
6
+
7
+ import { ROUTER_HISTORY } from '@/config/router'
8
+ import { routeTree } from '@/routeTree.gen'
9
+
10
+ const history = ROUTER_HISTORY === 'hash' ? createHashHistory() : createBrowserHistory()
11
+
12
+ export const router = createRouter({
13
+ routeTree,
14
+ history,
15
+ defaultPreload: 'intent',
16
+ scrollRestoration: true,
17
+ defaultStructuralSharing: true,
18
+ defaultPreloadStaleTime: 0,
19
+ })
20
+
21
+ declare module '@tanstack/react-router' {
22
+ interface Register {
23
+ router: typeof router
24
+ }
25
+ }
@@ -0,0 +1,5 @@
1
+ import '@/assets/css/main.dev.css'
2
+
3
+ import { mountDevPreview } from '@/portal/mount-dev-preview'
4
+
5
+ void mountDevPreview()
@@ -0,0 +1,8 @@
1
+ import { mountGreenfieldDev } from '../../.wphs/.kit-bootstrap-greenfield.tsx'
2
+
3
+ import { App } from '@/app'
4
+
5
+ /** Local dev: module-config overlay + routed starter preview. */
6
+ export async function mountDevPreview() {
7
+ await mountGreenfieldDev({ preview: <App /> })
8
+ }
@@ -0,0 +1,12 @@
1
+ import ReactDOM from 'react-dom/client'
2
+
3
+ import { App } from '@/app'
4
+
5
+ /** Production mount — same App shell as woodsportal-client-frontend CDN app chunk. */
6
+ export async function mountPortal() {
7
+ const root = document.getElementById('app')
8
+ if (!root) {
9
+ return
10
+ }
11
+ ReactDOM.createRoot(root).render(<App />)
12
+ }
@@ -0,0 +1,77 @@
1
+ /* eslint-disable */
2
+
3
+ // @ts-nocheck
4
+
5
+ // noinspection JSUnusedGlobalSymbols
6
+
7
+ // This file was automatically generated by TanStack Router.
8
+ // You should NOT make any changes in this file as it will be overwritten.
9
+ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
10
+
11
+ import { Route as rootRouteImport } from './routes/__root'
12
+ import { Route as GuideRouteImport } from './routes/guide'
13
+ import { Route as IndexRouteImport } from './routes/index'
14
+
15
+ const GuideRoute = GuideRouteImport.update({
16
+ id: '/guide',
17
+ path: '/guide',
18
+ getParentRoute: () => rootRouteImport,
19
+ } as any)
20
+ const IndexRoute = IndexRouteImport.update({
21
+ id: '/',
22
+ path: '/',
23
+ getParentRoute: () => rootRouteImport,
24
+ } as any)
25
+
26
+ export interface FileRoutesByFullPath {
27
+ '/': typeof IndexRoute
28
+ '/guide': typeof GuideRoute
29
+ }
30
+ export interface FileRoutesByTo {
31
+ '/': typeof IndexRoute
32
+ '/guide': typeof GuideRoute
33
+ }
34
+ export interface FileRoutesById {
35
+ __root__: typeof rootRouteImport
36
+ '/': typeof IndexRoute
37
+ '/guide': typeof GuideRoute
38
+ }
39
+ export interface FileRouteTypes {
40
+ fileRoutesByFullPath: FileRoutesByFullPath
41
+ fullPaths: '/' | '/guide'
42
+ fileRoutesByTo: FileRoutesByTo
43
+ to: '/' | '/guide'
44
+ id: '__root__' | '/' | '/guide'
45
+ fileRoutesById: FileRoutesById
46
+ }
47
+ export interface RootRouteChildren {
48
+ IndexRoute: typeof IndexRoute
49
+ GuideRoute: typeof GuideRoute
50
+ }
51
+
52
+ declare module '@tanstack/react-router' {
53
+ interface FileRoutesByPath {
54
+ '/guide': {
55
+ id: '/guide'
56
+ path: '/guide'
57
+ fullPath: '/guide'
58
+ preLoaderRoute: typeof GuideRouteImport
59
+ parentRoute: typeof rootRouteImport
60
+ }
61
+ '/': {
62
+ id: '/'
63
+ path: '/'
64
+ fullPath: '/'
65
+ preLoaderRoute: typeof IndexRouteImport
66
+ parentRoute: typeof rootRouteImport
67
+ }
68
+ }
69
+ }
70
+
71
+ const rootRouteChildren: RootRouteChildren = {
72
+ IndexRoute: IndexRoute,
73
+ GuideRoute: GuideRoute,
74
+ }
75
+ export const routeTree = rootRouteImport
76
+ ._addFileChildren(rootRouteChildren)
77
+ ._addFileTypes<FileRouteTypes>()
@@ -0,0 +1,15 @@
1
+ import { Outlet, createRootRoute } from '@tanstack/react-router'
2
+
3
+ import { StarterShell } from '@/components/layout/starter-shell'
4
+
5
+ function RootLayout() {
6
+ return (
7
+ <StarterShell>
8
+ <Outlet />
9
+ </StarterShell>
10
+ )
11
+ }
12
+
13
+ export const Route = createRootRoute({
14
+ component: RootLayout,
15
+ })
@@ -0,0 +1,7 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+
3
+ import { StarterGuidePage } from '@/features/starter'
4
+
5
+ export const Route = createFileRoute('/guide')({
6
+ component: StarterGuidePage,
7
+ })
@@ -0,0 +1,7 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+
3
+ import { StarterHomePage } from '@/features/starter'
4
+
5
+ export const Route = createFileRoute('/')({
6
+ component: StarterHomePage,
7
+ })
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "jsx": "react-jsx",
5
+ "module": "ESNext",
6
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
7
+ "types": ["vite/client"],
8
+ "moduleResolution": "bundler",
9
+ "allowImportingTsExtensions": true,
10
+ "verbatimModuleSyntax": true,
11
+ "noEmit": true,
12
+ "skipLibCheck": true,
13
+ "strict": true,
14
+ "baseUrl": ".",
15
+ "paths": {
16
+ "@/*": ["./src/*"]
17
+ }
18
+ },
19
+ "include": ["src/**/*.ts", "src/**/*.tsx", "wphs.adapter.ts"]
20
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@woodsportal/hubspot-kit",
3
- "version": "1.0.4",
3
+ "version": "1.0.26",
4
4
  "description": "WoodsCLI — HubSpot CMS dev kit by WoodsPortal. wp CLI, Vite presets, module-config overlay, and theme/module build pipelines.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -14,6 +14,9 @@
14
14
  "vite",
15
15
  "templates",
16
16
  "examples",
17
+ "src/dev",
18
+ "src/dev-module-config",
19
+ "src/routing",
17
20
  "README.md",
18
21
  "CHANGELOG.md",
19
22
  "SECURITY.md",
@@ -6,10 +6,13 @@ import { existsSync, readFileSync, readdirSync } from 'node:fs'
6
6
  import { resolve, dirname } from 'node:path'
7
7
  import { fileURLToPath } from 'node:url'
8
8
  import { findProjectRoot } from './project-root.mjs'
9
+ import { moduleOutDir, readWoodsportalConfig } from './read-woodsportal-config.mjs'
9
10
  import { gzipSync } from 'node:zlib'
10
11
 
11
12
  const root = findProjectRoot()
12
- const embedMode = process.env.VITE_PORTAL_EMBED_MODE ?? 'cdn'
13
+ const { config } = readWoodsportalConfig(root)
14
+ const isMonolith = config?.projectType === 'module-monolith'
15
+ const embedMode = isMonolith ? 'monolith' : (process.env.VITE_PORTAL_EMBED_MODE ?? 'cdn')
13
16
 
14
17
  const HUBSPOT_FILE_LIMIT = Number(process.env.HUBSPOT_MODULE_RAW_LIMIT_BYTES ?? 1.5 * 1024 * 1024)
15
18
  const BOOTSTRAP_RAW_LIMIT = Number(process.env.HUBSPOT_BOOTSTRAP_RAW_LIMIT_BYTES ?? 200 * 1024)
@@ -35,7 +38,7 @@ function checkFile(label, path, rawLimit) {
35
38
  }
36
39
  }
37
40
 
38
- const moduleDir = resolve(root, 'dist/WoodsPortal.Module')
41
+ const moduleDir = moduleOutDir(root)
39
42
  const moduleJs = resolve(moduleDir, 'module.js')
40
43
 
41
44
  if (embedMode === 'monolith') {
@@ -21,7 +21,8 @@ Local state for **WoodsCLI** (\`@woodsportal/hubspot-kit\`). Not shipped to HubS
21
21
  | \`cache/\` | CLI caches (gitignored) |
22
22
  | \`audit/baseline/\` | Build parity snapshots (gitignored) |
23
23
  | \`.kit-module-config\` | Symlink to kit dev-module-config |
24
- | \`.kit-bootstrap.tsx\` | Symlink to kit dev bootstrap |
24
+ | \`.kit-bootstrap.tsx\` | Symlink to kit dev bootstrap (full portal) |
25
+ | \`.kit-bootstrap-greenfield.tsx\` | Symlink to kit greenfield dev bootstrap |
25
26
 
26
27
  Run \`wp doctor\` to recreate missing folders.
27
28
  `
@@ -135,7 +135,7 @@ export function finalizeHubSpotModuleDist(moduleDir, publicDir, options = {}) {
135
135
  writeFileSync(jsPath, escaped, 'utf8')
136
136
  assertValidJavaScriptSyntax(jsPath)
137
137
 
138
- if (escaped.length > bootstrapRawLimit) {
138
+ if (bootstrapRawLimit != null && escaped.length > bootstrapRawLimit) {
139
139
  throw new Error(
140
140
  `module.js bootstrap ${escaped.length} bytes exceeds ${bootstrapRawLimit} byte limit`,
141
141
  )
@@ -11,7 +11,10 @@ import { wphsPaths } from './wphs-paths.mjs'
11
11
  * @param {string} [projectRoot]
12
12
  */
13
13
  export function readWoodsportalConfig(projectRoot) {
14
- const root = projectRoot ?? findProjectRootWithConfig()
14
+ const envRoot = process.env.WP_HS_PROJECT_ROOT?.trim()
15
+ const root = envRoot
16
+ ? resolve(envRoot)
17
+ : findProjectRootWithConfig(projectRoot ?? process.cwd())
15
18
  const configPath = resolveProjectConfigPath(root)
16
19
  if (!configPath || !existsSync(configPath)) {
17
20
  return { root, configPath: null, config: null, paths: wphsPaths(root) }
@@ -4,14 +4,19 @@
4
4
  * Kept separate from Vite closeBundle to avoid hook-order races on CI.
5
5
  */
6
6
  import { fileURLToPath } from 'node:url'
7
- import { findProjectRoot } from './project-root.mjs'
8
- import { dirname, resolve } from 'node:path'
7
+ import { resolve } from 'node:path'
8
+
9
9
  import { finalizeHubSpotModuleDist } from './hubspot-module-postbuild.mjs'
10
+ import { moduleOutDir, readWoodsportalConfig } from './read-woodsportal-config.mjs'
11
+
12
+ const { root, config } = readWoodsportalConfig()
13
+ /** Hybrid CDN thin bootstrap only — monolith ships the full app in module.js. */
14
+ const bootstrapRawLimit = config?.projectType === 'module-monolith' ? null : 200 * 1024
10
15
 
11
- const root = findProjectRoot()
12
16
  const { jsBytes, cssBytes } = finalizeHubSpotModuleDist(
13
- resolve(root, 'dist/WoodsPortal.Module'),
17
+ moduleOutDir(root),
14
18
  resolve(root, 'public'),
19
+ { bootstrapRawLimit },
15
20
  )
16
21
 
17
22
  console.log(
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Force kit dev bootstrap imports through `.wphs/` symlinks so JSX uses Vite's
3
+ * prebundled react/jsx-dev-runtime (avoids CJS named-export errors from node_modules).
4
+ */
5
+ export default function bootstrapShimResolver({
6
+ bootstrapShim,
7
+ bootstrapGreenfieldShim,
8
+ kitRoot,
9
+ }) {
10
+ const kitRootNorm = kitRoot.replace(/\\/g, '/')
11
+ const normSource = (source) => source.replace(/\\/g, '/')
12
+
13
+ function isGreenfieldBootstrap(source) {
14
+ const id = normSource(source)
15
+ return (
16
+ source === '@woodsportal/hubspot-kit/dev/bootstrap-greenfield' ||
17
+ source === '@wphs/bootstrap-greenfield' ||
18
+ id.endsWith('/dev/bootstrap-greenfield.tsx') ||
19
+ id.endsWith('/dev/bootstrap-greenfield') ||
20
+ id.includes(`${kitRootNorm}/src/dev/bootstrap-greenfield`)
21
+ )
22
+ }
23
+
24
+ function isFullBootstrap(source) {
25
+ const id = normSource(source)
26
+ return (
27
+ source === '@woodsportal/hubspot-kit/dev/bootstrap' ||
28
+ source === '@wphs/bootstrap' ||
29
+ id.endsWith('/dev/bootstrap.tsx') ||
30
+ id.includes(`${kitRootNorm}/src/dev/bootstrap.tsx`)
31
+ )
32
+ }
33
+
34
+ return {
35
+ name: 'wp-bootstrap-shim-resolver',
36
+ enforce: 'pre',
37
+ resolveId(source) {
38
+ if (isGreenfieldBootstrap(source)) {
39
+ return bootstrapGreenfieldShim
40
+ }
41
+ if (isFullBootstrap(source)) {
42
+ return bootstrapShim
43
+ }
44
+ return null
45
+ },
46
+ }
47
+ }
@@ -40,7 +40,13 @@ export function ensureDevKitShims(projectRoot, kitRoot) {
40
40
  { directory: false },
41
41
  )
42
42
 
43
- return { moduleConfigRoot, bootstrapShim }
43
+ const bootstrapGreenfieldShim = ensureSymlink(
44
+ resolve(devDir, '.kit-bootstrap-greenfield.tsx'),
45
+ resolve(kitRoot, 'src/dev/bootstrap-greenfield.tsx'),
46
+ { directory: false },
47
+ )
48
+
49
+ return { moduleConfigRoot, bootstrapShim, bootstrapGreenfieldShim }
44
50
  }
45
51
 
46
52
  /** @deprecated use ensureDevKitShims */
@@ -3,7 +3,8 @@
3
3
  * Kit dev-module-config must skip react-compiler (CJS/ESM interop when loaded from kit path)
4
4
  * but still use the React plugin's Babel JSX pipeline — do not use plugin `exclude`.
5
5
  */
6
- const DEV_KIT_UI_RE = /[/\\]dev-module-config[/\\]|[/\\]\.kit-bootstrap\.tsx$|[/\\]dev[/\\]bootstrap\.tsx$/
6
+ const DEV_KIT_UI_RE =
7
+ /[/\\]dev-module-config[/\\]|[/\\]\.kit-bootstrap(?:-greenfield)?\.tsx$|[/\\]dev[/\\]bootstrap(?:-greenfield)?\.tsx$/
7
8
 
8
9
  export function createDevReactPluginOptions() {
9
10
  return {