@woodsportal/hubspot-kit 1.0.5 → 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.
- package/CHANGELOG.md +9 -0
- package/README.md +4 -2
- package/dist/{chunk-JDGIAYHX.js → chunk-GTTIJVAR.js} +43 -3
- package/dist/chunk-GTTIJVAR.js.map +1 -0
- package/dist/cli.js +1378 -339
- package/dist/cli.js.map +1 -1
- package/dist/exec-U2OJATR3.js +12 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/examples/greenfield/module-hybrid-cdn/README.md +40 -3
- package/examples/greenfield/module-hybrid-cdn/public/fields.json +44 -1
- package/examples/greenfield/module-hybrid-cdn/src/app.tsx +8 -0
- package/examples/greenfield/module-hybrid-cdn/src/assets/css/main.dev.css +6 -1
- package/examples/greenfield/module-hybrid-cdn/src/cdn/app-entry.ts +32 -0
- package/examples/greenfield/module-hybrid-cdn/src/cdn/vendor-entry.ts +28 -0
- package/examples/greenfield/module-hybrid-cdn/src/components/layout/section-card.tsx +20 -0
- package/examples/greenfield/module-hybrid-cdn/src/components/layout/starter-nav.tsx +28 -0
- package/examples/greenfield/module-hybrid-cdn/src/components/layout/starter-shell.tsx +18 -0
- package/examples/greenfield/module-hybrid-cdn/src/config/router.ts +4 -0
- package/examples/greenfield/module-hybrid-cdn/src/config/routes.ts +5 -0
- package/examples/greenfield/module-hybrid-cdn/src/defaultData.ts +2 -7
- package/examples/greenfield/module-hybrid-cdn/src/features/starter/components/starter-guide-page.tsx +93 -0
- package/examples/greenfield/module-hybrid-cdn/src/features/starter/components/starter-home-page.tsx +21 -0
- package/examples/greenfield/module-hybrid-cdn/src/features/starter/components/starter-home.tsx +52 -0
- package/examples/greenfield/module-hybrid-cdn/src/features/starter/components/starter-single-page.tsx +17 -0
- package/examples/greenfield/module-hybrid-cdn/src/features/starter/content/woodscli-guide-content.ts +86 -0
- package/examples/greenfield/module-hybrid-cdn/src/features/starter/hooks/use-starter-preview.ts +47 -0
- package/examples/greenfield/module-hybrid-cdn/src/features/starter/index.ts +6 -0
- package/examples/greenfield/module-hybrid-cdn/src/hubspot/bootstrap.ts +10 -0
- package/examples/greenfield/module-hybrid-cdn/src/hubspot/default-data.ts +7 -0
- package/examples/greenfield/module-hybrid-cdn/src/integrations/tanstack-router/router.tsx +25 -0
- package/examples/greenfield/module-hybrid-cdn/src/main.tsx +4 -3
- package/examples/greenfield/module-hybrid-cdn/src/portal/mount-dev-preview.tsx +8 -0
- package/examples/greenfield/module-hybrid-cdn/src/portal/mount-portal.tsx +12 -0
- package/examples/greenfield/module-hybrid-cdn/src/routeTree.gen.ts +77 -0
- package/examples/greenfield/module-hybrid-cdn/src/routes/__root.tsx +15 -0
- package/examples/greenfield/module-hybrid-cdn/src/routes/guide.tsx +7 -0
- package/examples/greenfield/module-hybrid-cdn/src/routes/index.tsx +7 -0
- package/examples/greenfield/module-hybrid-cdn/tsconfig.json +20 -0
- package/examples/greenfield/module-monolith/public/fields.json +44 -1
- package/examples/greenfield/module-monolith/src/app.tsx +8 -0
- package/examples/greenfield/module-monolith/src/assets/css/main.dev.css +8 -0
- package/examples/greenfield/module-monolith/src/components/layout/section-card.tsx +20 -0
- package/examples/greenfield/module-monolith/src/components/layout/starter-nav.tsx +28 -0
- package/examples/greenfield/module-monolith/src/components/layout/starter-shell.tsx +18 -0
- package/examples/greenfield/module-monolith/src/config/router.ts +4 -0
- package/examples/greenfield/module-monolith/src/config/routes.ts +5 -0
- package/examples/greenfield/module-monolith/src/defaultData.ts +2 -7
- package/examples/greenfield/module-monolith/src/features/starter/components/starter-guide-page.tsx +93 -0
- package/examples/greenfield/module-monolith/src/features/starter/components/starter-home-page.tsx +21 -0
- package/examples/greenfield/module-monolith/src/features/starter/components/starter-home.tsx +52 -0
- package/examples/greenfield/module-monolith/src/features/starter/components/starter-single-page.tsx +17 -0
- package/examples/greenfield/module-monolith/src/features/starter/content/woodscli-guide-content.ts +86 -0
- package/examples/greenfield/module-monolith/src/features/starter/hooks/use-starter-preview.ts +47 -0
- package/examples/greenfield/module-monolith/src/features/starter/index.ts +6 -0
- package/examples/greenfield/module-monolith/src/hubspot/default-data.ts +7 -0
- package/examples/greenfield/module-monolith/src/hubspot/types.ts +17 -0
- package/examples/greenfield/module-monolith/src/integrations/tanstack-router/router.tsx +25 -0
- package/examples/greenfield/module-monolith/src/main.tsx +5 -0
- package/examples/greenfield/module-monolith/src/portal/mount-dev-preview.tsx +8 -0
- package/examples/greenfield/module-monolith/src/portal/mount-portal.tsx +12 -0
- package/examples/greenfield/module-monolith/src/routeTree.gen.ts +77 -0
- package/examples/greenfield/module-monolith/src/routes/__root.tsx +15 -0
- package/examples/greenfield/module-monolith/src/routes/guide.tsx +7 -0
- package/examples/greenfield/module-monolith/src/routes/index.tsx +7 -0
- package/examples/greenfield/module-monolith/tsconfig.json +20 -0
- package/package.json +1 -1
- package/scripts/check-hubspot-bundle-size.mjs +5 -2
- package/scripts/ensure-wphs-workspace.mjs +2 -1
- package/scripts/hubspot-module-postbuild.mjs +1 -1
- package/scripts/read-woodsportal-config.mjs +4 -1
- package/scripts/run-hubspot-module-postbuild.mjs +9 -4
- package/scripts/vite/bootstrap-shim-resolver.js +47 -0
- package/scripts/vite/ensure-dev-kit-shims.js +7 -1
- package/scripts/vite/react-dev-plugin-options.js +2 -1
- package/scripts/watch-hubspot.mjs +107 -17
- package/scripts/wphs-paths.mjs +1 -0
- package/scripts/write-audit-artifacts.mjs +2 -1
- package/src/dev/bootstrap-greenfield.tsx +17 -9
- package/src/dev-module-config/dev-module-display-name.ts +5 -0
- package/src/dev-module-config/ui/ModuleConfigPersistenceStatus.tsx +5 -5
- package/src/dev-module-config/ui/ModuleConfigShell.tsx +2 -1
- package/src/dev-module-config/ui/module-config-theme.css +6 -0
- package/src/dev-module-config/ui/navigation/ModuleConfigNavContext.tsx +11 -5
- package/templates/module-hybrid-cdn/README.md +53 -3
- package/templates/module-hybrid-cdn/package.json +6 -2
- package/templates/module-hybrid-cdn/public/fields.json +44 -1
- package/templates/module-hybrid-cdn/src/app.tsx +8 -0
- package/templates/module-hybrid-cdn/src/assets/css/main.dev.css +6 -1
- package/templates/module-hybrid-cdn/src/cdn/app-entry.ts +32 -0
- package/templates/module-hybrid-cdn/src/cdn/vendor-entry.ts +28 -0
- package/templates/module-hybrid-cdn/src/components/layout/section-card.tsx +20 -0
- package/templates/module-hybrid-cdn/src/components/layout/starter-nav.tsx +28 -0
- package/templates/module-hybrid-cdn/src/components/layout/starter-shell.tsx +18 -0
- package/templates/module-hybrid-cdn/src/config/router.ts +4 -0
- package/templates/module-hybrid-cdn/src/config/routes.ts +5 -0
- package/templates/module-hybrid-cdn/src/defaultData.ts +2 -7
- package/templates/module-hybrid-cdn/src/features/starter/components/starter-guide-page.tsx +93 -0
- package/templates/module-hybrid-cdn/src/features/starter/components/starter-home-page.tsx +21 -0
- package/templates/module-hybrid-cdn/src/features/starter/components/starter-home.tsx +52 -0
- package/templates/module-hybrid-cdn/src/features/starter/components/starter-single-page.tsx +17 -0
- package/templates/module-hybrid-cdn/src/features/starter/content/woodscli-guide-content.ts +86 -0
- package/templates/module-hybrid-cdn/src/features/starter/hooks/starter-preview-values.ts +42 -0
- package/templates/module-hybrid-cdn/src/features/starter/hooks/use-starter-preview.dev.ts +17 -0
- package/templates/module-hybrid-cdn/src/features/starter/hooks/use-starter-preview.ts +30 -0
- package/templates/module-hybrid-cdn/src/features/starter/index.ts +6 -0
- package/templates/module-hybrid-cdn/src/hubspot/bootstrap.ts +10 -0
- package/templates/module-hybrid-cdn/src/hubspot/default-data.ts +7 -0
- package/templates/module-hybrid-cdn/src/hubspot/module-entry.tsx +9 -0
- package/templates/module-hybrid-cdn/src/integrations/tanstack-router/router.tsx +25 -0
- package/templates/module-hybrid-cdn/src/main.tsx +4 -3
- package/templates/module-hybrid-cdn/src/portal/mount-dev-preview.tsx +8 -0
- package/templates/module-hybrid-cdn/src/portal/mount-portal.tsx +12 -0
- package/templates/module-hybrid-cdn/src/routeTree.gen.ts +77 -0
- package/templates/module-hybrid-cdn/src/routes/__root.tsx +15 -0
- package/templates/module-hybrid-cdn/src/routes/guide.tsx +7 -0
- package/templates/module-hybrid-cdn/src/routes/index.tsx +7 -0
- package/templates/module-hybrid-cdn/tsconfig.json +20 -0
- package/templates/module-hybrid-cdn/vite.config.monolith.mjs +8 -0
- package/templates/module-monolith/README.md +59 -0
- package/templates/module-monolith/index.html +1 -1
- package/templates/module-monolith/package.json +10 -3
- package/templates/module-monolith/public/fields.json +44 -1
- package/templates/module-monolith/public/module.html +1 -0
- package/templates/module-monolith/src/app.tsx +8 -0
- package/templates/module-monolith/src/assets/css/main.dev.css +6 -1
- package/templates/module-monolith/src/components/layout/section-card.tsx +20 -0
- package/templates/module-monolith/src/components/layout/starter-nav.tsx +28 -0
- package/templates/module-monolith/src/components/layout/starter-shell.tsx +18 -0
- package/templates/module-monolith/src/config/router.ts +4 -0
- package/templates/module-monolith/src/config/routes.ts +5 -0
- package/templates/module-monolith/src/defaultData.ts +2 -7
- package/templates/module-monolith/src/features/starter/components/starter-guide-page.tsx +93 -0
- package/templates/module-monolith/src/features/starter/components/starter-home-page.tsx +21 -0
- package/templates/module-monolith/src/features/starter/components/starter-home.tsx +52 -0
- package/templates/module-monolith/src/features/starter/components/starter-single-page.tsx +17 -0
- package/templates/module-monolith/src/features/starter/content/woodscli-guide-content.ts +86 -0
- package/templates/module-monolith/src/features/starter/hooks/starter-preview-values.ts +42 -0
- package/templates/module-monolith/src/features/starter/hooks/use-starter-preview.dev.ts +17 -0
- package/templates/module-monolith/src/features/starter/hooks/use-starter-preview.ts +30 -0
- package/templates/module-monolith/src/features/starter/index.ts +6 -0
- package/templates/module-monolith/src/hubspot/default-data.ts +7 -0
- package/templates/module-monolith/src/hubspot/module-entry.tsx +9 -0
- package/templates/module-monolith/src/integrations/tanstack-router/router.tsx +25 -0
- package/templates/module-monolith/src/main.tsx +4 -3
- package/templates/module-monolith/src/portal/mount-dev-preview.tsx +8 -0
- package/templates/module-monolith/src/portal/mount-portal.tsx +12 -0
- package/templates/module-monolith/src/routeTree.gen.ts +77 -0
- package/templates/module-monolith/src/routes/__root.tsx +15 -0
- package/templates/module-monolith/src/routes/guide.tsx +7 -0
- package/templates/module-monolith/src/routes/index.tsx +7 -0
- package/templates/module-monolith/tsconfig.json +20 -0
- package/templates/module-monolith/vite.config.monolith.mjs +8 -0
- package/templates/module-monolith/woodscli.json +1 -0
- package/templates/shared/.env.dev.example +5 -0
- package/templates/shared/.env.prod.example +5 -0
- package/templates/shared/.env.stg.example +3 -0
- package/templates/shared/portal.cdn.json.example +8 -0
- package/templates/theme-starter/package.json +5 -2
- package/vite/vite.config.dev.mjs +9 -1
- package/vite/vite.config.monolith.mjs +22 -17
- package/dist/chunk-JDGIAYHX.js.map +0 -1
- package/dist/exec-K5C6LZP4.js +0 -10
- /package/dist/{exec-K5C6LZP4.js.map → exec-U2OJATR3.js.map} +0 -0
|
@@ -3,15 +3,21 @@
|
|
|
3
3
|
* HubSpot CMS watch — uses woodscli.json upload paths.
|
|
4
4
|
*/
|
|
5
5
|
import { spawn, spawnSync } from 'node:child_process'
|
|
6
|
-
import { watch } from 'node:fs'
|
|
7
|
-
import { resolve } from 'node:path'
|
|
6
|
+
import { existsSync, watch } from 'node:fs'
|
|
7
|
+
import { dirname, resolve } from 'node:path'
|
|
8
|
+
import { fileURLToPath } from 'node:url'
|
|
8
9
|
|
|
9
10
|
import { moduleOutDir, moduleUploadPath, readWoodsportalConfig, themeUploadPath } from './read-woodsportal-config.mjs'
|
|
10
11
|
|
|
12
|
+
const kitScriptsDir = dirname(fileURLToPath(import.meta.url))
|
|
13
|
+
const kitRoot = resolve(kitScriptsDir, '..')
|
|
14
|
+
const kitCli = resolve(kitRoot, 'dist/cli.js')
|
|
15
|
+
|
|
16
|
+
const argv = process.argv.slice(2)
|
|
17
|
+
const rebuild = argv.includes('--rebuild')
|
|
18
|
+
const tier = parseTier(argv)
|
|
19
|
+
|
|
11
20
|
const { root, config } = readWoodsportalConfig()
|
|
12
|
-
const rebuild = process.argv.includes('--rebuild')
|
|
13
|
-
const isProd = process.argv.includes('--prod')
|
|
14
|
-
const buildScript = isProd ? 'build:prod' : 'build:dev'
|
|
15
21
|
|
|
16
22
|
const isTheme = config?.projectType === 'theme-starter'
|
|
17
23
|
const hubspotModulePath = moduleOutDir(root)
|
|
@@ -22,13 +28,85 @@ const watchRoots = isTheme
|
|
|
22
28
|
? [theme.srcDir]
|
|
23
29
|
: ['src', 'public'].map((dir) => resolve(root, dir))
|
|
24
30
|
|
|
31
|
+
/** @type {import('node:child_process').ChildProcess | null} */
|
|
32
|
+
let cmsChild = null
|
|
33
|
+
/** @type {ReturnType<typeof setTimeout> | null} */
|
|
34
|
+
let debounceTimer = null
|
|
35
|
+
/** @type {import('node:fs').FSWatcher[]} */
|
|
36
|
+
const fsWatchers = []
|
|
37
|
+
let shuttingDown = false
|
|
38
|
+
|
|
39
|
+
function parseTier(args) {
|
|
40
|
+
const idx = args.indexOf('--tier')
|
|
41
|
+
if (idx >= 0 && args[idx + 1]) {
|
|
42
|
+
const value = args[idx + 1]
|
|
43
|
+
if (value === 'dev' || value === 'stg' || value === 'prod') return value
|
|
44
|
+
}
|
|
45
|
+
if (args.includes('--prod')) return 'prod'
|
|
46
|
+
if (args.includes('--stg')) return 'stg'
|
|
47
|
+
return 'dev'
|
|
48
|
+
}
|
|
49
|
+
|
|
25
50
|
function log(msg) {
|
|
26
51
|
console.log(`[watch] ${msg}`)
|
|
27
52
|
}
|
|
28
53
|
|
|
29
|
-
function
|
|
30
|
-
|
|
31
|
-
|
|
54
|
+
function shutdown(signal) {
|
|
55
|
+
if (shuttingDown) return
|
|
56
|
+
shuttingDown = true
|
|
57
|
+
|
|
58
|
+
if (debounceTimer) {
|
|
59
|
+
clearTimeout(debounceTimer)
|
|
60
|
+
debounceTimer = null
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (const watcher of fsWatchers) {
|
|
64
|
+
try {
|
|
65
|
+
watcher.close()
|
|
66
|
+
} catch {
|
|
67
|
+
// ignore
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (cmsChild && !cmsChild.killed) {
|
|
72
|
+
cmsChild.kill(signal)
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
process.exit(signal === 'SIGINT' ? 130 : 143)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
process.on('SIGINT', () => shutdown('SIGINT'))
|
|
80
|
+
process.on('SIGTERM', () => shutdown('SIGTERM'))
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Run wp build from the same @woodsportal/hubspot-kit package as this script.
|
|
84
|
+
* Avoids consumer node_modules/.bin/wp (often an older pin with wrong outDir).
|
|
85
|
+
*/
|
|
86
|
+
function runWpBuild() {
|
|
87
|
+
const env = { ...process.env, WP_HS_PROJECT_ROOT: root }
|
|
88
|
+
|
|
89
|
+
let command = process.execPath
|
|
90
|
+
let args = []
|
|
91
|
+
|
|
92
|
+
if (existsSync(kitCli)) {
|
|
93
|
+
args = [kitCli, 'build', '--tier', tier, '--quiet']
|
|
94
|
+
} else {
|
|
95
|
+
const localWp = resolve(root, 'node_modules', '.bin', 'wp')
|
|
96
|
+
command = existsSync(localWp) ? localWp : 'wp'
|
|
97
|
+
args = ['build', '--tier', tier, '--quiet']
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
log(`Running wp build --tier ${tier} …`)
|
|
101
|
+
const result = spawnSync(command, args, {
|
|
102
|
+
cwd: root,
|
|
103
|
+
stdio: 'inherit',
|
|
104
|
+
env,
|
|
105
|
+
shell: command === 'wp',
|
|
106
|
+
})
|
|
107
|
+
if (result.status !== 0) {
|
|
108
|
+
process.exit(result.status ?? 1)
|
|
109
|
+
}
|
|
32
110
|
}
|
|
33
111
|
|
|
34
112
|
function runBuild() {
|
|
@@ -36,19 +114,22 @@ function runBuild() {
|
|
|
36
114
|
log('Theme project — no module build step')
|
|
37
115
|
return
|
|
38
116
|
}
|
|
39
|
-
|
|
40
|
-
runYarn(buildScript)
|
|
117
|
+
runWpBuild()
|
|
41
118
|
}
|
|
42
119
|
|
|
43
120
|
function startCmsWatch() {
|
|
44
121
|
const src = isTheme ? theme.srcDir : hubspotModulePath
|
|
45
122
|
const target = isTheme ? theme.uploadPath : hubspotUploadTarget
|
|
46
123
|
log(`Starting hs cms watch ${src} → ${target}`)
|
|
47
|
-
|
|
124
|
+
cmsChild = spawn('hs', ['cms', 'watch', src, target], { cwd: root, stdio: 'inherit' })
|
|
125
|
+
cmsChild.on('exit', (code) => {
|
|
126
|
+
if (shuttingDown) return
|
|
127
|
+
process.exit(code ?? 0)
|
|
128
|
+
})
|
|
129
|
+
return cmsChild
|
|
48
130
|
}
|
|
49
131
|
|
|
50
132
|
function startRebuildWatcher() {
|
|
51
|
-
let debounceTimer = null
|
|
52
133
|
let building = false
|
|
53
134
|
let pending = false
|
|
54
135
|
|
|
@@ -75,12 +156,21 @@ function startRebuildWatcher() {
|
|
|
75
156
|
}
|
|
76
157
|
|
|
77
158
|
for (const watchRoot of watchRoots) {
|
|
78
|
-
watch(watchRoot, { recursive: true }, () => scheduleBuild())
|
|
159
|
+
const watcher = watch(watchRoot, { recursive: true }, () => scheduleBuild())
|
|
160
|
+
fsWatchers.push(watcher)
|
|
161
|
+
log(`Watching ${watchRoot}`)
|
|
79
162
|
}
|
|
80
163
|
}
|
|
81
164
|
|
|
82
|
-
if (!isTheme)
|
|
83
|
-
const
|
|
84
|
-
if (
|
|
165
|
+
if (!isTheme) {
|
|
166
|
+
const moduleJs = resolve(hubspotModulePath, 'module.js')
|
|
167
|
+
if (!existsSync(moduleJs)) {
|
|
168
|
+
log('Module dist missing — building before watch')
|
|
169
|
+
runBuild()
|
|
170
|
+
} else if (rebuild) {
|
|
171
|
+
runBuild()
|
|
172
|
+
}
|
|
173
|
+
}
|
|
85
174
|
|
|
86
|
-
|
|
175
|
+
startCmsWatch()
|
|
176
|
+
if (rebuild && !isTheme) startRebuildWatcher()
|
package/scripts/wphs-paths.mjs
CHANGED
|
@@ -68,6 +68,7 @@ export function wphsPaths(projectRoot) {
|
|
|
68
68
|
stateDir: join(workspaceRoot, 'state'),
|
|
69
69
|
kitModuleConfigLink: join(workspaceRoot, '.kit-module-config'),
|
|
70
70
|
kitBootstrapLink: join(workspaceRoot, '.kit-bootstrap.tsx'),
|
|
71
|
+
kitBootstrapGreenfieldLink: join(workspaceRoot, '.kit-bootstrap-greenfield.tsx'),
|
|
71
72
|
readmePath: join(workspaceRoot, 'README.md'),
|
|
72
73
|
brandingAssetsUrlPrefix: `/${workspaceDir}/assets/branding/`,
|
|
73
74
|
legacyBrandingAssetsUrlPrefixes: [
|
|
@@ -6,6 +6,7 @@ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from
|
|
|
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 } from './read-woodsportal-config.mjs'
|
|
9
10
|
import { gzipSync } from 'node:zlib'
|
|
10
11
|
|
|
11
12
|
const root = findProjectRoot()
|
|
@@ -33,7 +34,7 @@ const report = {
|
|
|
33
34
|
cdn: {},
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
const moduleDir =
|
|
37
|
+
const moduleDir = moduleOutDir(root)
|
|
37
38
|
for (const name of ['module.js', 'module.css', 'module.html', 'fields.json']) {
|
|
38
39
|
const path = resolve(moduleDir, name)
|
|
39
40
|
if (existsSync(path)) {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Use after `wp init`; adopt `dev/bootstrap` when wiring a real WoodsPortal consumer app.
|
|
4
4
|
*/
|
|
5
5
|
import { createRoot } from 'react-dom/client'
|
|
6
|
+
import type { ReactNode } from 'react'
|
|
6
7
|
|
|
7
8
|
import {
|
|
8
9
|
bindModuleConfigToHubSpotData,
|
|
@@ -16,7 +17,12 @@ import {
|
|
|
16
17
|
} from '@/dev/module-config/ui/ModuleConfigOverlay'
|
|
17
18
|
import wphsAdapter from '@wphs/adapter'
|
|
18
19
|
|
|
19
|
-
export
|
|
20
|
+
export type MountGreenfieldDevOptions = {
|
|
21
|
+
/** Scaffold-owned preview (e.g. templates/src/app/StarterPage.tsx). */
|
|
22
|
+
preview?: ReactNode
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function mountGreenfieldDev(options: MountGreenfieldDevOptions = {}): Promise<void> {
|
|
20
26
|
const rootElement = document.getElementById('app')
|
|
21
27
|
|
|
22
28
|
try {
|
|
@@ -31,15 +37,17 @@ export async function mountGreenfieldDev(): Promise<void> {
|
|
|
31
37
|
}
|
|
32
38
|
|
|
33
39
|
createRoot(rootElement).render(
|
|
34
|
-
|
|
40
|
+
<div>
|
|
35
41
|
<ModuleConfigOverlay />
|
|
36
|
-
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
{options.preview ?? (
|
|
43
|
+
<main style={{ padding: '1.5rem', fontFamily: 'system-ui, sans-serif' }}>
|
|
44
|
+
<h1>WoodsCLI dev</h1>
|
|
45
|
+
<p>
|
|
46
|
+
Edit HubSpot module fields with <kbd>Ctrl+Shift+Z</kbd> (Mac: <kbd>⌃⇧Z</kbd>).
|
|
47
|
+
</p>
|
|
48
|
+
</main>
|
|
49
|
+
)}
|
|
50
|
+
</div>
|
|
43
51
|
)
|
|
44
52
|
} catch (error) {
|
|
45
53
|
const message = error instanceof Error ? error.message : String(error)
|
|
@@ -8,7 +8,7 @@ type ModuleConfigPersistenceStatusProps = {
|
|
|
8
8
|
function StatusIcon({ variant }: { variant: 'dirty' | 'saved' | 'info' }) {
|
|
9
9
|
if (variant === 'dirty') {
|
|
10
10
|
return (
|
|
11
|
-
<svg viewBox="0 0 16 16" className="
|
|
11
|
+
<svg viewBox="0 0 16 16" className="mc-status-icon" fill="currentColor" aria-hidden>
|
|
12
12
|
<circle cx="8" cy="8" r="3.5" />
|
|
13
13
|
</svg>
|
|
14
14
|
)
|
|
@@ -16,7 +16,7 @@ function StatusIcon({ variant }: { variant: 'dirty' | 'saved' | 'info' }) {
|
|
|
16
16
|
|
|
17
17
|
if (variant === 'saved') {
|
|
18
18
|
return (
|
|
19
|
-
<svg viewBox="0 0 16 16" className="
|
|
19
|
+
<svg viewBox="0 0 16 16" className="mc-status-icon" fill="none" aria-hidden>
|
|
20
20
|
<path
|
|
21
21
|
d="M3.5 8.5 6.5 11.5 12.5 4.5"
|
|
22
22
|
stroke="currentColor"
|
|
@@ -29,7 +29,7 @@ function StatusIcon({ variant }: { variant: 'dirty' | 'saved' | 'info' }) {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
return (
|
|
32
|
-
<svg viewBox="0 0 16 16" className="
|
|
32
|
+
<svg viewBox="0 0 16 16" className="mc-status-icon" fill="none" aria-hidden>
|
|
33
33
|
<circle cx="8" cy="8" r="6.25" stroke="currentColor" strokeWidth="1.5" />
|
|
34
34
|
<path d="M8 7v4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
|
|
35
35
|
<circle cx="8" cy="4.75" r="0.85" fill="currentColor" />
|
|
@@ -59,7 +59,7 @@ export function ModuleConfigPersistenceStatus({
|
|
|
59
59
|
<StatusIcon variant="saved" />
|
|
60
60
|
<div className="mc-persistence-status__body">
|
|
61
61
|
<span className="mc-persistence-status__label">Saved locally</span>
|
|
62
|
-
<code className="mc-persistence-status__path">.
|
|
62
|
+
<code className="mc-persistence-status__path">.wphs/config/module-config.local.json</code>
|
|
63
63
|
</div>
|
|
64
64
|
</div>
|
|
65
65
|
)
|
|
@@ -71,7 +71,7 @@ export function ModuleConfigPersistenceStatus({
|
|
|
71
71
|
<StatusIcon variant="info" />
|
|
72
72
|
<div className="mc-persistence-status__body">
|
|
73
73
|
<span className="mc-persistence-status__label">Using example config</span>
|
|
74
|
-
<code className="mc-persistence-status__path">.
|
|
74
|
+
<code className="mc-persistence-status__path">.wphs/config/module-config.example.json</code>
|
|
75
75
|
</div>
|
|
76
76
|
</div>
|
|
77
77
|
)
|
|
@@ -25,6 +25,7 @@ import { mcSurface } from './module-config-surfaces'
|
|
|
25
25
|
import { ModuleConfigThemeToggle } from './ModuleConfigThemeToggle'
|
|
26
26
|
import { ModuleConfigDockSideIcon } from './ModuleConfigDockSideIcon'
|
|
27
27
|
import { ModuleConfigPersistenceStatus } from './ModuleConfigPersistenceStatus'
|
|
28
|
+
import { devModuleDisplayName } from '../dev-module-display-name'
|
|
28
29
|
|
|
29
30
|
type Tab = 'CONTENT' | 'STYLE'
|
|
30
31
|
|
|
@@ -179,7 +180,7 @@ function ModuleConfigShellInner({
|
|
|
179
180
|
))}
|
|
180
181
|
</nav>
|
|
181
182
|
<div className="flex items-start justify-between gap-2">
|
|
182
|
-
<h2 className={hsTypography.moduleTitle}>
|
|
183
|
+
<h2 className={hsTypography.moduleTitle}>{devModuleDisplayName()}</h2>
|
|
183
184
|
<div className="flex shrink-0 items-center gap-1">
|
|
184
185
|
<ModuleConfigThemeToggle theme={theme} onToggle={toggleTheme} />
|
|
185
186
|
{compact && onToggleOverlaySide ? (
|
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
type ReactNode,
|
|
8
8
|
} from 'react'
|
|
9
9
|
|
|
10
|
+
import { devModuleDisplayName } from '../../dev-module-display-name'
|
|
11
|
+
|
|
10
12
|
export type BreadcrumbSegment = {
|
|
11
13
|
id: string
|
|
12
14
|
label: string
|
|
@@ -27,6 +29,14 @@ type ModuleConfigNavContextValue = {
|
|
|
27
29
|
|
|
28
30
|
const ROOT_SEGMENT: BreadcrumbSegment = { id: 'contents', label: 'Contents' }
|
|
29
31
|
|
|
32
|
+
function moduleTitleSegment(stackLength: number, popTo: (id: string) => void): BreadcrumbSegment {
|
|
33
|
+
return {
|
|
34
|
+
id: 'module',
|
|
35
|
+
label: devModuleDisplayName(),
|
|
36
|
+
onClick: stackLength > 1 ? () => popTo('module') : undefined,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
const ModuleConfigNavContext = createContext<ModuleConfigNavContextValue | null>(null)
|
|
31
41
|
|
|
32
42
|
export function ModuleConfigNavProvider({ children }: { children: ReactNode }) {
|
|
@@ -124,11 +134,7 @@ export function ModuleConfigNavProvider({ children }: { children: ReactNode }) {
|
|
|
124
134
|
}, [clearAllDrillDownPanels])
|
|
125
135
|
|
|
126
136
|
const segments = useMemo(() => {
|
|
127
|
-
const moduleTitle
|
|
128
|
-
id: 'module',
|
|
129
|
-
label: 'woodsportal-module',
|
|
130
|
-
onClick: stack.length > 1 ? () => popTo('module') : undefined,
|
|
131
|
-
}
|
|
137
|
+
const moduleTitle = moduleTitleSegment(stack.length, popTo)
|
|
132
138
|
|
|
133
139
|
if (stack.length <= 1) {
|
|
134
140
|
return [ROOT_SEGMENT, moduleTitle]
|
|
@@ -1,9 +1,59 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Getting started
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This project was scaffolded with **WoodsCLI** (`@woodsportal/hubspot-kit`).
|
|
4
|
+
|
|
5
|
+
## Daily dev
|
|
4
6
|
|
|
5
7
|
```bash
|
|
6
|
-
|
|
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
|
+
## Routing (`wp init`)
|
|
16
|
+
|
|
17
|
+
Default is **hash history** — same as `woodsportal-client-frontend` (`#/guide` URLs work in HubSpot CMS).
|
|
18
|
+
|
|
19
|
+
| `wp init --router` | Behavior |
|
|
20
|
+
|--------------------|----------|
|
|
21
|
+
| `hash` (default) | `createHashHistory()` — production-safe in HubSpot embeds |
|
|
22
|
+
| `browser` | Clean `/guide` URLs for local dev only |
|
|
23
|
+
| `none` | Single-page starter, no TanStack Router |
|
|
24
|
+
|
|
25
|
+
Recorded in `woodscli.json` → `scaffold.router`.
|
|
26
|
+
|
|
27
|
+
## `src/` layout
|
|
28
|
+
|
|
29
|
+
| Path | Purpose |
|
|
30
|
+
|------|---------|
|
|
31
|
+
| `main.tsx` | Dev entry — loads styles and `mountDevPreview()` |
|
|
32
|
+
| `app.tsx` | `RouterProvider` shell (client-frontend pattern) |
|
|
33
|
+
| `config/router.ts` | Hash vs browser history |
|
|
34
|
+
| `portal/` | Dev vs production mount (`mount-dev-preview`, `mount-portal`) |
|
|
35
|
+
| `routes/` | TanStack file routes — keep thin; import from `features/` |
|
|
36
|
+
| `features/starter/` | Demo home + developer guide (replace with your app) |
|
|
37
|
+
| `components/` | Shared layout and UI |
|
|
38
|
+
| `integrations/` | Router and third-party wiring |
|
|
39
|
+
| `hubspot/` | HubSpot types and dev fixtures |
|
|
40
|
+
| `config/` | Route constants and app config |
|
|
41
|
+
| `assets/css/` | Tailwind entry for local dev |
|
|
42
|
+
|
|
43
|
+
Kit runtime expects `src/defaultData.ts` (re-exports `hubspot/default-data.ts`).
|
|
44
|
+
|
|
45
|
+
## Docs (in the kit package)
|
|
46
|
+
|
|
47
|
+
After `npm install`, see:
|
|
48
|
+
|
|
49
|
+
- `node_modules/@woodsportal/hubspot-kit/docs/CLI-REFERENCE.md`
|
|
50
|
+
- `node_modules/@woodsportal/hubspot-kit/docs/MODULES.md`
|
|
51
|
+
- `node_modules/@woodsportal/hubspot-kit/docs/HUBSPOT-CLI-SETUP.md`
|
|
52
|
+
|
|
53
|
+
## HubSpot upload
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
cp hubspot.config.yml.example hubspot.config.yml
|
|
57
|
+
# add portal + personal access key
|
|
58
|
+
wp upload
|
|
59
|
+
```
|
|
@@ -9,11 +9,15 @@
|
|
|
9
9
|
"deploy:prod": "wp deploy --tier prod"
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"@woodsportal/hubspot-kit": "^1.0.
|
|
12
|
+
"@woodsportal/hubspot-kit": "^1.0.11",
|
|
13
|
+
"@dnd-kit/core": "^6.3.1",
|
|
14
|
+
"@dnd-kit/sortable": "^10.0.0",
|
|
15
|
+
"@dnd-kit/utilities": "^3.2.2",
|
|
13
16
|
"@tailwindcss/vite": "^4.0.6",
|
|
17
|
+
"@tanstack/react-router": "^1.121.2",
|
|
14
18
|
"@tanstack/router-plugin": "^1.121.2",
|
|
15
19
|
"@vitejs/plugin-react": "^4.3.4",
|
|
16
|
-
"babel-plugin-react-compiler": "^1.0.
|
|
20
|
+
"babel-plugin-react-compiler": "^1.0.11",
|
|
17
21
|
"react": "^19.0.0",
|
|
18
22
|
"react-dom": "^19.0.0",
|
|
19
23
|
"tailwindcss": "^4.0.6",
|
|
@@ -1 +1,44 @@
|
|
|
1
|
-
[
|
|
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
|
-
|
|
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
|
+
}
|
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
export
|
|
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'
|