@woodsportal/hubspot-kit 1.0.30 → 1.0.31
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 +12 -0
- package/dist/chunk-4GOJPOW3.js +258 -0
- package/dist/chunk-4GOJPOW3.js.map +1 -0
- package/dist/cli.js +546 -240
- package/dist/cli.js.map +1 -1
- package/dist/{exec-U2OJATR3.js → exec-VY6BGWF3.js} +6 -2
- package/examples/greenfield/module-hybrid-cdn/public/fields.json +7 -2
- package/examples/greenfield/module-hybrid-cdn/public/module.html +22 -0
- package/examples/greenfield/module-hybrid-cdn/src/cdn/app-entry.ts +5 -0
- package/examples/greenfield/module-hybrid-cdn/src/portal/api-endpoint.ts +11 -0
- package/examples/greenfield/module-hybrid-cdn/vite.config.cdn-app.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn/vite.config.cdn-esm.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn/vite.config.cdn-vendor.mjs +1 -0
- package/package.json +1 -1
- package/scripts/build-cdn.mjs +11 -7
- package/scripts/hubspot-module-postbuild.mjs +10 -6
- package/scripts/portal-subprocess-stdio.mjs +38 -0
- package/scripts/publish-cdn-github.mjs +20 -2
- package/scripts/resolve-vite-config.mjs +26 -0
- package/scripts/run-hubspot-module-postbuild.mjs +4 -1
- package/scripts/run-vite-build.mjs +13 -5
- package/templates/module-hybrid-cdn/public/fields.json +7 -2
- package/templates/module-hybrid-cdn/public/module.html +22 -0
- package/templates/module-hybrid-cdn/src/cdn/app-entry.ts +5 -0
- package/templates/module-hybrid-cdn/src/portal/api-endpoint.ts +11 -0
- package/templates/module-hybrid-cdn/vite.config.cdn-app.mjs +1 -0
- package/templates/module-hybrid-cdn/vite.config.cdn-esm.mjs +1 -0
- package/templates/module-hybrid-cdn/vite.config.cdn-vendor.mjs +1 -0
- package/vite/vite.config.hubspot.mjs +8 -2
- package/dist/chunk-GTTIJVAR.js +0 -127
- package/dist/chunk-GTTIJVAR.js.map +0 -1
- /package/dist/{exec-U2OJATR3.js.map → exec-VY6BGWF3.js.map} +0 -0
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
runCommand,
|
|
4
|
+
runCommandAsync,
|
|
4
5
|
runNodeScript,
|
|
6
|
+
runNodeScriptAsync,
|
|
5
7
|
runNodeScriptLongRunning
|
|
6
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-4GOJPOW3.js";
|
|
7
9
|
export {
|
|
8
10
|
runCommand,
|
|
11
|
+
runCommandAsync,
|
|
9
12
|
runNodeScript,
|
|
13
|
+
runNodeScriptAsync,
|
|
10
14
|
runNodeScriptLongRunning
|
|
11
15
|
};
|
|
12
|
-
//# sourceMappingURL=exec-
|
|
16
|
+
//# sourceMappingURL=exec-VY6BGWF3.js.map
|
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
"name": "tagline",
|
|
11
11
|
"label": "Tagline",
|
|
12
12
|
"type": "text",
|
|
13
|
+
"display": "textarea",
|
|
13
14
|
"tab": "CONTENT",
|
|
14
|
-
"help_text": "Subtitle shown under the hero title
|
|
15
|
+
"help_text": "Subtitle shown under the hero title.",
|
|
15
16
|
"default": "Customize this preview with Ctrl+Shift+Z, then replace StarterPage with your portal app."
|
|
16
17
|
},
|
|
17
18
|
{
|
|
@@ -27,12 +28,16 @@
|
|
|
27
28
|
"label": "Accent color",
|
|
28
29
|
"type": "color",
|
|
29
30
|
"tab": "STYLE",
|
|
30
|
-
"default": {
|
|
31
|
+
"default": {
|
|
32
|
+
"color": "#0091ae",
|
|
33
|
+
"opacity": 100
|
|
34
|
+
}
|
|
31
35
|
},
|
|
32
36
|
{
|
|
33
37
|
"name": "hero_style",
|
|
34
38
|
"label": "Hero style",
|
|
35
39
|
"type": "choice",
|
|
40
|
+
"display": "select",
|
|
36
41
|
"tab": "STYLE",
|
|
37
42
|
"choices": [
|
|
38
43
|
["gradient", "Gradient"],
|
|
@@ -1 +1,23 @@
|
|
|
1
|
+
{# Hybrid CDN — React app loads from jsDelivr; module.js is a thin bootstrap only. #}
|
|
2
|
+
{% set in_cms_editor = is_in_editor or is_in_page_editor or is_in_previewer %}
|
|
3
|
+
<script>
|
|
4
|
+
window.hsInEditor = {{ in_cms_editor|lower }};
|
|
5
|
+
window.hubSpotData = {
|
|
6
|
+
title: {{ module.title|tojson }},
|
|
7
|
+
tagline: {{ module.tagline|tojson }},
|
|
8
|
+
show_feature_cards: {{ module.show_feature_cards|lower }},
|
|
9
|
+
accent_color: {{ module.accent_color|tojson }},
|
|
10
|
+
hero_style: {{ module.hero_style|tojson }},
|
|
11
|
+
assetBaseUrl: "__PORTAL_CDN_BASE__",
|
|
12
|
+
buildId: "__PORTAL_BUILD_ID__",
|
|
13
|
+
isInEditor: {{ in_cms_editor|lower }}
|
|
14
|
+
};
|
|
15
|
+
</script>
|
|
16
|
+
{% set portal_cdn_base = "__PORTAL_CDN_BASE__" %}
|
|
17
|
+
{% set portal_vendor_script = "__PORTAL_VENDOR_FILE__" %}
|
|
18
|
+
{% set portal_app_script = "__PORTAL_APP_FILE__" %}
|
|
19
|
+
{% set portal_app_css = "__PORTAL_APP_CSS_FILE__" %}
|
|
20
|
+
{{ require_css(portal_cdn_base ~ '/' ~ portal_app_css, { position: 'footer' }) }}
|
|
21
|
+
{{ require_js(portal_cdn_base ~ '/' ~ portal_vendor_script, { position: 'footer', defer: true }) }}
|
|
22
|
+
{{ require_js(portal_cdn_base ~ '/' ~ portal_app_script, { position: 'footer', defer: true }) }}
|
|
1
23
|
<div id="app"></div>
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CDN application chunk — mounts after vendor.js (HubSpot require_js order).
|
|
3
3
|
*/
|
|
4
|
+
import '@/assets/css/main.dev.css'
|
|
5
|
+
|
|
6
|
+
import { resolvePortalApiEndpoint } from '@/portal/api-endpoint'
|
|
4
7
|
import { mountPortal } from '@/portal/mount-portal'
|
|
5
8
|
|
|
6
9
|
declare const __PORTAL_BUILD_ID__: string | undefined
|
|
@@ -10,6 +13,7 @@ declare global {
|
|
|
10
13
|
WoodsPortal?: {
|
|
11
14
|
mount: typeof mountPortal
|
|
12
15
|
version: string
|
|
16
|
+
apiEndpoint: string
|
|
13
17
|
}
|
|
14
18
|
}
|
|
15
19
|
}
|
|
@@ -20,6 +24,7 @@ const buildId =
|
|
|
20
24
|
window.WoodsPortal = {
|
|
21
25
|
mount: mountPortal,
|
|
22
26
|
version: buildId,
|
|
27
|
+
apiEndpoint: resolvePortalApiEndpoint(),
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
async function autoMount() {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WoodsPortal REST base URL — baked from .env.stg / .env.prod at CDN build time.
|
|
3
|
+
* Referenced from app-entry so verify-cdn-api-endpoint.mjs can refuse wrong-tier publishes.
|
|
4
|
+
*/
|
|
5
|
+
export function resolvePortalApiEndpoint(): string {
|
|
6
|
+
const raw = import.meta.env.VITE_PUBLIC_REST_API_ENDPOINT
|
|
7
|
+
if (typeof raw !== 'string' || !raw.trim()) {
|
|
8
|
+
return ''
|
|
9
|
+
}
|
|
10
|
+
return raw.replace(/\/$/, '')
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@woodsportal/hubspot-kit/vite/vite.config.cdn-app.mjs'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@woodsportal/hubspot-kit/vite/vite.config.cdn-esm.mjs'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@woodsportal/hubspot-kit/vite/vite.config.cdn-vendor.mjs'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@woodsportal/hubspot-kit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.31",
|
|
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",
|
package/scripts/build-cdn.mjs
CHANGED
|
@@ -10,6 +10,12 @@ import { findProjectRoot } from './project-root.mjs'
|
|
|
10
10
|
import { envWithPortalBuildId } from './resolve-portal-build-id.mjs'
|
|
11
11
|
import { envForPortalBuild, defaultDatadogFlag } from './portal-vite-env.mjs'
|
|
12
12
|
import { resolvePortalViteMode } from './portal-env-mode.mjs'
|
|
13
|
+
import { resolveViteConfig } from './resolve-vite-config.mjs'
|
|
14
|
+
import {
|
|
15
|
+
exitOnSpawnFailure,
|
|
16
|
+
portalSubprocessStdio,
|
|
17
|
+
portalViteLogArgs,
|
|
18
|
+
} from './portal-subprocess-stdio.mjs'
|
|
13
19
|
|
|
14
20
|
const root = findProjectRoot()
|
|
15
21
|
|
|
@@ -22,15 +28,13 @@ const env = envForPortalBuild(mode, root, {
|
|
|
22
28
|
function runWithEnv(config, buildMode) {
|
|
23
29
|
const result = spawnSync(
|
|
24
30
|
'yarn',
|
|
25
|
-
['vite', 'build', '--config', config, '--mode', buildMode],
|
|
26
|
-
{ cwd: root, stdio:
|
|
31
|
+
['vite', 'build', '--config', config, '--mode', buildMode, ...portalViteLogArgs()],
|
|
32
|
+
{ cwd: root, stdio: portalSubprocessStdio(), env, encoding: 'utf8' },
|
|
27
33
|
)
|
|
28
|
-
|
|
29
|
-
process.exit(result.status ?? 1)
|
|
30
|
-
}
|
|
34
|
+
exitOnSpawnFailure(result)
|
|
31
35
|
}
|
|
32
36
|
|
|
33
|
-
runWithEnv('vite.config.cdn-vendor.mjs', mode)
|
|
34
|
-
runWithEnv('vite.config.cdn-app.mjs', mode)
|
|
37
|
+
runWithEnv(resolveViteConfig('vite.config.cdn-vendor.mjs', root), mode)
|
|
38
|
+
runWithEnv(resolveViteConfig('vite.config.cdn-app.mjs', root), mode)
|
|
35
39
|
|
|
36
40
|
console.log('[build-cdn] vendor + app chunks written to dist/cdn/')
|
|
@@ -92,10 +92,10 @@ export function injectCdnManifestIntoModuleHtml(html, manifestPath) {
|
|
|
92
92
|
/**
|
|
93
93
|
* @param {string} moduleDir absolute path to dist/WoodsPortal.Module
|
|
94
94
|
* @param {string} publicDir absolute path to public/
|
|
95
|
-
* @param {{ manifestPath?: string, bootstrapRawLimit?: number }} [options]
|
|
95
|
+
* @param {{ manifestPath?: string, bootstrapRawLimit?: number, allowEmptyModuleCss?: boolean }} [options]
|
|
96
96
|
*/
|
|
97
97
|
export function finalizeHubSpotModuleDist(moduleDir, publicDir, options = {}) {
|
|
98
|
-
const { bootstrapRawLimit = 200 * 1024 } = options
|
|
98
|
+
const { bootstrapRawLimit = 200 * 1024, allowEmptyModuleCss = false } = options
|
|
99
99
|
const manifestPath =
|
|
100
100
|
options.manifestPath ?? resolve(moduleDir, '../cdn/manifest.json')
|
|
101
101
|
mkdirSync(moduleDir, { recursive: true })
|
|
@@ -110,10 +110,14 @@ export function finalizeHubSpotModuleDist(moduleDir, publicDir, options = {}) {
|
|
|
110
110
|
|
|
111
111
|
const cssPath = resolve(moduleDir, 'module.css')
|
|
112
112
|
if (!existsSync(cssPath)) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
if (allowEmptyModuleCss) {
|
|
114
|
+
writeFileSync(cssPath, '/* component styles load from CDN app.*.css */\n', 'utf8')
|
|
115
|
+
} else {
|
|
116
|
+
const entries = existsSync(moduleDir) ? readdirSync(moduleDir) : []
|
|
117
|
+
throw new Error(
|
|
118
|
+
`Missing ${cssPath} — Vite lib build must emit module.css first. Found in ${moduleDir}: ${entries.join(', ') || '(empty)'}`,
|
|
119
|
+
)
|
|
120
|
+
}
|
|
117
121
|
}
|
|
118
122
|
|
|
119
123
|
for (const file of ['module.html', 'fields.json']) {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Child process stdio — quiet when wp shows @clack task spinners.
|
|
3
|
+
* Set WP_HS_QUIET_BUILD=1 from wp build/deploy/publish (interactive TTY, non-verbose).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export function isQuietBuild() {
|
|
7
|
+
return process.env.WP_HS_QUIET_BUILD === '1'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** @deprecated alias — same as isQuietBuild */
|
|
11
|
+
export const isQuietSubprocess = isQuietBuild
|
|
12
|
+
|
|
13
|
+
/** @returns {'inherit' | 'pipe'} */
|
|
14
|
+
export function portalSubprocessStdio() {
|
|
15
|
+
return isQuietBuild() ? 'pipe' : 'inherit'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Extra Vite CLI args when output is piped away from the TTY spinner. */
|
|
19
|
+
export function portalViteLogArgs() {
|
|
20
|
+
return isQuietBuild() ? ['--logLevel', 'warn'] : []
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {import('node:child_process').SpawnSyncReturns<string>} result
|
|
25
|
+
*/
|
|
26
|
+
export function exitOnSpawnFailure(result) {
|
|
27
|
+
const status = result.status ?? 1
|
|
28
|
+
if (status === 0) {
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
if (typeof result.stdout === 'string' && result.stdout.length > 0) {
|
|
32
|
+
process.stdout.write(result.stdout)
|
|
33
|
+
}
|
|
34
|
+
if (typeof result.stderr === 'string' && result.stderr.length > 0) {
|
|
35
|
+
process.stderr.write(result.stderr)
|
|
36
|
+
}
|
|
37
|
+
process.exit(status)
|
|
38
|
+
}
|
|
@@ -25,14 +25,25 @@ import {
|
|
|
25
25
|
import { dirname, join, resolve } from 'node:path'
|
|
26
26
|
import { fileURLToPath } from 'node:url'
|
|
27
27
|
|
|
28
|
-
import { loadPortalCdnConfig, jsDelivrBaseUrl
|
|
28
|
+
import { loadPortalCdnConfig, jsDelivrBaseUrl } from './load-portal-cdn-config.mjs'
|
|
29
|
+
import { findProjectRoot } from './project-root.mjs'
|
|
30
|
+
import {
|
|
31
|
+
exitOnSpawnFailure,
|
|
32
|
+
isQuietBuild,
|
|
33
|
+
portalSubprocessStdio,
|
|
34
|
+
} from './portal-subprocess-stdio.mjs'
|
|
29
35
|
import { resolvePortalBuildId } from './resolve-portal-build-id.mjs'
|
|
30
36
|
|
|
37
|
+
const root = findProjectRoot()
|
|
38
|
+
|
|
31
39
|
const args = new Set(process.argv.slice(2))
|
|
32
40
|
const dryRun = args.has('--dry-run')
|
|
33
41
|
const skipJsDelivrCheck = args.has('--skip-jsdelivr-check')
|
|
34
42
|
|
|
35
43
|
function log(msg) {
|
|
44
|
+
if (isQuietBuild()) {
|
|
45
|
+
return
|
|
46
|
+
}
|
|
36
47
|
console.log(`[publish:cdn] ${msg}`)
|
|
37
48
|
}
|
|
38
49
|
|
|
@@ -43,10 +54,17 @@ function fail(msg) {
|
|
|
43
54
|
|
|
44
55
|
function run(cmd, cmdArgs, options = {}) {
|
|
45
56
|
const result = spawnSync(cmd, cmdArgs, {
|
|
46
|
-
stdio:
|
|
57
|
+
stdio: portalSubprocessStdio(),
|
|
58
|
+
encoding: 'utf8',
|
|
47
59
|
...options,
|
|
48
60
|
})
|
|
49
61
|
if (result.status !== 0) {
|
|
62
|
+
if (typeof result.stdout === 'string' && result.stdout.length > 0) {
|
|
63
|
+
process.stdout.write(result.stdout)
|
|
64
|
+
}
|
|
65
|
+
if (typeof result.stderr === 'string' && result.stderr.length > 0) {
|
|
66
|
+
process.stderr.write(result.stderr)
|
|
67
|
+
}
|
|
50
68
|
fail(`Command failed: ${cmd} ${cmdArgs.join(' ')}`)
|
|
51
69
|
}
|
|
52
70
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a Vite config path: prefer project root, fall back to kit vite presets.
|
|
3
|
+
*/
|
|
4
|
+
import { existsSync } from 'node:fs'
|
|
5
|
+
import { basename, resolve } from 'node:path'
|
|
6
|
+
|
|
7
|
+
import { findProjectRoot, getKitRoot } from './project-root.mjs'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} configArg absolute path or filename (e.g. vite.config.cdn-vendor.mjs)
|
|
11
|
+
* @param {string} [projectRoot]
|
|
12
|
+
*/
|
|
13
|
+
export function resolveViteConfig(configArg, projectRoot = findProjectRoot()) {
|
|
14
|
+
const isAbsolute = configArg.startsWith('/')
|
|
15
|
+
const localPath = isAbsolute ? configArg : resolve(projectRoot, configArg)
|
|
16
|
+
if (existsSync(localPath)) {
|
|
17
|
+
return localPath
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const kitPath = resolve(getKitRoot(), 'vite', basename(configArg))
|
|
21
|
+
if (existsSync(kitPath)) {
|
|
22
|
+
return kitPath
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return localPath
|
|
26
|
+
}
|
|
@@ -39,7 +39,10 @@ const bootstrapRawLimit = config?.projectType === 'module-monolith' ? null : 200
|
|
|
39
39
|
const { jsBytes, cssBytes } = finalizeHubSpotModuleDist(
|
|
40
40
|
moduleOutDir(root),
|
|
41
41
|
resolveModulePublicDir(root, config),
|
|
42
|
-
{
|
|
42
|
+
{
|
|
43
|
+
bootstrapRawLimit,
|
|
44
|
+
allowEmptyModuleCss: config?.projectType === 'module-hybrid-cdn',
|
|
45
|
+
},
|
|
43
46
|
)
|
|
44
47
|
|
|
45
48
|
console.log(
|
|
@@ -9,16 +9,24 @@ import { spawnSync } from 'node:child_process'
|
|
|
9
9
|
import { defaultDatadogFlag, envForPortalBuild } from './portal-vite-env.mjs'
|
|
10
10
|
import { resolvePortalViteMode } from './portal-env-mode.mjs'
|
|
11
11
|
import { findProjectRoot } from './project-root.mjs'
|
|
12
|
+
import { resolveViteConfig } from './resolve-vite-config.mjs'
|
|
13
|
+
import {
|
|
14
|
+
exitOnSpawnFailure,
|
|
15
|
+
portalSubprocessStdio,
|
|
16
|
+
portalViteLogArgs,
|
|
17
|
+
} from './portal-subprocess-stdio.mjs'
|
|
12
18
|
|
|
13
19
|
const root = findProjectRoot()
|
|
14
|
-
const
|
|
20
|
+
const configArg = process.argv[2]
|
|
15
21
|
const mode = resolvePortalViteMode()
|
|
16
22
|
|
|
17
|
-
if (!
|
|
23
|
+
if (!configArg) {
|
|
18
24
|
console.error('Usage: node scripts/run-vite-build.mjs <vite-config.mjs> [--stg]')
|
|
19
25
|
process.exit(1)
|
|
20
26
|
}
|
|
21
27
|
|
|
28
|
+
const config = resolveViteConfig(configArg, root)
|
|
29
|
+
|
|
22
30
|
const env = envForPortalBuild(mode, root, {
|
|
23
31
|
VITE_DATADOG_RUM_ENABLED: defaultDatadogFlag(mode),
|
|
24
32
|
WP_HS_PROJECT_ROOT: root,
|
|
@@ -26,8 +34,8 @@ const env = envForPortalBuild(mode, root, {
|
|
|
26
34
|
|
|
27
35
|
const result = spawnSync(
|
|
28
36
|
'npx',
|
|
29
|
-
['vite', 'build', '--config', config, '--mode', mode],
|
|
30
|
-
{ cwd: root, stdio:
|
|
37
|
+
['vite', 'build', '--config', config, '--mode', mode, ...portalViteLogArgs()],
|
|
38
|
+
{ cwd: root, stdio: portalSubprocessStdio(), env, shell: true, encoding: 'utf8' },
|
|
31
39
|
)
|
|
32
40
|
|
|
33
|
-
|
|
41
|
+
exitOnSpawnFailure(result)
|
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
"name": "tagline",
|
|
11
11
|
"label": "Tagline",
|
|
12
12
|
"type": "text",
|
|
13
|
+
"display": "textarea",
|
|
13
14
|
"tab": "CONTENT",
|
|
14
|
-
"help_text": "Subtitle shown under the hero title
|
|
15
|
+
"help_text": "Subtitle shown under the hero title.",
|
|
15
16
|
"default": "Customize this preview with Ctrl+Shift+Z, then replace StarterPage with your portal app."
|
|
16
17
|
},
|
|
17
18
|
{
|
|
@@ -27,12 +28,16 @@
|
|
|
27
28
|
"label": "Accent color",
|
|
28
29
|
"type": "color",
|
|
29
30
|
"tab": "STYLE",
|
|
30
|
-
"default": {
|
|
31
|
+
"default": {
|
|
32
|
+
"color": "#0091ae",
|
|
33
|
+
"opacity": 100
|
|
34
|
+
}
|
|
31
35
|
},
|
|
32
36
|
{
|
|
33
37
|
"name": "hero_style",
|
|
34
38
|
"label": "Hero style",
|
|
35
39
|
"type": "choice",
|
|
40
|
+
"display": "select",
|
|
36
41
|
"tab": "STYLE",
|
|
37
42
|
"choices": [
|
|
38
43
|
["gradient", "Gradient"],
|
|
@@ -1 +1,23 @@
|
|
|
1
|
+
{# Hybrid CDN — React app loads from jsDelivr; module.js is a thin bootstrap only. #}
|
|
2
|
+
{% set in_cms_editor = is_in_editor or is_in_page_editor or is_in_previewer %}
|
|
3
|
+
<script>
|
|
4
|
+
window.hsInEditor = {{ in_cms_editor|lower }};
|
|
5
|
+
window.hubSpotData = {
|
|
6
|
+
title: {{ module.title|tojson }},
|
|
7
|
+
tagline: {{ module.tagline|tojson }},
|
|
8
|
+
show_feature_cards: {{ module.show_feature_cards|lower }},
|
|
9
|
+
accent_color: {{ module.accent_color|tojson }},
|
|
10
|
+
hero_style: {{ module.hero_style|tojson }},
|
|
11
|
+
assetBaseUrl: "__PORTAL_CDN_BASE__",
|
|
12
|
+
buildId: "__PORTAL_BUILD_ID__",
|
|
13
|
+
isInEditor: {{ in_cms_editor|lower }}
|
|
14
|
+
};
|
|
15
|
+
</script>
|
|
16
|
+
{% set portal_cdn_base = "__PORTAL_CDN_BASE__" %}
|
|
17
|
+
{% set portal_vendor_script = "__PORTAL_VENDOR_FILE__" %}
|
|
18
|
+
{% set portal_app_script = "__PORTAL_APP_FILE__" %}
|
|
19
|
+
{% set portal_app_css = "__PORTAL_APP_CSS_FILE__" %}
|
|
20
|
+
{{ require_css(portal_cdn_base ~ '/' ~ portal_app_css, { position: 'footer' }) }}
|
|
21
|
+
{{ require_js(portal_cdn_base ~ '/' ~ portal_vendor_script, { position: 'footer', defer: true }) }}
|
|
22
|
+
{{ require_js(portal_cdn_base ~ '/' ~ portal_app_script, { position: 'footer', defer: true }) }}
|
|
1
23
|
<div id="app"></div>
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CDN application chunk — mounts after vendor.js (HubSpot require_js order).
|
|
3
3
|
*/
|
|
4
|
+
import '@/assets/css/main.dev.css'
|
|
5
|
+
|
|
6
|
+
import { resolvePortalApiEndpoint } from '@/portal/api-endpoint'
|
|
4
7
|
import { mountPortal } from '@/portal/mount-portal'
|
|
5
8
|
|
|
6
9
|
declare const __PORTAL_BUILD_ID__: string | undefined
|
|
@@ -10,6 +13,7 @@ declare global {
|
|
|
10
13
|
WoodsPortal?: {
|
|
11
14
|
mount: typeof mountPortal
|
|
12
15
|
version: string
|
|
16
|
+
apiEndpoint: string
|
|
13
17
|
}
|
|
14
18
|
}
|
|
15
19
|
}
|
|
@@ -20,6 +24,7 @@ const buildId =
|
|
|
20
24
|
window.WoodsPortal = {
|
|
21
25
|
mount: mountPortal,
|
|
22
26
|
version: buildId,
|
|
27
|
+
apiEndpoint: resolvePortalApiEndpoint(),
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
async function autoMount() {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WoodsPortal REST base URL — baked from .env.stg / .env.prod at CDN build time.
|
|
3
|
+
* Referenced from app-entry so verify-cdn-api-endpoint.mjs can refuse wrong-tier publishes.
|
|
4
|
+
*/
|
|
5
|
+
export function resolvePortalApiEndpoint(): string {
|
|
6
|
+
const raw = import.meta.env.VITE_PUBLIC_REST_API_ENDPOINT
|
|
7
|
+
if (typeof raw !== 'string' || !raw.trim()) {
|
|
8
|
+
return ''
|
|
9
|
+
}
|
|
10
|
+
return raw.replace(/\/$/, '')
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@woodsportal/hubspot-kit/vite/vite.config.cdn-app.mjs'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@woodsportal/hubspot-kit/vite/vite.config.cdn-esm.mjs'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@woodsportal/hubspot-kit/vite/vite.config.cdn-vendor.mjs'
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defineConfig } from 'vite'
|
|
2
2
|
import { resolve } from 'node:path'
|
|
3
|
+
import { readWoodsportalConfig } from '../scripts/read-woodsportal-config.mjs'
|
|
3
4
|
import {
|
|
4
5
|
portalDefine,
|
|
5
6
|
portalPlugins,
|
|
@@ -7,17 +8,22 @@ import {
|
|
|
7
8
|
root,
|
|
8
9
|
} from '../scripts/vite/portal-build-shared.js'
|
|
9
10
|
|
|
11
|
+
const { config } = readWoodsportalConfig(root)
|
|
12
|
+
const outDir = config?.module?.outDir ?? 'dist/WoodsPortal.Module'
|
|
13
|
+
const bootstrapEntry =
|
|
14
|
+
config?.entries?.hubspotBootstrap ?? 'src/hubspot/bootstrap.ts'
|
|
15
|
+
|
|
10
16
|
export default defineConfig(({ mode }) => ({
|
|
11
17
|
plugins: portalPlugins(mode, { codeSplitting: false }),
|
|
12
18
|
resolve: portalResolve(mode),
|
|
13
19
|
define: portalDefine(mode),
|
|
14
20
|
build: {
|
|
15
|
-
outDir: resolve(root,
|
|
21
|
+
outDir: resolve(root, outDir),
|
|
16
22
|
emptyOutDir: true,
|
|
17
23
|
copyPublicDir: false,
|
|
18
24
|
cssCodeSplit: false,
|
|
19
25
|
lib: {
|
|
20
|
-
entry: resolve(root,
|
|
26
|
+
entry: resolve(root, bootstrapEntry),
|
|
21
27
|
name: 'WoodsPortalHubSpotBootstrap',
|
|
22
28
|
fileName: () => 'module.js',
|
|
23
29
|
formats: ['iife'],
|
package/dist/chunk-GTTIJVAR.js
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/lib/exec.ts
|
|
4
|
-
import { spawn, spawnSync } from "child_process";
|
|
5
|
-
|
|
6
|
-
// src/lib/errors.ts
|
|
7
|
-
var ExitCode = {
|
|
8
|
-
SUCCESS: 0,
|
|
9
|
-
USER_ERROR: 1,
|
|
10
|
-
PREREQUISITE: 2,
|
|
11
|
-
BUILD_FAILED: 3,
|
|
12
|
-
PUBLISH_FAILED: 4,
|
|
13
|
-
AUDIT_FAILED: 5,
|
|
14
|
-
INTERNAL: 10
|
|
15
|
-
};
|
|
16
|
-
var WpHsError = class extends Error {
|
|
17
|
-
exitCode;
|
|
18
|
-
remediation;
|
|
19
|
-
constructor(message, options = {}) {
|
|
20
|
-
super(message, { cause: options.cause });
|
|
21
|
-
this.name = "WpHsError";
|
|
22
|
-
this.exitCode = options.exitCode ?? ExitCode.USER_ERROR;
|
|
23
|
-
this.remediation = options.remediation;
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
function isWpHsError(error) {
|
|
27
|
-
return error instanceof WpHsError;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// src/lib/exec.ts
|
|
31
|
-
var SECRET_PATTERN = /(token|secret|password|pat|key)=/i;
|
|
32
|
-
function maskEnv(env) {
|
|
33
|
-
const masked = { ...env };
|
|
34
|
-
for (const key of Object.keys(masked)) {
|
|
35
|
-
if (/token|secret|password|pat|auth/i.test(key) && masked[key]) {
|
|
36
|
-
masked[key] = "[redacted]";
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return masked;
|
|
40
|
-
}
|
|
41
|
-
function runCommand(command, args, options) {
|
|
42
|
-
const { cwd, env = process.env, logger, dryRun = false, okCodes = [0] } = options;
|
|
43
|
-
if (dryRun) {
|
|
44
|
-
logger?.info(`[dry-run] ${command} ${args.join(" ")}`, { cwd });
|
|
45
|
-
return { status: 0, stdout: "", stderr: "" };
|
|
46
|
-
}
|
|
47
|
-
logger?.debug(`exec: ${command} ${args.join(" ")}`, {
|
|
48
|
-
cwd,
|
|
49
|
-
env: logger ? maskEnv(env) : void 0
|
|
50
|
-
});
|
|
51
|
-
const spawnOpts = {
|
|
52
|
-
cwd,
|
|
53
|
-
env,
|
|
54
|
-
encoding: "utf8",
|
|
55
|
-
stdio: ["inherit", "pipe", "pipe"]
|
|
56
|
-
};
|
|
57
|
-
const result = spawnSync(command, args, spawnOpts);
|
|
58
|
-
const status = result.status ?? 1;
|
|
59
|
-
const stdout = typeof result.stdout === "string" ? result.stdout : "";
|
|
60
|
-
const stderr = typeof result.stderr === "string" ? result.stderr : "";
|
|
61
|
-
if (!okCodes.includes(status)) {
|
|
62
|
-
const detail = stderr.trim() || stdout.trim() || `exit ${status}`;
|
|
63
|
-
if (SECRET_PATTERN.test(detail)) {
|
|
64
|
-
throw new WpHsError(`Command failed: ${command}`, {
|
|
65
|
-
exitCode: ExitCode.USER_ERROR,
|
|
66
|
-
remediation: "Re-run with --verbose for details (secrets are never logged)."
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
throw new WpHsError(`Command failed: ${command} ${args.join(" ")}
|
|
70
|
-
${detail}`, {
|
|
71
|
-
exitCode: ExitCode.USER_ERROR
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
return { status, stdout, stderr };
|
|
75
|
-
}
|
|
76
|
-
function runNodeScript(scriptPath, args, options) {
|
|
77
|
-
runCommand(process.execPath, [scriptPath, ...args], options);
|
|
78
|
-
}
|
|
79
|
-
function runNodeScriptLongRunning(scriptPath, args, options) {
|
|
80
|
-
const { cwd, env = process.env, logger, dryRun = false } = options;
|
|
81
|
-
if (dryRun) {
|
|
82
|
-
logger?.info(`[dry-run] node ${scriptPath} ${args.join(" ")}`, { cwd });
|
|
83
|
-
return Promise.resolve(0);
|
|
84
|
-
}
|
|
85
|
-
return new Promise((resolve, reject) => {
|
|
86
|
-
const child = spawn(process.execPath, [scriptPath, ...args], {
|
|
87
|
-
cwd,
|
|
88
|
-
env,
|
|
89
|
-
stdio: "inherit"
|
|
90
|
-
});
|
|
91
|
-
const forwardSignal = (signal) => {
|
|
92
|
-
if (!child.killed) {
|
|
93
|
-
child.kill(signal);
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
process.on("SIGINT", forwardSignal);
|
|
97
|
-
process.on("SIGTERM", forwardSignal);
|
|
98
|
-
child.on("error", (error) => {
|
|
99
|
-
process.off("SIGINT", forwardSignal);
|
|
100
|
-
process.off("SIGTERM", forwardSignal);
|
|
101
|
-
reject(error);
|
|
102
|
-
});
|
|
103
|
-
child.on("exit", (code, signal) => {
|
|
104
|
-
process.off("SIGINT", forwardSignal);
|
|
105
|
-
process.off("SIGTERM", forwardSignal);
|
|
106
|
-
if (signal === "SIGINT") {
|
|
107
|
-
resolve(130);
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
if (signal === "SIGTERM") {
|
|
111
|
-
resolve(143);
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
resolve(code ?? 1);
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export {
|
|
120
|
-
ExitCode,
|
|
121
|
-
WpHsError,
|
|
122
|
-
isWpHsError,
|
|
123
|
-
runCommand,
|
|
124
|
-
runNodeScript,
|
|
125
|
-
runNodeScriptLongRunning
|
|
126
|
-
};
|
|
127
|
-
//# sourceMappingURL=chunk-GTTIJVAR.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/exec.ts","../src/lib/errors.ts"],"sourcesContent":["import { spawn, spawnSync, type SpawnSyncOptions } from 'node:child_process'\n\nimport { WpHsError, ExitCode } from './errors.js'\nimport type { Logger } from './logger.js'\n\nconst SECRET_PATTERN = /(token|secret|password|pat|key)=/i\n\nfunction maskEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const masked: NodeJS.ProcessEnv = { ...env }\n for (const key of Object.keys(masked)) {\n if (/token|secret|password|pat|auth/i.test(key) && masked[key]) {\n masked[key] = '[redacted]'\n }\n }\n return masked\n}\n\nexport type RunOptions = {\n cwd: string\n env?: NodeJS.ProcessEnv\n logger?: Logger\n dryRun?: boolean\n timeoutMs?: number\n /** Expected exit code on success (default 0) */\n okCodes?: number[]\n}\n\nexport function runCommand(\n command: string,\n args: string[],\n options: RunOptions,\n): { status: number; stdout: string; stderr: string } {\n const { cwd, env = process.env, logger, dryRun = false, okCodes = [0] } = options\n\n if (dryRun) {\n logger?.info(`[dry-run] ${command} ${args.join(' ')}`, { cwd })\n return { status: 0, stdout: '', stderr: '' }\n }\n\n logger?.debug(`exec: ${command} ${args.join(' ')}`, {\n cwd,\n env: logger ? maskEnv(env) : undefined,\n })\n\n const spawnOpts: SpawnSyncOptions = {\n cwd,\n env,\n encoding: 'utf8',\n stdio: ['inherit', 'pipe', 'pipe'],\n }\n\n const result = spawnSync(command, args, spawnOpts)\n const status = result.status ?? 1\n const stdout = typeof result.stdout === 'string' ? result.stdout : ''\n const stderr = typeof result.stderr === 'string' ? result.stderr : ''\n\n if (!okCodes.includes(status)) {\n const detail = stderr.trim() || stdout.trim() || `exit ${status}`\n if (SECRET_PATTERN.test(detail)) {\n throw new WpHsError(`Command failed: ${command}`, {\n exitCode: ExitCode.USER_ERROR,\n remediation: 'Re-run with --verbose for details (secrets are never logged).',\n })\n }\n throw new WpHsError(`Command failed: ${command} ${args.join(' ')}\\n${detail}`, {\n exitCode: ExitCode.USER_ERROR,\n })\n }\n\n return { status, stdout, stderr }\n}\n\nexport function runNodeScript(\n scriptPath: string,\n args: string[],\n options: RunOptions,\n): void {\n runCommand(process.execPath, [scriptPath, ...args], options)\n}\n\n/**\n * Spawn a Node script with inherited stdio and forward SIGINT/SIGTERM (wp watch, long-running tools).\n * Unlike spawnSync, the parent event loop stays alive so Ctrl+C works while the child runs.\n */\nexport function runNodeScriptLongRunning(\n scriptPath: string,\n args: string[],\n options: RunOptions,\n): Promise<number> {\n const { cwd, env = process.env, logger, dryRun = false } = options\n\n if (dryRun) {\n logger?.info(`[dry-run] node ${scriptPath} ${args.join(' ')}`, { cwd })\n return Promise.resolve(0)\n }\n\n return new Promise((resolve, reject) => {\n const child = spawn(process.execPath, [scriptPath, ...args], {\n cwd,\n env,\n stdio: 'inherit',\n })\n\n const forwardSignal = (signal: NodeJS.Signals) => {\n if (!child.killed) {\n child.kill(signal)\n }\n }\n\n process.on('SIGINT', forwardSignal)\n process.on('SIGTERM', forwardSignal)\n\n child.on('error', (error) => {\n process.off('SIGINT', forwardSignal)\n process.off('SIGTERM', forwardSignal)\n reject(error)\n })\n\n child.on('exit', (code, signal) => {\n process.off('SIGINT', forwardSignal)\n process.off('SIGTERM', forwardSignal)\n\n if (signal === 'SIGINT') {\n resolve(130)\n return\n }\n if (signal === 'SIGTERM') {\n resolve(143)\n return\n }\n\n resolve(code ?? 1)\n })\n })\n}\n","export const ExitCode = {\n SUCCESS: 0,\n USER_ERROR: 1,\n PREREQUISITE: 2,\n BUILD_FAILED: 3,\n PUBLISH_FAILED: 4,\n AUDIT_FAILED: 5,\n INTERNAL: 10,\n} as const\n\nexport type ExitCodeValue = (typeof ExitCode)[keyof typeof ExitCode]\n\nexport class WpHsError extends Error {\n readonly exitCode: ExitCodeValue\n readonly remediation?: string\n\n constructor(\n message: string,\n options: { exitCode?: ExitCodeValue; remediation?: string; cause?: unknown } = {},\n ) {\n super(message, { cause: options.cause })\n this.name = 'WpHsError'\n this.exitCode = options.exitCode ?? ExitCode.USER_ERROR\n this.remediation = options.remediation\n }\n}\n\nexport function isWpHsError(error: unknown): error is WpHsError {\n return error instanceof WpHsError\n}\n"],"mappings":"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","names":[]}
|
|
File without changes
|