@woodsportal/hubspot-kit 1.0.2

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 (135) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/LICENSE +21 -0
  3. package/README.md +62 -0
  4. package/SECURITY.md +27 -0
  5. package/dist/chunk-JDGIAYHX.js +87 -0
  6. package/dist/chunk-JDGIAYHX.js.map +1 -0
  7. package/dist/chunk-U4KFGTEW.js +136 -0
  8. package/dist/chunk-U4KFGTEW.js.map +1 -0
  9. package/dist/cli.js +1849 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/exec-K5C6LZP4.js +10 -0
  12. package/dist/exec-K5C6LZP4.js.map +1 -0
  13. package/dist/index.d.ts +228 -0
  14. package/dist/index.js +204 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/vite/index.d.ts +17 -0
  17. package/dist/vite/index.js +9 -0
  18. package/dist/vite/index.js.map +1 -0
  19. package/examples/README.md +12 -0
  20. package/examples/fixtures/adopted-module-hybrid/public/fields.json +1 -0
  21. package/examples/fixtures/adopted-module-hybrid/public/module.html +1 -0
  22. package/examples/fixtures/adopted-module-hybrid/woodscli.json +7 -0
  23. package/examples/fixtures/adopted-module-monolith/public/fields.json +1 -0
  24. package/examples/fixtures/adopted-module-monolith/woodscli.json +7 -0
  25. package/examples/fixtures/adopted-theme-minimal/src/theme/templates/page.html +1 -0
  26. package/examples/fixtures/adopted-theme-minimal/src/theme/theme.json +1 -0
  27. package/examples/fixtures/adopted-theme-minimal/woodscli.json +7 -0
  28. package/examples/fixtures/existing-module-hybrid/public/fields.json +1 -0
  29. package/examples/fixtures/existing-module-hybrid/public/module.html +1 -0
  30. package/examples/fixtures/existing-theme-minimal/templates/page.html +1 -0
  31. package/examples/fixtures/existing-theme-minimal/theme.json +1 -0
  32. package/examples/greenfield/module-hybrid-cdn/README.md +9 -0
  33. package/examples/greenfield/module-hybrid-cdn/index.html +1 -0
  34. package/examples/greenfield/module-hybrid-cdn/package.json +14 -0
  35. package/examples/greenfield/module-hybrid-cdn/public/fields.json +1 -0
  36. package/examples/greenfield/module-hybrid-cdn/public/module.html +1 -0
  37. package/examples/greenfield/module-hybrid-cdn/src/main.tsx +2 -0
  38. package/examples/greenfield/module-hybrid-cdn/vite.config.dev.mjs +1 -0
  39. package/examples/greenfield/module-hybrid-cdn/vite.config.hubspot.mjs +1 -0
  40. package/examples/greenfield/module-hybrid-cdn/woodscli.json +8 -0
  41. package/examples/greenfield/module-hybrid-cdn/wphs.adapter.ts +7 -0
  42. package/examples/greenfield/module-monolith/package.json +12 -0
  43. package/examples/greenfield/module-monolith/public/fields.json +1 -0
  44. package/examples/greenfield/module-monolith/vite.config.dev.mjs +1 -0
  45. package/examples/greenfield/module-monolith/vite.config.monolith.mjs +1 -0
  46. package/examples/greenfield/module-monolith/woodscli.json +25 -0
  47. package/examples/greenfield/module-monolith/wphs.adapter.ts +7 -0
  48. package/examples/greenfield/theme-starter/package.json +11 -0
  49. package/examples/greenfield/theme-starter/src/theme/css/main.css +1 -0
  50. package/examples/greenfield/theme-starter/src/theme/templates/base.html +1 -0
  51. package/examples/greenfield/theme-starter/src/theme/templates/page.html +1 -0
  52. package/examples/greenfield/theme-starter/src/theme/theme.json +1 -0
  53. package/examples/greenfield/theme-starter/theme.json +6 -0
  54. package/examples/greenfield/theme-starter/vite.config.dev.mjs +1 -0
  55. package/examples/greenfield/theme-starter/woodscli.json +7 -0
  56. package/examples/manifest.json +20 -0
  57. package/examples/smoke/run-matrix.mjs +100 -0
  58. package/package.json +94 -0
  59. package/scripts/audit-module-config-fields.mjs +73 -0
  60. package/scripts/audit-parity.mjs +22 -0
  61. package/scripts/audit-tdz-split.mjs +111 -0
  62. package/scripts/build-cdn.mjs +36 -0
  63. package/scripts/check-hubspot-bundle-size.mjs +58 -0
  64. package/scripts/config-files.mjs +53 -0
  65. package/scripts/detect-layout.mjs +70 -0
  66. package/scripts/ensure-wphs-workspace.mjs +53 -0
  67. package/scripts/generate-cdn-manifest.mjs +92 -0
  68. package/scripts/generate-module-config-example.mjs +57 -0
  69. package/scripts/hubspot-module-postbuild.mjs +151 -0
  70. package/scripts/load-portal-cdn-config.mjs +91 -0
  71. package/scripts/migrate-sdk-3-api.mjs +144 -0
  72. package/scripts/module-config-dir.mjs +12 -0
  73. package/scripts/portal-env-mode.mjs +24 -0
  74. package/scripts/portal-vite-env.mjs +39 -0
  75. package/scripts/project-root.mjs +30 -0
  76. package/scripts/publish-cdn-github.mjs +273 -0
  77. package/scripts/read-woodsportal-config.mjs +40 -0
  78. package/scripts/resolve-cdn-base-url.mjs +32 -0
  79. package/scripts/resolve-consumer-dep.mjs +25 -0
  80. package/scripts/resolve-portal-build-id.mjs +67 -0
  81. package/scripts/run-dev.mjs +93 -0
  82. package/scripts/run-hubspot-module-postbuild.mjs +19 -0
  83. package/scripts/run-vite-build.mjs +33 -0
  84. package/scripts/tailwind/prefix-plugin.js +291 -0
  85. package/scripts/tailwind/prefix.env.js +11 -0
  86. package/scripts/tailwind/tailwind-content-plugin.js +27 -0
  87. package/scripts/tailwind/tailwind-content.js +267 -0
  88. package/scripts/verify-cdn-api-endpoint.mjs +68 -0
  89. package/scripts/vite/ensure-dev-kit-shims.js +49 -0
  90. package/scripts/vite/ensure-dev-module-config-shim.js +1 -0
  91. package/scripts/vite/module-config-store.js +242 -0
  92. package/scripts/vite/plugins.d.ts +9 -0
  93. package/scripts/vite/portal-build-shared.js +110 -0
  94. package/scripts/vite/react-dev-plugin-options.js +21 -0
  95. package/scripts/vite/resolve-from-project-root.js +31 -0
  96. package/scripts/vite/sdk-log-bridge.js +104 -0
  97. package/scripts/watch-hubspot.mjs +86 -0
  98. package/scripts/wphs-paths.mjs +115 -0
  99. package/scripts/write-audit-artifacts.mjs +56 -0
  100. package/templates/hubspot.config.yml.example +10 -0
  101. package/templates/module-hybrid-cdn/README.md +9 -0
  102. package/templates/module-hybrid-cdn/hubspot.config.yml.example +10 -0
  103. package/templates/module-hybrid-cdn/index.html +1 -0
  104. package/templates/module-hybrid-cdn/package.json +14 -0
  105. package/templates/module-hybrid-cdn/public/fields.json +1 -0
  106. package/templates/module-hybrid-cdn/public/module.html +1 -0
  107. package/templates/module-hybrid-cdn/src/main.tsx +2 -0
  108. package/templates/module-hybrid-cdn/vite.config.dev.mjs +1 -0
  109. package/templates/module-hybrid-cdn/vite.config.hubspot.mjs +1 -0
  110. package/templates/module-hybrid-cdn/woodscli.json +8 -0
  111. package/templates/module-hybrid-cdn/wphs.adapter.ts +7 -0
  112. package/templates/module-monolith/hubspot.config.yml.example +10 -0
  113. package/templates/module-monolith/package.json +12 -0
  114. package/templates/module-monolith/public/fields.json +1 -0
  115. package/templates/module-monolith/vite.config.dev.mjs +1 -0
  116. package/templates/module-monolith/vite.config.monolith.mjs +1 -0
  117. package/templates/module-monolith/woodscli.json +25 -0
  118. package/templates/module-monolith/wphs.adapter.ts +7 -0
  119. package/templates/theme-starter/hubspot.config.yml.example +10 -0
  120. package/templates/theme-starter/package.json +11 -0
  121. package/templates/theme-starter/src/theme/css/main.css +1 -0
  122. package/templates/theme-starter/src/theme/templates/base.html +1 -0
  123. package/templates/theme-starter/src/theme/templates/page.html +1 -0
  124. package/templates/theme-starter/src/theme/theme.json +1 -0
  125. package/templates/theme-starter/theme.json +6 -0
  126. package/templates/theme-starter/vite.config.dev.mjs +1 -0
  127. package/templates/theme-starter/woodscli.json +7 -0
  128. package/vite/vite.config.audit.mjs +33 -0
  129. package/vite/vite.config.cdn-app.mjs +44 -0
  130. package/vite/vite.config.cdn-esm.mjs +35 -0
  131. package/vite/vite.config.cdn-vendor.mjs +33 -0
  132. package/vite/vite.config.dev.mjs +62 -0
  133. package/vite/vite.config.hubspot.mjs +33 -0
  134. package/vite/vite.config.monolith.mjs +55 -0
  135. package/vite/vite.config.tdz-audit.mjs +58 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,31 @@
1
+ # Changelog
2
+
3
+ ## 1.0.2 — 2026-06-07
4
+
5
+ ### WoodsCLI product positioning
6
+ - Rebrand CLI/docs as **WoodsCLI** — HubSpot CMS dev kit by WoodsPortal
7
+ - Canonical dev command **`wp dev`** (`wp local` remains alias)
8
+ - **`.wphs/`** workspace (config, assets, fixtures, cache, audit baseline)
9
+ - `wp migrate`, `wp adopt`, `wp examples list|smoke`, `wp fields sync`, `wp audit secrets`
10
+ - `HubSpotKitAdapter` + `wphs.adapter.ts` consumer hook
11
+ - `projectType`-aware build/upload/watch; monolith Vite preset in kit
12
+ - Complete `module-hybrid-cdn`, `module-monolith`, `theme-starter` templates
13
+ - Examples smoke matrix + `npm run test:examples` in CI
14
+ - Production upload guard (`--yes` / `WP_HS_CONFIRM_PROD=1`)
15
+ - `--tier stg` support across build/deploy/publish
16
+
17
+ ## 1.0.1 — 2026-06-07
18
+
19
+ - Fix module-config overlay drawer width (CSS fallback + Tailwind scan kit sources)
20
+ - Default local workspace dir `.dev` (was `dev`); `moduleConfigDir` in `woodsportal.hs.json`
21
+ - Module-config UI: drill-down panel stack, toggle isolation, menu-options parity fixes
22
+ - Overlay shortcut `Ctrl+Shift+Z` / `Cmd+Shift+Z` (was `Ctrl+Shift+M`)
23
+ - Extract canonical routing helpers to `src/routing/` (`build-api-routes`, `route-format`)
24
+ - Tailwind content plugin scans kit `dev-module-config` paths on HMR
25
+
26
+ ## 1.0.0 — 2026-06-07
27
+
28
+ - Initial release: `wp` CLI with `doctor`, `init`, `dev`, `build`, `deploy`, `audit`
29
+ - Vite presets for hybrid CDN + HubSpot module builds
30
+ - Local module-config dev tooling (extracted from woodsportal-client-frontend)
31
+ - `woodsportal.hs.json` project configuration schema
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Digital Woods
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # WoodsCLI
2
+
3
+ > HubSpot CMS dev kit by WoodsPortal — modules and themes.
4
+ > `npm i @woodsportal/hubspot-kit` · `wp dev`
5
+
6
+ WoodsCLI (`wp`) is the development CLI for **HubSpot CMS custom modules** (React/JS + `fields.json`) and **themes** (templates, CSS, theme fields). It wraps `@hubspot/cli` for upload/watch and adds local preview, Content/Styles field editing (**Ctrl+Shift+Z**), and hybrid CDN or monolith build pipelines.
7
+
8
+ ## Two tracks
9
+
10
+ | Track | `projectType` | Quick start |
11
+ |-------|---------------|-------------|
12
+ | **Module (hybrid CDN)** | `module-hybrid-cdn` | `wp init -t module-hybrid-cdn` → `wp dev` → `wp deploy` |
13
+ | **Module (monolith)** | `module-monolith` | `wp init -t module-monolith` → `wp dev` → `wp build --hubspot-only` |
14
+ | **Theme** | `theme-starter` | `wp init -t theme-starter` → `wp dev` → `wp upload` |
15
+
16
+ WoodsPortal client-frontend is the **reference advanced module** (hybrid CDN + full app adapter).
17
+
18
+ ## Quick start
19
+
20
+ ```bash
21
+ npx wp init -t module-hybrid-cdn
22
+ cd my-project && npm install
23
+ wp doctor
24
+ wp dev # alias: wp local
25
+ ```
26
+
27
+ Existing repo:
28
+
29
+ ```bash
30
+ wp adopt
31
+ wp migrate --yes # if migrating from .dev/
32
+ ```
33
+
34
+ ## Commands
35
+
36
+ | Command | Description |
37
+ |---------|-------------|
38
+ | `wp dev` | Local Vite server + CMS dev tool |
39
+ | `wp build` | CDN + module (or theme no-op) |
40
+ | `wp deploy` | Build, publish CDN, upload module |
41
+ | `wp adopt` | Detect module/theme and add config + `.wphs/` |
42
+ | `wp migrate` | `.dev/` → `.wphs/` |
43
+ | `wp examples smoke` | Run examples matrix (CI) |
44
+ | `wp audit secrets` | Scan for hardcoded credentials |
45
+
46
+ See [docs/CLI-REFERENCE.md](./docs/CLI-REFERENCE.md), [docs/MODULES.md](./docs/MODULES.md), [docs/THEMES.md](./docs/THEMES.md), [docs/EXAMPLES.md](./docs/EXAMPLES.md).
47
+
48
+ ## Config
49
+
50
+ **Config:** `woodscli.json` at project root. Legacy `woodsportal.hs.json` → run `wp migrate --yes`.
51
+
52
+ Local workspace: **`.wphs/`** only. Consumer hook: **`wphs.adapter.ts`**.
53
+
54
+ HubSpot upload/watch: **`hubspot.config.yml`** — [setup guide](./docs/HUBSPOT-CLI-SETUP.md).
55
+
56
+ ## Prerequisites
57
+
58
+ Node 20+, `@hubspot/cli`, HubSpot account with CMS.
59
+
60
+ ## License
61
+
62
+ MIT — Digital Woods
package/SECURITY.md ADDED
@@ -0,0 +1,27 @@
1
+ # Security
2
+
3
+ ## Reporting
4
+
5
+ Report vulnerabilities to Digital Woods security contact (see repository maintainers).
6
+
7
+ ## Secrets
8
+
9
+ - Never commit HubSpot PATs, OAuth client secrets, or Stripe keys.
10
+ - Use env files (`.env.dev`, `.env.prod`) — gitignored.
11
+ - Run `wp audit secrets` before release.
12
+
13
+ ## Production upload
14
+
15
+ `wp upload --tier prod` requires `--yes` or `WP_HS_CONFIRM_PROD=1` to prevent accidental Design Manager overwrites.
16
+
17
+ ## HubSpot CLI
18
+
19
+ `wp auth` delegates to `hs auth`. Use least-privilege CMS scopes for upload accounts.
20
+
21
+ ## WoodsCLI workspace
22
+
23
+ `.wphs/config/module-config.local.json` and `.wphs/assets/` are gitignored — may contain customer branding URLs; do not log full contents in production.
24
+
25
+ ## Dependency hygiene
26
+
27
+ `npm run verify` on publish; address CVEs in direct dependencies promptly.
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/lib/exec.ts
4
+ import { 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
+
80
+ export {
81
+ ExitCode,
82
+ WpHsError,
83
+ isWpHsError,
84
+ runCommand,
85
+ runNodeScript
86
+ };
87
+ //# sourceMappingURL=chunk-JDGIAYHX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/exec.ts","../src/lib/errors.ts"],"sourcesContent":["import { 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","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":";;;AAAA,SAAS,iBAAwC;;;ACA1C,IAAM,WAAW;AAAA,EACtB,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,UAAU;AACZ;AAIO,IAAM,YAAN,cAAwB,MAAM;AAAA,EAC1B;AAAA,EACA;AAAA,EAET,YACE,SACA,UAA+E,CAAC,GAChF;AACA,UAAM,SAAS,EAAE,OAAO,QAAQ,MAAM,CAAC;AACvC,SAAK,OAAO;AACZ,SAAK,WAAW,QAAQ,YAAY,SAAS;AAC7C,SAAK,cAAc,QAAQ;AAAA,EAC7B;AACF;AAEO,SAAS,YAAY,OAAoC;AAC9D,SAAO,iBAAiB;AAC1B;;;ADxBA,IAAM,iBAAiB;AAEvB,SAAS,QAAQ,KAA2C;AAC1D,QAAM,SAA4B,EAAE,GAAG,IAAI;AAC3C,aAAW,OAAO,OAAO,KAAK,MAAM,GAAG;AACrC,QAAI,kCAAkC,KAAK,GAAG,KAAK,OAAO,GAAG,GAAG;AAC9D,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AACA,SAAO;AACT;AAYO,SAAS,WACd,SACA,MACA,SACoD;AACpD,QAAM,EAAE,KAAK,MAAM,QAAQ,KAAK,QAAQ,SAAS,OAAO,UAAU,CAAC,CAAC,EAAE,IAAI;AAE1E,MAAI,QAAQ;AACV,YAAQ,KAAK,aAAa,OAAO,IAAI,KAAK,KAAK,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC;AAC9D,WAAO,EAAE,QAAQ,GAAG,QAAQ,IAAI,QAAQ,GAAG;AAAA,EAC7C;AAEA,UAAQ,MAAM,SAAS,OAAO,IAAI,KAAK,KAAK,GAAG,CAAC,IAAI;AAAA,IAClD;AAAA,IACA,KAAK,SAAS,QAAQ,GAAG,IAAI;AAAA,EAC/B,CAAC;AAED,QAAM,YAA8B;AAAA,IAClC;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV,OAAO,CAAC,WAAW,QAAQ,MAAM;AAAA,EACnC;AAEA,QAAM,SAAS,UAAU,SAAS,MAAM,SAAS;AACjD,QAAM,SAAS,OAAO,UAAU;AAChC,QAAM,SAAS,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS;AACnE,QAAM,SAAS,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS;AAEnE,MAAI,CAAC,QAAQ,SAAS,MAAM,GAAG;AAC7B,UAAM,SAAS,OAAO,KAAK,KAAK,OAAO,KAAK,KAAK,QAAQ,MAAM;AAC/D,QAAI,eAAe,KAAK,MAAM,GAAG;AAC/B,YAAM,IAAI,UAAU,mBAAmB,OAAO,IAAI;AAAA,QAChD,UAAU,SAAS;AAAA,QACnB,aAAa;AAAA,MACf,CAAC;AAAA,IACH;AACA,UAAM,IAAI,UAAU,mBAAmB,OAAO,IAAI,KAAK,KAAK,GAAG,CAAC;AAAA,EAAK,MAAM,IAAI;AAAA,MAC7E,UAAU,SAAS;AAAA,IACrB,CAAC;AAAA,EACH;AAEA,SAAO,EAAE,QAAQ,QAAQ,OAAO;AAClC;AAEO,SAAS,cACd,YACA,MACA,SACM;AACN,aAAW,QAAQ,UAAU,CAAC,YAAY,GAAG,IAAI,GAAG,OAAO;AAC7D;","names":[]}
@@ -0,0 +1,136 @@
1
+ // src/lib/paths.ts
2
+ import { existsSync as existsSync2, readFileSync } from "fs";
3
+ import { dirname, resolve as resolve2 } from "path";
4
+ import { fileURLToPath } from "url";
5
+
6
+ // src/lib/config-files.ts
7
+ import { existsSync } from "fs";
8
+ import { resolve } from "path";
9
+ var WOODSCLI_CONFIG_FILE = "woodscli.json";
10
+ function envConfigPath(projectRoot) {
11
+ const fromEnv = process.env.WOODSCLI_CONFIG?.trim() || process.env.WP_HS_CONFIG?.trim() || null;
12
+ if (!fromEnv) return null;
13
+ return resolve(projectRoot, fromEnv);
14
+ }
15
+ function woodscliConfigPath(projectRoot) {
16
+ return resolve(projectRoot, WOODSCLI_CONFIG_FILE);
17
+ }
18
+ function resolveProjectConfig(projectRoot) {
19
+ const envPath = envConfigPath(projectRoot);
20
+ if (envPath) {
21
+ if (!existsSync(envPath)) return null;
22
+ return { path: envPath, fileName: WOODSCLI_CONFIG_FILE };
23
+ }
24
+ const path = woodscliConfigPath(projectRoot);
25
+ if (existsSync(path)) {
26
+ return { path, fileName: WOODSCLI_CONFIG_FILE };
27
+ }
28
+ return null;
29
+ }
30
+ function findProjectRoot(from = process.cwd()) {
31
+ if (process.env.WP_HS_PROJECT_ROOT?.trim()) {
32
+ return resolve(process.env.WP_HS_PROJECT_ROOT.trim());
33
+ }
34
+ let dir = resolve(from);
35
+ while (true) {
36
+ if (resolveProjectConfig(dir)) {
37
+ return dir;
38
+ }
39
+ const parent = resolve(dir, "..");
40
+ if (parent === dir) {
41
+ return null;
42
+ }
43
+ dir = parent;
44
+ }
45
+ }
46
+
47
+ // src/lib/errors.ts
48
+ var ExitCode = {
49
+ SUCCESS: 0,
50
+ USER_ERROR: 1,
51
+ PREREQUISITE: 2,
52
+ BUILD_FAILED: 3,
53
+ PUBLISH_FAILED: 4,
54
+ AUDIT_FAILED: 5,
55
+ INTERNAL: 10
56
+ };
57
+ var WpHsError = class extends Error {
58
+ exitCode;
59
+ remediation;
60
+ constructor(message, options = {}) {
61
+ super(message, { cause: options.cause });
62
+ this.name = "WpHsError";
63
+ this.exitCode = options.exitCode ?? ExitCode.USER_ERROR;
64
+ this.remediation = options.remediation;
65
+ }
66
+ };
67
+
68
+ // src/lib/paths.ts
69
+ var KIT_PACKAGE_NAME = "@woodsportal/hubspot-kit";
70
+ function resolveKitRoot() {
71
+ let dir = dirname(fileURLToPath(import.meta.url));
72
+ while (true) {
73
+ const pkgPath = resolve2(dir, "package.json");
74
+ if (existsSync2(pkgPath)) {
75
+ try {
76
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
77
+ if (pkg.name === KIT_PACKAGE_NAME) {
78
+ return dir;
79
+ }
80
+ } catch {
81
+ }
82
+ }
83
+ const parent = dirname(dir);
84
+ if (parent === dir) {
85
+ break;
86
+ }
87
+ dir = parent;
88
+ }
89
+ throw new WpHsError("Could not resolve @woodsportal/hubspot-kit package root.", {
90
+ exitCode: ExitCode.INTERNAL
91
+ });
92
+ }
93
+ var kitRoot = resolveKitRoot();
94
+ function getKitRoot() {
95
+ return kitRoot;
96
+ }
97
+ function findProjectRoot2(from = process.cwd()) {
98
+ const root = findProjectRoot(from);
99
+ if (!root) {
100
+ throw new WpHsError("Could not find woodscli.json in this directory or any parent.", {
101
+ exitCode: ExitCode.USER_ERROR,
102
+ remediation: "Run wp init, wp adopt, or pass --project <path>. Legacy config: wp migrate --yes."
103
+ });
104
+ }
105
+ return root;
106
+ }
107
+ function kitPath(...segments) {
108
+ return resolve2(kitRoot, ...segments);
109
+ }
110
+
111
+ // src/vite/paths.ts
112
+ function getViteConfigPath(preset) {
113
+ return kitPath("vite", `vite.config.${preset}.mjs`);
114
+ }
115
+
116
+ // src/vite/create-vite-config.ts
117
+ function createViteConfig(presetOrOptions) {
118
+ const options = typeof presetOrOptions === "string" ? { preset: presetOrOptions } : presetOrOptions;
119
+ if (options.projectRoot?.trim()) {
120
+ process.env.WP_HS_PROJECT_ROOT = options.projectRoot.trim();
121
+ }
122
+ return getViteConfigPath(options.preset);
123
+ }
124
+
125
+ export {
126
+ WOODSCLI_CONFIG_FILE,
127
+ resolveProjectConfig,
128
+ ExitCode,
129
+ WpHsError,
130
+ getKitRoot,
131
+ findProjectRoot2 as findProjectRoot,
132
+ kitPath,
133
+ getViteConfigPath,
134
+ createViteConfig
135
+ };
136
+ //# sourceMappingURL=chunk-U4KFGTEW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/paths.ts","../src/lib/config-files.ts","../src/lib/errors.ts","../src/vite/paths.ts","../src/vite/create-vite-config.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nimport { findProjectRoot as findConfigProjectRoot } from './config-files.js'\nimport { WpHsError, ExitCode } from './errors.js'\n\nconst KIT_PACKAGE_NAME = '@woodsportal/hubspot-kit'\n\nfunction resolveKitRoot(): string {\n let dir = dirname(fileURLToPath(import.meta.url))\n while (true) {\n const pkgPath = resolve(dir, 'package.json')\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as { name?: string }\n if (pkg.name === KIT_PACKAGE_NAME) {\n return dir\n }\n } catch {\n // keep walking\n }\n }\n const parent = dirname(dir)\n if (parent === dir) {\n break\n }\n dir = parent\n }\n\n throw new WpHsError('Could not resolve @woodsportal/hubspot-kit package root.', {\n exitCode: ExitCode.INTERNAL,\n })\n}\n\nconst kitRoot = resolveKitRoot()\n\nexport function getKitRoot(): string {\n return kitRoot\n}\n\nexport function findProjectRoot(from = process.cwd()): string {\n const root = findConfigProjectRoot(from)\n if (!root) {\n throw new WpHsError('Could not find woodscli.json in this directory or any parent.', {\n exitCode: ExitCode.USER_ERROR,\n remediation: 'Run wp init, wp adopt, or pass --project <path>. Legacy config: wp migrate --yes.',\n })\n }\n return root\n}\n\nexport function kitPath(...segments: string[]): string {\n return resolve(kitRoot, ...segments)\n}\n\nexport { resolveProjectConfig, WOODSCLI_CONFIG_FILE, LEGACY_WOODSPORTAL_HS_FILE } from './config-files.js'\n","import { existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\n/** Canonical WoodsCLI project config — the only supported filename. */\nexport const WOODSCLI_CONFIG_FILE = 'woodscli.json'\n\n/** Removed in v1.0.2+ — detected only for migrate/doctor messaging. */\nexport const LEGACY_WOODSPORTAL_HS_FILE = 'woodsportal.hs.json'\n\nexport type ResolvedProjectConfig = {\n path: string\n fileName: typeof WOODSCLI_CONFIG_FILE\n}\n\nfunction envConfigPath(projectRoot: string): string | null {\n const fromEnv = process.env.WOODSCLI_CONFIG?.trim() || process.env.WP_HS_CONFIG?.trim() || null\n if (!fromEnv) return null\n return resolve(projectRoot, fromEnv)\n}\n\nexport function legacyConfigPath(projectRoot: string): string {\n return resolve(projectRoot, LEGACY_WOODSPORTAL_HS_FILE)\n}\n\nexport function woodscliConfigPath(projectRoot: string): string {\n return resolve(projectRoot, WOODSCLI_CONFIG_FILE)\n}\n\n/** @returns null | 'legacy-only' | 'duplicate' */\nexport function legacyConfigConflict(projectRoot: string): 'legacy-only' | 'duplicate' | null {\n const hasWoodscli = existsSync(woodscliConfigPath(projectRoot))\n const hasLegacy = existsSync(legacyConfigPath(projectRoot))\n if (hasLegacy && hasWoodscli) return 'duplicate'\n if (hasLegacy && !hasWoodscli) return 'legacy-only'\n return null\n}\n\n/**\n * Resolve the active project config file (`woodscli.json` only).\n */\nexport function resolveProjectConfig(projectRoot: string): ResolvedProjectConfig | null {\n const envPath = envConfigPath(projectRoot)\n if (envPath) {\n if (!existsSync(envPath)) return null\n return { path: envPath, fileName: WOODSCLI_CONFIG_FILE }\n }\n\n const path = woodscliConfigPath(projectRoot)\n if (existsSync(path)) {\n return { path, fileName: WOODSCLI_CONFIG_FILE }\n }\n\n return null\n}\n\nexport function findProjectRoot(from = process.cwd()): string | null {\n if (process.env.WP_HS_PROJECT_ROOT?.trim()) {\n return resolve(process.env.WP_HS_PROJECT_ROOT.trim())\n }\n\n let dir = resolve(from)\n while (true) {\n if (resolveProjectConfig(dir)) {\n return dir\n }\n const parent = resolve(dir, '..')\n if (parent === dir) {\n return null\n }\n dir = parent\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","import { kitPath } from '../lib/paths.js'\n\nexport type VitePreset = 'dev' | 'hubspot' | 'cdn-vendor' | 'cdn-app' | 'cdn-esm'\n\nexport function getViteConfigPath(preset: VitePreset): string {\n return kitPath('vite', `vite.config.${preset}.mjs`)\n}\n","import { getViteConfigPath, type VitePreset } from './paths.js'\n\nexport type CreateViteConfigOptions = {\n /** Vite preset shipped with @woodsportal/hubspot-kit */\n preset: VitePreset\n /** Consumer project root (sets WP_HS_PROJECT_ROOT for kit scripts) */\n projectRoot?: string\n}\n\n/**\n * Returns the absolute path to a kit Vite config preset.\n * Consumer repos typically re-export: `export { default } from '@woodsportal/hubspot-kit/vite/vite.config.hubspot.mjs'`\n * or use this helper in a thin wrapper config.\n */\nexport function createViteConfig(\n presetOrOptions: VitePreset | CreateViteConfigOptions,\n): string {\n const options: CreateViteConfigOptions =\n typeof presetOrOptions === 'string' ? { preset: presetOrOptions } : presetOrOptions\n\n if (options.projectRoot?.trim()) {\n process.env.WP_HS_PROJECT_ROOT = options.projectRoot.trim()\n }\n\n return getViteConfigPath(options.preset)\n}\n"],"mappings":"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","names":["existsSync","resolve","resolve","existsSync","findProjectRoot"]}