@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.
Files changed (32) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/chunk-4GOJPOW3.js +258 -0
  3. package/dist/chunk-4GOJPOW3.js.map +1 -0
  4. package/dist/cli.js +546 -240
  5. package/dist/cli.js.map +1 -1
  6. package/dist/{exec-U2OJATR3.js → exec-VY6BGWF3.js} +6 -2
  7. package/examples/greenfield/module-hybrid-cdn/public/fields.json +7 -2
  8. package/examples/greenfield/module-hybrid-cdn/public/module.html +22 -0
  9. package/examples/greenfield/module-hybrid-cdn/src/cdn/app-entry.ts +5 -0
  10. package/examples/greenfield/module-hybrid-cdn/src/portal/api-endpoint.ts +11 -0
  11. package/examples/greenfield/module-hybrid-cdn/vite.config.cdn-app.mjs +1 -0
  12. package/examples/greenfield/module-hybrid-cdn/vite.config.cdn-esm.mjs +1 -0
  13. package/examples/greenfield/module-hybrid-cdn/vite.config.cdn-vendor.mjs +1 -0
  14. package/package.json +1 -1
  15. package/scripts/build-cdn.mjs +11 -7
  16. package/scripts/hubspot-module-postbuild.mjs +10 -6
  17. package/scripts/portal-subprocess-stdio.mjs +38 -0
  18. package/scripts/publish-cdn-github.mjs +20 -2
  19. package/scripts/resolve-vite-config.mjs +26 -0
  20. package/scripts/run-hubspot-module-postbuild.mjs +4 -1
  21. package/scripts/run-vite-build.mjs +13 -5
  22. package/templates/module-hybrid-cdn/public/fields.json +7 -2
  23. package/templates/module-hybrid-cdn/public/module.html +22 -0
  24. package/templates/module-hybrid-cdn/src/cdn/app-entry.ts +5 -0
  25. package/templates/module-hybrid-cdn/src/portal/api-endpoint.ts +11 -0
  26. package/templates/module-hybrid-cdn/vite.config.cdn-app.mjs +1 -0
  27. package/templates/module-hybrid-cdn/vite.config.cdn-esm.mjs +1 -0
  28. package/templates/module-hybrid-cdn/vite.config.cdn-vendor.mjs +1 -0
  29. package/vite/vite.config.hubspot.mjs +8 -2
  30. package/dist/chunk-GTTIJVAR.js +0 -127
  31. package/dist/chunk-GTTIJVAR.js.map +0 -1
  32. /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-GTTIJVAR.js";
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-U2OJATR3.js.map
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 in the local dev preview.",
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": { "color": "#0091ae", "opacity": 100 }
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.30",
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",
@@ -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: 'inherit', env },
31
+ ['vite', 'build', '--config', config, '--mode', buildMode, ...portalViteLogArgs()],
32
+ { cwd: root, stdio: portalSubprocessStdio(), env, encoding: 'utf8' },
27
33
  )
28
- if (result.status !== 0) {
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
- const entries = existsSync(moduleDir) ? readdirSync(moduleDir) : []
114
- throw new Error(
115
- `Missing ${cssPath} — Vite lib build must emit module.css first. Found in ${moduleDir}: ${entries.join(', ') || '(empty)'}`,
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, root } from './load-portal-cdn-config.mjs'
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: 'inherit',
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
- { bootstrapRawLimit },
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 config = process.argv[2]
20
+ const configArg = process.argv[2]
15
21
  const mode = resolvePortalViteMode()
16
22
 
17
- if (!config) {
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: 'inherit', env, shell: true },
37
+ ['vite', 'build', '--config', config, '--mode', mode, ...portalViteLogArgs()],
38
+ { cwd: root, stdio: portalSubprocessStdio(), env, shell: true, encoding: 'utf8' },
31
39
  )
32
40
 
33
- process.exit(result.status ?? 1)
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 in the local dev preview.",
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": { "color": "#0091ae", "opacity": 100 }
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, 'dist/WoodsPortal.Module'),
21
+ outDir: resolve(root, outDir),
16
22
  emptyOutDir: true,
17
23
  copyPublicDir: false,
18
24
  cssCodeSplit: false,
19
25
  lib: {
20
- entry: resolve(root, 'src/hubspot/bootstrap.ts'),
26
+ entry: resolve(root, bootstrapEntry),
21
27
  name: 'WoodsPortalHubSpotBootstrap',
22
28
  fileName: () => 'module.js',
23
29
  formats: ['iife'],
@@ -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":[]}