@woodsportal/hubspot-kit 1.0.37 → 1.0.39-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ### CI / CodeBuild
6
+ - **`woodsportal-<env>-cb-hubspot-kit-publish`** — `hubspot-kit-npm-publish.yml` runs `npm run verify` then publishes `@woodsportal/hubspot-kit` (`next` on `dev`, `latest` on `main`). Client-frontend node-push installs kit from npm via `HUBSPOT_KIT_NPM_TAG` override.
7
+
5
8
  ### Module build id (collaborative CDN deploys)
6
9
  - **Rename** `.portal-build-id` → `.module-build-id` (channel baseline); `wp migrate --yes` upgrades legacy projects
7
10
  - **Auto-bump** on `wp deploy` and `wp build --cdn-only` — `{baseline}.{devSlug}.{seq}` in gitignored `.wphs/state/module-build-id.json`
@@ -19,6 +22,16 @@
19
22
  - **CSS entries** — shared `tailwind.css.config.css`; documented never-merge rules on `main.dev.css` / `main.prod.css`
20
23
  - **`wp doctor`** — CSS pipeline checks (prefix, `.temp` @source, `skipRuntimeCss`, `module.css` dw utilities, CDN `require_css` warning)
21
24
 
25
+ ## 1.0.39 — 2026-06-08
26
+
27
+ ### Dev server
28
+ - **Module-config overlay** — sync runtime state on mount and after init (fixes stuck "Loading module config…" when Vite loads duplicate module copies); pin `@woodsportal/hubspot-kit/src/dev-module-config` to `.wphs/.kit-module-config`
29
+
30
+ ## 1.0.38 — 2026-06-08
31
+
32
+ ### Dev server
33
+ - **Module-config overlay** — stop force-resolving `react` / `react-dom` to CJS `index.js` for kit UI under `.wphs/`; fixes `createContext` / named export errors in the browser on `yarn local`
34
+
22
35
  ## 1.0.37 — 2026-06-08
23
36
 
24
37
  ### Dev server
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@woodsportal/hubspot-kit",
3
- "version": "1.0.37",
3
+ "version": "1.0.39-dev.0",
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",
@@ -51,7 +51,7 @@
51
51
  "build": "tsup",
52
52
  "dev": "tsup --watch",
53
53
  "typecheck": "tsc --noEmit",
54
- "test": "node --import tsx --test 'src/lib/**/*.test.ts' 'src/schema/**/*.test.ts' 'src/vite/**/*.test.ts' 'scripts/portal-build-id.test.mjs' 'scripts/resolve-consumer-dep.test.mjs' 'scripts/vite/ensure-dev-kit-shims.test.mjs' 'scripts/vite/consumer-react-aliases.test.mjs' && vitest run src/dev-module-config/__tests__/normalize-repeater-items.test.ts src/dev-module-config/__tests__/module-config-transform.test.ts src/dev-module-config/__tests__/flatten-visible-fields.test.ts",
54
+ "test": "node --import tsx --test 'src/lib/**/*.test.ts' 'src/schema/**/*.test.ts' 'src/vite/**/*.test.ts' 'scripts/portal-build-id.test.mjs' 'scripts/resolve-consumer-dep.test.mjs' 'scripts/vite/ensure-dev-kit-shims.test.mjs' 'scripts/vite/consumer-react-aliases.test.mjs' 'scripts/vite/resolve-from-project-root.test.mjs' && vitest run src/dev-module-config/__tests__/normalize-repeater-items.test.ts src/dev-module-config/__tests__/module-config-transform.test.ts src/dev-module-config/__tests__/flatten-visible-fields.test.ts",
55
55
  "test:examples": "node examples/smoke/run-matrix.mjs --tier static",
56
56
  "lint": "eslint src",
57
57
  "verify": "npm run typecheck && npm run test && npm run test:examples && npm run build",
@@ -87,12 +87,26 @@
87
87
  "vite": "^6.0.0"
88
88
  },
89
89
  "peerDependenciesMeta": {
90
- "@tailwindcss/vite": { "optional": true },
91
- "@tanstack/router-plugin": { "optional": true },
92
- "@vitejs/plugin-react": { "optional": true },
93
- "babel-plugin-react-compiler": { "optional": true },
94
- "react": { "optional": true },
95
- "react-dom": { "optional": true },
96
- "vite": { "optional": true }
90
+ "@tailwindcss/vite": {
91
+ "optional": true
92
+ },
93
+ "@tanstack/router-plugin": {
94
+ "optional": true
95
+ },
96
+ "@vitejs/plugin-react": {
97
+ "optional": true
98
+ },
99
+ "babel-plugin-react-compiler": {
100
+ "optional": true
101
+ },
102
+ "react": {
103
+ "optional": true
104
+ },
105
+ "react-dom": {
106
+ "optional": true
107
+ },
108
+ "vite": {
109
+ "optional": true
110
+ }
97
111
  }
98
112
  }
@@ -73,6 +73,7 @@ export async function createDevServerViteConfig(options = {}) {
73
73
  alias: {
74
74
  ...consumerReactAliases(projectRoot),
75
75
  '@/dev/module-config': moduleConfigRoot,
76
+ '@woodsportal/hubspot-kit/src/dev-module-config': moduleConfigRoot,
76
77
  '@wphs/bootstrap': bootstrapShim,
77
78
  '@wphs/bootstrap-greenfield': bootstrapGreenfieldShim,
78
79
  '@woodsportal/hubspot-kit/dev/bootstrap': bootstrapShim,
@@ -2,6 +2,22 @@ import { resolve, sep } from 'node:path'
2
2
 
3
3
  import { resolveConsumerDep } from '../resolve-consumer-dep.mjs'
4
4
 
5
+ /** Let Vite dedupe + optimizeDeps handle React; forced absolute paths serve CJS in the browser. */
6
+ export const SKIP_CONSUMER_RESOLVE_SPECS = new Set([
7
+ 'react',
8
+ 'react-dom',
9
+ 'react/jsx-runtime',
10
+ 'react/jsx-dev-runtime',
11
+ 'react-dom/client',
12
+ 'react/compiler-runtime',
13
+ ])
14
+
15
+ function shouldSkipConsumerResolve(source) {
16
+ if (SKIP_CONSUMER_RESOLVE_SPECS.has(source)) return true
17
+ if (source.startsWith('react/') || source.startsWith('react-dom/')) return true
18
+ return false
19
+ }
20
+
5
21
  /**
6
22
  * When Vite transforms kit dev UI (`.wphs/`, package src, or dev-module-config),
7
23
  * bare imports must resolve from the consumer project's node_modules.
@@ -29,6 +45,9 @@ export default function resolveFromProjectRoot(projectRoot, kitRoot) {
29
45
  if (!isKitUiImporter(importer)) {
30
46
  return null
31
47
  }
48
+ if (shouldSkipConsumerResolve(source)) {
49
+ return null
50
+ }
32
51
  try {
33
52
  return resolveConsumerDep(source, projectRoot)
34
53
  } catch {
@@ -0,0 +1,37 @@
1
+ import assert from 'node:assert/strict'
2
+ import { resolve } from 'node:path'
3
+ import { describe, it } from 'node:test'
4
+
5
+ import { consumerDepFixtureRoot } from '../test-fixtures/consumer-dep-fixture-root.mjs'
6
+ import resolveFromProjectRoot, {
7
+ SKIP_CONSUMER_RESOLVE_SPECS,
8
+ } from './resolve-from-project-root.js'
9
+
10
+ describe('resolveFromProjectRoot', () => {
11
+ const kitRoot = resolve(consumerDepFixtureRoot, 'kit')
12
+ const kitUiFile = resolve(consumerDepFixtureRoot, '.wphs/.kit-module-config/ui/ModuleConfigNavContext.tsx')
13
+ const appFile = resolve(consumerDepFixtureRoot, 'src/app.tsx')
14
+
15
+ const plugin = resolveFromProjectRoot(consumerDepFixtureRoot, kitRoot)
16
+ const resolveId = /** @type {(source: string, importer: string) => Promise<string | null>} */ (
17
+ plugin.resolveId.bind(plugin)
18
+ )
19
+
20
+ it('does not force-resolve react for kit UI (avoids CJS index.js in browser)', async () => {
21
+ assert.ok(SKIP_CONSUMER_RESOLVE_SPECS.has('react'))
22
+ assert.equal(await resolveId('react', kitUiFile), null)
23
+ })
24
+
25
+ it('does not force-resolve react/jsx-dev-runtime for kit UI (alias + optimizeDeps)', async () => {
26
+ assert.equal(await resolveId('react/jsx-dev-runtime', kitUiFile), null)
27
+ })
28
+
29
+ it('still resolves other deps from the consumer for kit UI', async () => {
30
+ const resolved = await resolveId('vite', kitUiFile)
31
+ assert.match(resolved ?? '', /node_modules[/\\]vite[/\\]/)
32
+ })
33
+
34
+ it('ignores non-kit importers', async () => {
35
+ assert.equal(await resolveId('vite', appFile), null)
36
+ })
37
+ })
@@ -64,6 +64,7 @@ export async function initModuleConfigRuntime(): Promise<ModuleConfigState> {
64
64
  ])
65
65
  state = buildState(schema, defaults, overrides)
66
66
  applyRuntimeSideEffects(state)
67
+ window.dispatchEvent(new CustomEvent(MODULE_CONFIG_CHANGED, { detail: state }))
67
68
  return state
68
69
  }
69
70
 
@@ -53,6 +53,7 @@ function ModuleConfigShellInner({
53
53
  isApplying,
54
54
  validationErrors,
55
55
  persistenceSource,
56
+ initError,
56
57
  setFieldValue,
57
58
  applyChanges,
58
59
  discardDraft,
@@ -124,7 +125,11 @@ function ModuleConfigShellInner({
124
125
  const developerOptionToggleField = state?.schema.find((field) => field.name === 'developer_option')
125
126
 
126
127
  if (!state) {
127
- return <div className={`p-4 ${hsTypography.helper}`}>Loading module config…</div>
128
+ return (
129
+ <div className={`p-4 ${hsTypography.helper}`}>
130
+ {initError ?? 'Loading module config…'}
131
+ </div>
132
+ )
128
133
  }
129
134
 
130
135
  const onExport = () => {
@@ -18,6 +18,7 @@ export function useModuleConfig() {
18
18
  const [validationErrors, setValidationErrors] = useState<Array<ValidationError>>([])
19
19
  const [persistenceSource, setPersistenceSource] = useState<'local' | 'example' | 'empty'>('empty')
20
20
  const [isApplying, setIsApplying] = useState(false)
21
+ const [initError, setInitError] = useState<string | null>(null)
21
22
 
22
23
  const isDirty =
23
24
  JSON.stringify(draftValues) !== JSON.stringify(appliedValues)
@@ -26,6 +27,45 @@ export function useModuleConfig() {
26
27
  void loadWorkspaceMeta().then((source) => setPersistenceSource(source ?? 'empty'))
27
28
  }, [appliedValues])
28
29
 
30
+ useEffect(() => {
31
+ let cancelled = false
32
+
33
+ const syncFromRuntime = (next: ModuleConfigState) => {
34
+ if (cancelled) return
35
+ setState(next)
36
+ setDraftValues(next.moduleValues)
37
+ setAppliedValues(next.moduleValues)
38
+ setValidationErrors([])
39
+ }
40
+
41
+ const existing = getModuleConfigState()
42
+ if (existing) {
43
+ syncFromRuntime(existing)
44
+ return () => {
45
+ cancelled = true
46
+ }
47
+ }
48
+
49
+ void ensureModuleConfigReady()
50
+ .then((ready) => {
51
+ if (ready) syncFromRuntime(ready)
52
+ })
53
+ .catch((error) => {
54
+ console.error('[module-config] Failed to initialize', error)
55
+ if (!cancelled) {
56
+ setInitError(
57
+ error instanceof Error
58
+ ? error.message
59
+ : 'Failed to load module config. Check that /fields.json is available.',
60
+ )
61
+ }
62
+ })
63
+
64
+ return () => {
65
+ cancelled = true
66
+ }
67
+ }, [])
68
+
29
69
  useEffect(() => {
30
70
  const onChange = (event: Event) => {
31
71
  const detail = (event as CustomEvent<ModuleConfigState>).detail
@@ -110,6 +150,7 @@ export function useModuleConfig() {
110
150
  isApplying,
111
151
  validationErrors,
112
152
  persistenceSource,
153
+ initError,
113
154
  setFieldValue,
114
155
  applyChanges,
115
156
  discardDraft,
@@ -117,8 +158,9 @@ export function useModuleConfig() {
117
158
  }
118
159
  }
119
160
 
120
- export async function ensureModuleConfigReady() {
161
+ export async function ensureModuleConfigReady(): Promise<ModuleConfigState | null> {
121
162
  if (!getModuleConfigState()) {
122
163
  await initModuleConfigRuntime()
123
164
  }
165
+ return getModuleConfigState()
124
166
  }