@woodsportal/hubspot-kit 1.0.41-dev.5 → 1.0.41-dev.6
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 +7 -0
- package/README.md +191 -157
- package/dist/{chunk-S2YJDWZU.js → chunk-NWBK3XBZ.js} +1 -1
- package/dist/chunk-NWBK3XBZ.js.map +1 -0
- package/dist/chunk-WHYTQEZX.js +554 -0
- package/dist/chunk-WHYTQEZX.js.map +1 -0
- package/dist/cli.js +1494 -650
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/scaffold-page-lifecycle-EHMPWPPE.js +194 -0
- package/dist/scaffold-page-lifecycle-EHMPWPPE.js.map +1 -0
- package/dist/vite/index.js +1 -1
- package/examples/greenfield/module-hybrid-cdn/public/fields.json +73 -28
- package/examples/greenfield/module-hybrid-cdn/public/module.html +21 -5
- package/examples/greenfield/module-hybrid-cdn/src/assets/css/main.dev.css +15 -0
- package/examples/greenfield/module-hybrid-cdn/src/components/layout/starter-nav.tsx +12 -15
- package/examples/greenfield/module-hybrid-cdn/src/config/starter-nav.ts +9 -0
- package/examples/greenfield/module-hybrid-cdn/src/features/starter/components/starter-home.tsx +25 -29
- package/examples/greenfield/module-hybrid-cdn/src/features/starter/hooks/starter-preview-values.ts +60 -0
- package/examples/greenfield/module-hybrid-cdn/src/features/starter/hooks/use-starter-preview.dev.ts +18 -0
- package/examples/greenfield/module-hybrid-cdn/src/features/starter/hooks/use-starter-preview.ts +20 -37
- package/examples/greenfield/module-hybrid-cdn-auth-full/.env.dev.example +3 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/.env.local.example +6 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/.env.prod.example +5 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/.env.stg.example +3 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/.wphs/README.md +17 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/README.md +59 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/hubspot.config.yml.example +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/index.html +12 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/package.json +34 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/portal.cdn.json.example +8 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/public/fields.json +100 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/public/meta.json +9 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/public/module.html +38 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/app.tsx +172 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/assets/css/main.dev.css +23 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/assets/css/main.prod.css +12 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/assets/css/tailwind.css.config.css +7 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/cdn/app-entry.ts +48 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/cdn/vendor-entry.ts +28 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/Layouts/AppLayoutWrapper.tsx +38 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/Layouts/AuthLayout.tsx +7 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/Layouts/DefaultLayout.tsx +5 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/Layouts/MainLayout.tsx +42 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/Layouts/UnauthorizedLayout.tsx +5 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/Layouts/index.ts +13 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/layout/section-card.tsx +20 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/layout/starter-nav.tsx +28 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/layout/starter-shell.tsx +18 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/ui/Button.tsx +22 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/ui/Form.tsx +22 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/ui/Input.tsx +12 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/ui/Toaster.tsx +5 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/ui/auth/SsoLoginButton.tsx +34 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/ui/link.tsx +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/ui/loader/loader.tsx +18 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/config/auth-tier.ts +4 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/config/nav-scaffold.ts +4 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/config/portal-nav.ts +9 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/config/router.ts +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/config/routes.ts +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/data/hubSpotContext.ts +57 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/data/sync-sdk-hub-context.ts +44 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/defaultData.ts +2 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/env.ts +59 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/app-bootstrap-guards.ts +14 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/auth-navigation.ts +81 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/auth-route-paths.ts +29 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/auth-session-events.ts +2 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/auth-session.ts +51 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/clear-client-auth-session.ts +62 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/components/starter-login.tsx +83 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/login-email-storage.ts +64 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/login-session-bridge.ts +74 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/mfa-otp-utils.ts +24 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/mfa-pending-storage.ts +108 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/mfa-portal.ts +45 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/post-login-path.ts +17 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/resolve-auth-login-error-message.ts +52 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/resolve-post-login-path.ts +21 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/sso-callback.ts +120 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/use-mfa-login-otp-flow.ts +78 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/use-portal-session.ts +25 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/use-signed-in-email.ts +55 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/use-sso-callback-handler.ts +92 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/components/starter-guide-page.tsx +210 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/components/starter-home-page.tsx +21 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/components/starter-home.tsx +48 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/components/starter-page-frame.tsx +25 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/components/starter-protected-page.tsx +54 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/components/starter-single-page.tsx +17 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/content/auth-guide-content.ts +85 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/content/woodscli-guide-content.ts +86 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/hooks/starter-preview-values.ts +60 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/hooks/use-starter-preview.dev.ts +18 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/hooks/use-starter-preview.ts +30 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/index.ts +6 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/hubspot/bootstrap.ts +16 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/hubspot/default-data.ts +7 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/hubspot/is-cms-editor.ts +78 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/hubspot/module-entry.tsx +9 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/hubspot/types.ts +17 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/integrations/configure-woodsportal-sdk.ts +98 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/integrations/tanstack-query/root-provider.tsx +18 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/integrations/tanstack-router/router.tsx +39 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/integrations/woodsportal-sdk.ts +37 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/main.tsx +5 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/portal/api-endpoint.ts +11 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/portal/ensure-portal-cdn-stylesheet.ts +115 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/portal/mount-dev-preview.tsx +12 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/portal/mount-portal.tsx +57 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/routeTree.gen.ts +182 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/routes/__root.tsx +12 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/routes/_app/portal.tsx +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/routes/_auth/login/sso.tsx +27 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/routes/_auth/login.tsx +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/routes/_auth/two-fa.tsx +91 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/routes/guide.tsx +9 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/routes/index.tsx +8 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/routes/unauthorized.tsx +50 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/utils/ValidationSchema.ts +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/utils/cookie.ts +23 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/utils/normalize-router-path.ts +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/utils/private-route.tsx +100 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/utils/public-route.tsx +64 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/utils/resolve-api-base-url.ts +44 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/src/utils/safe-router-navigation.ts +82 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/tsconfig.json +20 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/vite.config.cdn-app.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/vite.config.cdn-esm.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/vite.config.cdn-vendor.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/vite.config.dev.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/vite.config.hubspot.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/vite.config.monolith.mjs +8 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/woodscli.json +38 -0
- package/examples/greenfield/module-hybrid-cdn-auth-full/wphs.adapter.ts +7 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/.env.dev.example +3 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/.env.local.example +6 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/.env.prod.example +5 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/.env.stg.example +3 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/.wphs/README.md +17 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/README.md +59 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/hubspot.config.yml.example +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/index.html +12 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/package.json +34 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/portal.cdn.json.example +8 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/public/fields.json +100 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/public/meta.json +9 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/public/module.html +38 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/app.tsx +180 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/assets/css/main.dev.css +23 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/assets/css/main.prod.css +12 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/assets/css/tailwind.css.config.css +7 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/cdn/app-entry.ts +48 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/cdn/vendor-entry.ts +28 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/Layouts/AppLayoutWrapper.tsx +38 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/Layouts/AuthLayout.tsx +7 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/Layouts/DefaultLayout.tsx +5 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/Layouts/MainLayout.tsx +42 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/Layouts/UnauthorizedLayout.tsx +5 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/Layouts/index.ts +13 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/layout/section-card.tsx +20 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/layout/starter-nav.tsx +28 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/layout/starter-shell.tsx +18 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/ui/Button.tsx +22 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/ui/Form.tsx +22 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/ui/Input.tsx +12 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/ui/Toaster.tsx +5 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/ui/auth/SsoLoginButton.tsx +34 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/ui/link.tsx +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/ui/loader/loader.tsx +18 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/config/auth-tier.ts +4 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/config/nav-scaffold.ts +4 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/config/portal-nav.ts +9 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/config/router.ts +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/config/routes.ts +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/data/hubSpotContext.ts +57 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/data/sync-sdk-hub-context.ts +44 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/defaultData.ts +2 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/env.ts +59 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/app-bootstrap-guards.ts +14 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/auth-navigation.ts +81 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/auth-route-paths.ts +29 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/auth-session-events.ts +2 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/auth-session.ts +26 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/clear-client-auth-session.ts +62 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/components/starter-login.tsx +83 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/login-email-storage.ts +64 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/login-session-bridge.ts +72 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/post-login-path.ts +17 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/resolve-auth-login-error-message.ts +52 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/resolve-post-login-path.ts +21 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/sso-callback.ts +120 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/use-portal-session.ts +25 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/use-signed-in-email.ts +55 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/use-sso-callback-handler.ts +92 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/components/starter-guide-page.tsx +210 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/components/starter-home-page.tsx +21 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/components/starter-home.tsx +48 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/components/starter-page-frame.tsx +25 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/components/starter-protected-page.tsx +54 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/components/starter-single-page.tsx +17 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/content/auth-guide-content.ts +85 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/content/woodscli-guide-content.ts +86 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/hooks/starter-preview-values.ts +60 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/hooks/use-starter-preview.dev.ts +18 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/hooks/use-starter-preview.ts +30 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/index.ts +6 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/hubspot/bootstrap.ts +16 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/hubspot/default-data.ts +7 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/hubspot/is-cms-editor.ts +78 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/hubspot/module-entry.tsx +9 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/hubspot/types.ts +17 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/integrations/configure-woodsportal-sdk.ts +94 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/integrations/tanstack-query/root-provider.tsx +18 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/integrations/tanstack-router/router.tsx +39 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/integrations/woodsportal-sdk.ts +37 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/main.tsx +5 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/portal/api-endpoint.ts +11 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/portal/ensure-portal-cdn-stylesheet.ts +115 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/portal/mount-dev-preview.tsx +12 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/portal/mount-portal.tsx +57 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/routeTree.gen.ts +155 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/routes/__root.tsx +12 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/routes/_app/portal.tsx +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/routes/_auth/login/sso.tsx +27 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/routes/_auth/login.tsx +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/routes/guide.tsx +9 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/routes/index.tsx +8 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/routes/unauthorized.tsx +50 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/utils/ValidationSchema.ts +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/utils/cookie.ts +23 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/utils/normalize-router-path.ts +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/utils/private-route.tsx +100 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/utils/public-route.tsx +64 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/utils/resolve-api-base-url.ts +44 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/src/utils/safe-router-navigation.ts +82 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/tsconfig.json +20 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/vite.config.cdn-app.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/vite.config.cdn-esm.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/vite.config.cdn-vendor.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/vite.config.dev.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/vite.config.hubspot.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/vite.config.monolith.mjs +8 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/woodscli.json +38 -0
- package/examples/greenfield/module-hybrid-cdn-auth-sso/wphs.adapter.ts +7 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/.env.dev.example +3 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/.env.local.example +6 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/.env.prod.example +5 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/.env.stg.example +3 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/.wphs/README.md +17 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/README.md +59 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/hubspot.config.yml.example +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/index.html +12 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/package.json +34 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/portal.cdn.json.example +8 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/public/fields.json +100 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/public/meta.json +9 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/public/module.html +38 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/app.tsx +173 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/assets/css/main.dev.css +23 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/assets/css/main.prod.css +12 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/assets/css/tailwind.css.config.css +7 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/cdn/app-entry.ts +48 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/cdn/vendor-entry.ts +28 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/Layouts/AppLayoutWrapper.tsx +38 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/Layouts/AuthLayout.tsx +7 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/Layouts/DefaultLayout.tsx +5 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/Layouts/MainLayout.tsx +42 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/Layouts/UnauthorizedLayout.tsx +5 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/Layouts/index.ts +13 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/layout/section-card.tsx +20 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/layout/starter-nav.tsx +28 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/layout/starter-shell.tsx +18 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/ui/Button.tsx +22 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/ui/Form.tsx +22 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/ui/Input.tsx +12 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/ui/Toaster.tsx +5 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/ui/link.tsx +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/ui/loader/loader.tsx +18 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/config/auth-tier.ts +4 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/config/nav-scaffold.ts +4 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/config/portal-nav.ts +9 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/config/router.ts +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/config/routes.ts +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/data/hubSpotContext.ts +57 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/data/sync-sdk-hub-context.ts +44 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/defaultData.ts +2 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/env.ts +59 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/app-bootstrap-guards.ts +14 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/auth-navigation.ts +81 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/auth-route-paths.ts +29 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/auth-session-events.ts +2 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/auth-session.ts +26 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/clear-client-auth-session.ts +62 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/components/starter-login.tsx +76 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/login-email-storage.ts +64 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/login-session-bridge.ts +72 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/post-login-path.ts +17 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/resolve-auth-login-error-message.ts +52 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/resolve-post-login-path.ts +21 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/use-portal-session.ts +25 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/use-signed-in-email.ts +55 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/components/starter-guide-page.tsx +218 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/components/starter-home-page.tsx +21 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/components/starter-home.tsx +48 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/components/starter-page-frame.tsx +25 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/components/starter-protected-page.tsx +54 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/components/starter-single-page.tsx +17 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/content/auth-guide-content.ts +91 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/content/woodscli-guide-content.ts +86 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/hooks/starter-preview-values.ts +60 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/hooks/use-starter-preview.dev.ts +18 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/hooks/use-starter-preview.ts +30 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/index.ts +6 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/hubspot/bootstrap.ts +16 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/hubspot/default-data.ts +7 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/hubspot/is-cms-editor.ts +78 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/hubspot/module-entry.tsx +9 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/hubspot/types.ts +17 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/integrations/configure-woodsportal-sdk.ts +94 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/integrations/tanstack-query/root-provider.tsx +18 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/integrations/tanstack-router/router.tsx +39 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/integrations/woodsportal-sdk.ts +37 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/main.tsx +5 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/portal/api-endpoint.ts +11 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/portal/ensure-portal-cdn-stylesheet.ts +115 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/portal/mount-dev-preview.tsx +12 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/portal/mount-portal.tsx +52 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/routeTree.gen.ts +134 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/routes/__root.tsx +12 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/routes/_app/portal.tsx +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/routes/_auth/login.tsx +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/routes/guide.tsx +9 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/routes/index.tsx +8 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/routes/unauthorized.tsx +50 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/utils/ValidationSchema.ts +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/utils/cookie.ts +23 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/utils/normalize-router-path.ts +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/utils/private-route.tsx +100 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/utils/public-route.tsx +64 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/utils/resolve-api-base-url.ts +44 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/src/utils/safe-router-navigation.ts +82 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/tsconfig.json +20 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/vite.config.cdn-app.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/vite.config.cdn-esm.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/vite.config.cdn-vendor.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/vite.config.dev.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/vite.config.hubspot.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/vite.config.monolith.mjs +8 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/woodscli.json +38 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter/wphs.adapter.ts +7 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/.env.dev.example +3 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/.env.local.example +6 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/.env.prod.example +5 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/.env.stg.example +3 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/.wphs/README.md +17 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/README.md +59 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/hubspot.config.yml.example +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/index.html +12 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/package.json +34 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/portal.cdn.json.example +8 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/public/fields.json +100 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/public/meta.json +9 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/public/module.html +38 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/app.tsx +173 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/assets/css/main.dev.css +23 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/assets/css/main.prod.css +12 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/assets/css/tailwind.css.config.css +7 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/cdn/app-entry.ts +48 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/cdn/vendor-entry.ts +28 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/Layouts/AppLayoutWrapper.tsx +38 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/Layouts/AuthLayout.tsx +7 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/Layouts/DefaultLayout.tsx +5 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/Layouts/MainLayout.tsx +7 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/Layouts/UnauthorizedLayout.tsx +5 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/Layouts/index.ts +13 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/layout/portal-sidebar-nav.tsx +33 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/layout/portal-sidebar.tsx +139 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/layout/section-card.tsx +20 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/layout/starter-nav.tsx +28 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/layout/starter-shell.tsx +18 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/ui/Button.tsx +22 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/ui/Form.tsx +22 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/ui/Input.tsx +12 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/ui/Toaster.tsx +5 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/ui/link.tsx +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/ui/loader/loader.tsx +18 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/config/auth-tier.ts +4 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/config/nav-scaffold.ts +4 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/config/portal-nav.ts +9 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/config/router.ts +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/config/routes.ts +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/data/hubSpotContext.ts +57 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/data/sync-sdk-hub-context.ts +44 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/defaultData.ts +2 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/env.ts +59 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/app-bootstrap-guards.ts +14 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/auth-navigation.ts +81 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/auth-route-paths.ts +29 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/auth-session-events.ts +2 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/auth-session.ts +26 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/clear-client-auth-session.ts +62 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/components/starter-login.tsx +76 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/login-email-storage.ts +64 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/login-session-bridge.ts +72 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/post-login-path.ts +17 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/resolve-auth-login-error-message.ts +52 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/resolve-post-login-path.ts +21 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/use-portal-session.ts +25 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/use-signed-in-email.ts +55 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/components/starter-guide-page.tsx +210 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/components/starter-home-page.tsx +21 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/components/starter-home.tsx +48 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/components/starter-page-frame.tsx +25 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/components/starter-protected-page.tsx +54 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/components/starter-single-page.tsx +17 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/content/auth-guide-content.ts +85 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/content/woodscli-guide-content.ts +86 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/hooks/starter-preview-values.ts +60 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/hooks/use-starter-preview.dev.ts +18 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/hooks/use-starter-preview.ts +30 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/index.ts +6 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/hubspot/bootstrap.ts +16 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/hubspot/default-data.ts +7 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/hubspot/is-cms-editor.ts +78 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/hubspot/module-entry.tsx +9 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/hubspot/types.ts +17 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/integrations/configure-woodsportal-sdk.ts +94 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/integrations/tanstack-query/root-provider.tsx +18 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/integrations/tanstack-router/router.tsx +39 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/integrations/woodsportal-sdk.ts +37 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/main.tsx +5 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/portal/api-endpoint.ts +11 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/portal/ensure-portal-cdn-stylesheet.ts +115 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/portal/mount-dev-preview.tsx +12 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/portal/mount-portal.tsx +52 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/routeTree.gen.ts +134 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/routes/__root.tsx +12 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/routes/_app/portal.tsx +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/routes/_auth/login.tsx +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/routes/guide.tsx +9 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/routes/index.tsx +8 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/routes/unauthorized.tsx +50 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/utils/ValidationSchema.ts +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/utils/cookie.ts +23 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/utils/normalize-router-path.ts +10 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/utils/private-route.tsx +100 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/utils/public-route.tsx +64 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/utils/resolve-api-base-url.ts +44 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/utils/safe-router-navigation.ts +82 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/tsconfig.json +20 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/vite.config.cdn-app.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/vite.config.cdn-esm.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/vite.config.cdn-vendor.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/vite.config.dev.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/vite.config.hubspot.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/vite.config.monolith.mjs +8 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/woodscli.json +38 -0
- package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/wphs.adapter.ts +7 -0
- package/examples/greenfield/module-monolith/public/fields.json +66 -16
- package/examples/greenfield/module-monolith/src/assets/css/main.dev.css +7 -0
- package/examples/greenfield/module-monolith/src/components/layout/starter-nav.tsx +12 -15
- package/examples/greenfield/module-monolith/src/config/starter-nav.ts +9 -0
- package/examples/greenfield/module-monolith/src/features/starter/components/starter-home.tsx +25 -29
- package/examples/greenfield/module-monolith/src/features/starter/hooks/starter-preview-values.ts +60 -0
- package/examples/greenfield/module-monolith/src/features/starter/hooks/use-starter-preview.dev.ts +18 -0
- package/examples/greenfield/module-monolith/src/features/starter/hooks/use-starter-preview.ts +20 -37
- package/examples/manifest.json +5 -1
- package/examples/smoke/run-matrix.mjs +37 -0
- package/package.json +7 -5
- package/scripts/cli-smoke-helpers.mjs +150 -0
- package/scripts/regenerate-route-tree.mjs +71 -0
- package/scripts/sync-auth-scaffold-from-client.mjs +41 -0
- package/scripts/wp-cli-smoke.test.mjs +263 -0
- package/src/dev/bootstrap-greenfield.tsx +6 -0
- package/templates/module-hybrid-cdn/public/fields.json +47 -7
- package/templates/module-hybrid-cdn/public/module.html +7 -6
- package/templates/module-hybrid-cdn/src/assets/css/main.dev.css +7 -0
- package/templates/module-hybrid-cdn/src/components/layout/starter-nav.tsx +12 -15
- package/templates/module-hybrid-cdn/src/config/starter-nav.ts +9 -0
- package/templates/module-hybrid-cdn/src/features/starter/components/starter-home.tsx +25 -27
- package/templates/module-hybrid-cdn/src/features/starter/hooks/starter-preview-values.ts +5 -5
- package/templates/module-monolith/public/fields.json +49 -6
- package/templates/module-monolith/src/assets/css/main.dev.css +7 -0
- package/templates/module-monolith/src/components/layout/starter-nav.tsx +12 -15
- package/templates/module-monolith/src/config/starter-nav.ts +9 -0
- package/templates/module-monolith/src/features/starter/components/starter-home.tsx +25 -27
- package/templates/module-monolith/src/features/starter/hooks/starter-preview-values.ts +5 -5
- package/templates/scaffold/auth/_full/src/app.tsx +172 -0
- package/templates/scaffold/auth/_full/src/config/auth-tier.ts +4 -0
- package/templates/scaffold/auth/_full/src/features/auth/auth-session.ts +51 -0
- package/templates/scaffold/auth/_full/src/features/auth/login-session-bridge.ts +74 -0
- package/templates/scaffold/auth/_full/src/features/auth/mfa-otp-utils.ts +24 -0
- package/templates/scaffold/auth/_full/src/features/auth/mfa-pending-storage.ts +108 -0
- package/templates/scaffold/auth/_full/src/features/auth/mfa-portal.ts +45 -0
- package/templates/scaffold/auth/_full/src/features/auth/use-mfa-login-otp-flow.ts +78 -0
- package/templates/scaffold/auth/_full/src/routeTree.gen.ts +182 -0
- package/templates/scaffold/auth/_full/src/routes/_auth/two-fa.tsx +91 -0
- package/templates/scaffold/auth/_nav-sidebar/src/components/Layouts/MainLayout.tsx +7 -0
- package/templates/scaffold/auth/_nav-sidebar/src/components/layout/portal-sidebar-nav.tsx +33 -0
- package/templates/scaffold/auth/_nav-sidebar/src/components/layout/portal-sidebar.tsx +139 -0
- package/templates/scaffold/auth/_nav-top/src/components/Layouts/MainLayout.tsx +42 -0
- package/templates/scaffold/auth/_shared/src/app.tsx +178 -0
- package/templates/scaffold/auth/_shared/src/components/Layouts/AppLayoutWrapper.tsx +38 -0
- package/templates/scaffold/auth/_shared/src/components/Layouts/AuthLayout.tsx +7 -0
- package/templates/scaffold/auth/_shared/src/components/Layouts/DefaultLayout.tsx +5 -0
- package/templates/scaffold/auth/_shared/src/components/Layouts/UnauthorizedLayout.tsx +5 -0
- package/templates/scaffold/auth/_shared/src/components/Layouts/index.ts +13 -0
- package/templates/scaffold/auth/_shared/src/components/ui/Button.tsx +22 -0
- package/templates/scaffold/auth/_shared/src/components/ui/Form.tsx +22 -0
- package/templates/scaffold/auth/_shared/src/components/ui/Input.tsx +12 -0
- package/templates/scaffold/auth/_shared/src/components/ui/Toaster.tsx +5 -0
- package/templates/scaffold/auth/_shared/src/components/ui/link.tsx +10 -0
- package/templates/scaffold/auth/_shared/src/components/ui/loader/loader.tsx +18 -0
- package/templates/scaffold/auth/_shared/src/config/auth-tier.ts +4 -0
- package/templates/scaffold/auth/_shared/src/config/nav-scaffold.ts +4 -0
- package/templates/scaffold/auth/_shared/src/config/portal-nav.ts +9 -0
- package/templates/scaffold/auth/_shared/src/config/router.ts +1 -0
- package/templates/scaffold/auth/_shared/src/config/routes.ts +10 -0
- package/templates/scaffold/auth/_shared/src/data/hubSpotContext.ts +57 -0
- package/templates/scaffold/auth/_shared/src/data/sync-sdk-hub-context.ts +44 -0
- package/templates/scaffold/auth/_shared/src/env.ts +59 -0
- package/templates/scaffold/auth/_shared/src/features/auth/app-bootstrap-guards.ts +14 -0
- package/templates/scaffold/auth/_shared/src/features/auth/auth-navigation.ts +81 -0
- package/templates/scaffold/auth/_shared/src/features/auth/auth-route-paths.ts +29 -0
- package/templates/scaffold/auth/_shared/src/features/auth/auth-session-events.ts +2 -0
- package/templates/scaffold/auth/_shared/src/features/auth/auth-session.ts +26 -0
- package/templates/scaffold/auth/_shared/src/features/auth/clear-client-auth-session.ts +62 -0
- package/templates/scaffold/auth/_shared/src/features/auth/components/starter-login.tsx +76 -0
- package/templates/scaffold/auth/_shared/src/features/auth/login-email-storage.ts +64 -0
- package/templates/scaffold/auth/_shared/src/features/auth/login-session-bridge.ts +72 -0
- package/templates/scaffold/auth/_shared/src/features/auth/post-login-path.ts +17 -0
- package/templates/scaffold/auth/_shared/src/features/auth/resolve-auth-login-error-message.ts +52 -0
- package/templates/scaffold/auth/_shared/src/features/auth/resolve-post-login-path.ts +21 -0
- package/templates/scaffold/auth/_shared/src/features/auth/use-portal-session.ts +25 -0
- package/templates/scaffold/auth/_shared/src/features/auth/use-signed-in-email.ts +55 -0
- package/templates/scaffold/auth/_shared/src/features/starter/components/starter-guide-page.tsx +218 -0
- package/templates/scaffold/auth/_shared/src/features/starter/components/starter-page-frame.tsx +25 -0
- package/templates/scaffold/auth/_shared/src/features/starter/components/starter-protected-page.tsx +54 -0
- package/templates/scaffold/auth/_shared/src/features/starter/content/auth-guide-content.ts +91 -0
- package/templates/scaffold/auth/_shared/src/hubspot/is-cms-editor.ts +78 -0
- package/templates/scaffold/auth/_shared/src/integrations/configure-woodsportal-sdk.ts +100 -0
- package/templates/scaffold/auth/_shared/src/integrations/tanstack-query/root-provider.tsx +18 -0
- package/templates/scaffold/auth/_shared/src/integrations/tanstack-router/router.tsx +39 -0
- package/templates/scaffold/auth/_shared/src/integrations/woodsportal-sdk.ts +37 -0
- package/templates/scaffold/auth/_shared/src/portal/mount-portal.tsx +59 -0
- package/templates/scaffold/auth/_shared/src/routeTree.gen.ts +134 -0
- package/templates/scaffold/auth/_shared/src/routes/__root.tsx +12 -0
- package/templates/scaffold/auth/_shared/src/routes/_app/portal.tsx +10 -0
- package/templates/scaffold/auth/_shared/src/routes/_auth/login.tsx +10 -0
- package/templates/scaffold/auth/_shared/src/routes/guide.tsx +9 -0
- package/templates/scaffold/auth/_shared/src/routes/index.tsx +8 -0
- package/templates/scaffold/auth/_shared/src/routes/unauthorized.tsx +50 -0
- package/templates/scaffold/auth/_shared/src/utils/ValidationSchema.ts +1 -0
- package/templates/scaffold/auth/_shared/src/utils/cookie.ts +23 -0
- package/templates/scaffold/auth/_shared/src/utils/normalize-router-path.ts +10 -0
- package/templates/scaffold/auth/_shared/src/utils/private-route.tsx +100 -0
- package/templates/scaffold/auth/_shared/src/utils/public-route.tsx +64 -0
- package/templates/scaffold/auth/_shared/src/utils/resolve-api-base-url.ts +44 -0
- package/templates/scaffold/auth/_shared/src/utils/safe-router-navigation.ts +82 -0
- package/templates/scaffold/auth/_sso/src/app.tsx +180 -0
- package/templates/scaffold/auth/_sso/src/components/ui/auth/SsoLoginButton.tsx +34 -0
- package/templates/scaffold/auth/_sso/src/config/auth-tier.ts +4 -0
- package/templates/scaffold/auth/_sso/src/features/auth/components/starter-login.tsx +83 -0
- package/templates/scaffold/auth/_sso/src/features/auth/sso-callback.ts +120 -0
- package/templates/scaffold/auth/_sso/src/features/auth/use-sso-callback-handler.ts +92 -0
- package/templates/scaffold/auth/_sso/src/routeTree.gen.ts +155 -0
- package/templates/scaffold/auth/_sso/src/routes/_auth/login/sso.tsx +27 -0
- package/templates/scaffold/auth/nav-manifest.json +8 -0
- package/templates/scaffold/auth/sync-manifest.json +16 -0
- package/templates/scaffold/auth/tier-manifest.json +18 -0
- package/templates/scaffold/page/page-component-auth.tsx.tpl +10 -0
- package/templates/scaffold/page/page-component-plain.tsx.tpl +11 -0
- package/templates/scaffold/page/route-protected-auth.tsx.tpl +10 -0
- package/templates/scaffold/page/route-public-auth.tsx.tpl +9 -0
- package/templates/scaffold/page/route-public-plain.tsx.tpl +6 -0
- package/dist/chunk-S2YJDWZU.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_WOODSPORTAL_HS,
|
|
4
|
+
KIT_PACKAGE_NAME,
|
|
5
|
+
LEGACY_WOODSPORTAL_HS_FILE,
|
|
6
|
+
WOODSCLI_CONFIG_FILE,
|
|
7
|
+
WPHS_WORKSPACE_DIR,
|
|
8
|
+
WoodsPortalHsSchema,
|
|
9
|
+
findProjectRoot,
|
|
10
|
+
getKitRoot,
|
|
11
|
+
isModuleProject,
|
|
12
|
+
isThemeProject,
|
|
13
|
+
kitPath,
|
|
14
|
+
legacyConfigConflict,
|
|
15
|
+
navConfigFileExists,
|
|
16
|
+
normalizePagePath,
|
|
17
|
+
normalizePageSlug,
|
|
18
|
+
resolveProjectConfig,
|
|
19
|
+
scaffoldPage,
|
|
20
|
+
titleFromSlug,
|
|
21
|
+
usesCdnPipeline
|
|
22
|
+
} from "./chunk-WHYTQEZX.js";
|
|
2
23
|
import {
|
|
3
24
|
ExitCode,
|
|
4
25
|
WpHsError,
|
|
@@ -11,198 +32,12 @@ import {
|
|
|
11
32
|
} from "./chunk-4GOJPOW3.js";
|
|
12
33
|
|
|
13
34
|
// src/cli/program.ts
|
|
14
|
-
import { readFileSync as readFileSync16 } from "fs";
|
|
15
|
-
import { resolve as resolve23 } from "path";
|
|
16
35
|
import { Command } from "commander";
|
|
17
36
|
|
|
18
37
|
// src/lib/config.ts
|
|
19
|
-
import { readFileSync
|
|
20
|
-
|
|
21
|
-
// src/lib/config-files.ts
|
|
22
|
-
import { existsSync } from "fs";
|
|
23
|
-
import { resolve } from "path";
|
|
24
|
-
var WOODSCLI_CONFIG_FILE = "woodscli.json";
|
|
25
|
-
var LEGACY_WOODSPORTAL_HS_FILE = "woodsportal.hs.json";
|
|
26
|
-
function envConfigPath(projectRoot) {
|
|
27
|
-
const fromEnv = process.env.WOODSCLI_CONFIG?.trim() || process.env.WP_HS_CONFIG?.trim() || null;
|
|
28
|
-
if (!fromEnv) return null;
|
|
29
|
-
return resolve(projectRoot, fromEnv);
|
|
30
|
-
}
|
|
31
|
-
function legacyConfigPath(projectRoot) {
|
|
32
|
-
return resolve(projectRoot, LEGACY_WOODSPORTAL_HS_FILE);
|
|
33
|
-
}
|
|
34
|
-
function woodscliConfigPath(projectRoot) {
|
|
35
|
-
return resolve(projectRoot, WOODSCLI_CONFIG_FILE);
|
|
36
|
-
}
|
|
37
|
-
function legacyConfigConflict(projectRoot) {
|
|
38
|
-
const hasWoodscli = existsSync(woodscliConfigPath(projectRoot));
|
|
39
|
-
const hasLegacy = existsSync(legacyConfigPath(projectRoot));
|
|
40
|
-
if (hasLegacy && hasWoodscli) return "duplicate";
|
|
41
|
-
if (hasLegacy && !hasWoodscli) return "legacy-only";
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
function resolveProjectConfig(projectRoot) {
|
|
45
|
-
const envPath = envConfigPath(projectRoot);
|
|
46
|
-
if (envPath) {
|
|
47
|
-
if (!existsSync(envPath)) return null;
|
|
48
|
-
return { path: envPath, fileName: WOODSCLI_CONFIG_FILE };
|
|
49
|
-
}
|
|
50
|
-
const path = woodscliConfigPath(projectRoot);
|
|
51
|
-
if (existsSync(path)) {
|
|
52
|
-
return { path, fileName: WOODSCLI_CONFIG_FILE };
|
|
53
|
-
}
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
function findProjectRoot(from = process.cwd()) {
|
|
57
|
-
if (process.env.WP_HS_PROJECT_ROOT?.trim()) {
|
|
58
|
-
return resolve(process.env.WP_HS_PROJECT_ROOT.trim());
|
|
59
|
-
}
|
|
60
|
-
let dir = resolve(from);
|
|
61
|
-
while (true) {
|
|
62
|
-
if (resolveProjectConfig(dir)) {
|
|
63
|
-
return dir;
|
|
64
|
-
}
|
|
65
|
-
const parent = resolve(dir, "..");
|
|
66
|
-
if (parent === dir) {
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
dir = parent;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// src/schema/woodsportal-hs.ts
|
|
74
|
-
import { z } from "zod";
|
|
75
|
-
var CONFIG_VERSION = 1;
|
|
76
|
-
var WPHS_WORKSPACE_DIR = ".wphs";
|
|
77
|
-
var WoodsPortalHsSchema = z.object({
|
|
78
|
-
configVersion: z.number().int().default(CONFIG_VERSION),
|
|
79
|
-
projectType: z.enum(["module-hybrid-cdn", "module-monolith", "theme-starter"]),
|
|
80
|
-
module: z.object({
|
|
81
|
-
name: z.string().min(1),
|
|
82
|
-
uploadPath: z.string().min(1),
|
|
83
|
-
outDir: z.string().default("dist/WoodsPortal.Module")
|
|
84
|
-
}),
|
|
85
|
-
theme: z.object({
|
|
86
|
-
name: z.string().min(1),
|
|
87
|
-
uploadPath: z.string().min(1),
|
|
88
|
-
srcDir: z.string().default("src/theme"),
|
|
89
|
-
themeJson: z.string().default("src/theme/theme.json")
|
|
90
|
-
}).optional(),
|
|
91
|
-
cdn: z.object({
|
|
92
|
-
mode: z.enum(["jsdelivr", "custom", "local", "none"]).default("jsdelivr"),
|
|
93
|
-
buildIdFile: z.string().default(".module-build-id"),
|
|
94
|
-
mirror: z.string().default("portal.cdn.json")
|
|
95
|
-
}).default({}),
|
|
96
|
-
entries: z.object({
|
|
97
|
-
app: z.string().default("src/cdn/app-entry.ts"),
|
|
98
|
-
vendor: z.string().default("src/cdn/vendor-entry.ts"),
|
|
99
|
-
hubspotBootstrap: z.string().default("src/hubspot/bootstrap.ts"),
|
|
100
|
-
/** Monolith production IIFE entry (wp build --hubspot-only) */
|
|
101
|
-
module: z.string().default("src/hubspot/module-entry.tsx"),
|
|
102
|
-
localDev: z.string().default("src/main.tsx")
|
|
103
|
-
}).default({}),
|
|
104
|
-
env: z.object({
|
|
105
|
-
tiers: z.array(z.enum(["dev", "stg", "prod"])).default(["dev", "stg", "prod"])
|
|
106
|
-
}).default({}),
|
|
107
|
-
dev: z.object({
|
|
108
|
-
moduleConfigDir: z.literal(WPHS_WORKSPACE_DIR).default(WPHS_WORKSPACE_DIR),
|
|
109
|
-
adapter: z.string().default("./wphs.adapter.ts"),
|
|
110
|
-
fieldsJson: z.string().default("public/fields.json"),
|
|
111
|
-
themeFieldsJson: z.string().optional()
|
|
112
|
-
}).default({}),
|
|
113
|
-
scaffold: z.object({
|
|
114
|
-
/** hash = woodsportal-client-frontend (HubSpot CMS); browser = local clean URLs; none = single page */
|
|
115
|
-
router: z.enum(["hash", "browser", "none"]).default("hash")
|
|
116
|
-
}).optional()
|
|
117
|
-
});
|
|
118
|
-
var DEFAULT_WOODSPORTAL_HS = {
|
|
119
|
-
configVersion: CONFIG_VERSION,
|
|
120
|
-
projectType: "module-hybrid-cdn",
|
|
121
|
-
module: {
|
|
122
|
-
name: "woodsportal-module",
|
|
123
|
-
uploadPath: "dw-modules/woodsportal-module.module",
|
|
124
|
-
outDir: "dist/WoodsPortal.Module"
|
|
125
|
-
},
|
|
126
|
-
cdn: {
|
|
127
|
-
mode: "jsdelivr",
|
|
128
|
-
buildIdFile: ".module-build-id",
|
|
129
|
-
mirror: "portal.cdn.json"
|
|
130
|
-
},
|
|
131
|
-
entries: {
|
|
132
|
-
app: "src/cdn/app-entry.ts",
|
|
133
|
-
vendor: "src/cdn/vendor-entry.ts",
|
|
134
|
-
hubspotBootstrap: "src/hubspot/bootstrap.ts",
|
|
135
|
-
module: "src/hubspot/module-entry.tsx",
|
|
136
|
-
localDev: "src/main.tsx"
|
|
137
|
-
},
|
|
138
|
-
env: {
|
|
139
|
-
tiers: ["dev", "stg", "prod"]
|
|
140
|
-
},
|
|
141
|
-
dev: {
|
|
142
|
-
moduleConfigDir: WPHS_WORKSPACE_DIR,
|
|
143
|
-
adapter: "./wphs.adapter.ts",
|
|
144
|
-
fieldsJson: "public/fields.json"
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
function isModuleProject(config) {
|
|
148
|
-
return config.projectType === "module-hybrid-cdn" || config.projectType === "module-monolith";
|
|
149
|
-
}
|
|
150
|
-
function isThemeProject(config) {
|
|
151
|
-
return config.projectType === "theme-starter";
|
|
152
|
-
}
|
|
153
|
-
function usesCdnPipeline(config) {
|
|
154
|
-
return config.projectType === "module-hybrid-cdn" && config.cdn.mode !== "none";
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// src/lib/paths.ts
|
|
158
|
-
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
159
|
-
import { dirname, resolve as resolve2 } from "path";
|
|
160
|
-
import { fileURLToPath } from "url";
|
|
161
|
-
var KIT_PACKAGE_NAME = "@woodsportal/hubspot-kit";
|
|
162
|
-
function resolveKitRoot() {
|
|
163
|
-
let dir = dirname(fileURLToPath(import.meta.url));
|
|
164
|
-
while (true) {
|
|
165
|
-
const pkgPath = resolve2(dir, "package.json");
|
|
166
|
-
if (existsSync2(pkgPath)) {
|
|
167
|
-
try {
|
|
168
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
169
|
-
if (pkg.name === KIT_PACKAGE_NAME) {
|
|
170
|
-
return dir;
|
|
171
|
-
}
|
|
172
|
-
} catch {
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
const parent = dirname(dir);
|
|
176
|
-
if (parent === dir) {
|
|
177
|
-
break;
|
|
178
|
-
}
|
|
179
|
-
dir = parent;
|
|
180
|
-
}
|
|
181
|
-
throw new WpHsError("Could not resolve @woodsportal/hubspot-kit package root.", {
|
|
182
|
-
exitCode: ExitCode.INTERNAL
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
var kitRoot = resolveKitRoot();
|
|
186
|
-
function getKitRoot() {
|
|
187
|
-
return kitRoot;
|
|
188
|
-
}
|
|
189
|
-
function findProjectRoot2(from = process.cwd()) {
|
|
190
|
-
const resolved = findProjectRoot(from);
|
|
191
|
-
if (!resolved) {
|
|
192
|
-
throw new WpHsError("Could not find woodscli.json in this directory or any parent.", {
|
|
193
|
-
exitCode: ExitCode.USER_ERROR,
|
|
194
|
-
remediation: "Run wp init, or pass --project <path>. Legacy config: wp migrate --yes."
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
return resolved;
|
|
198
|
-
}
|
|
199
|
-
function kitPath(...segments) {
|
|
200
|
-
return resolve2(kitRoot, ...segments);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// src/lib/config.ts
|
|
38
|
+
import { readFileSync } from "fs";
|
|
204
39
|
function loadConfig(projectRoot) {
|
|
205
|
-
const root = projectRoot ??
|
|
40
|
+
const root = projectRoot ?? findProjectRoot();
|
|
206
41
|
const resolved = resolveProjectConfig(root);
|
|
207
42
|
if (!resolved) {
|
|
208
43
|
throw new WpHsError(`Missing ${WOODSCLI_CONFIG_FILE}`, {
|
|
@@ -212,7 +47,7 @@ function loadConfig(projectRoot) {
|
|
|
212
47
|
}
|
|
213
48
|
let raw;
|
|
214
49
|
try {
|
|
215
|
-
raw = JSON.parse(
|
|
50
|
+
raw = JSON.parse(readFileSync(resolved.path, "utf8"));
|
|
216
51
|
} catch (cause) {
|
|
217
52
|
throw new WpHsError(`Invalid JSON in ${resolved.fileName}`, {
|
|
218
53
|
exitCode: ExitCode.USER_ERROR,
|
|
@@ -294,20 +129,160 @@ function createLogger(options = {}) {
|
|
|
294
129
|
};
|
|
295
130
|
}
|
|
296
131
|
|
|
132
|
+
// src/lib/kit-version-check.ts
|
|
133
|
+
import { existsSync, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
134
|
+
import { homedir } from "os";
|
|
135
|
+
import { dirname, join } from "path";
|
|
136
|
+
import pc2 from "picocolors";
|
|
137
|
+
import semver from "semver";
|
|
138
|
+
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@woodsportal%2Fhubspot-kit";
|
|
139
|
+
var VERSION_CHECK_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
140
|
+
var VERSION_CHECK_FETCH_TIMEOUT_MS = 2500;
|
|
141
|
+
function getVersionCheckCachePath() {
|
|
142
|
+
if (process.env.WP_VERSION_CHECK_CACHE) {
|
|
143
|
+
return process.env.WP_VERSION_CHECK_CACHE;
|
|
144
|
+
}
|
|
145
|
+
const base = process.env.XDG_CACHE_HOME ? join(process.env.XDG_CACHE_HOME, "woodsportal") : join(homedir(), ".cache", "woodsportal");
|
|
146
|
+
return join(base, "wp-kit-version.json");
|
|
147
|
+
}
|
|
148
|
+
function readVersionCheckCache(now = Date.now()) {
|
|
149
|
+
const cachePath = getVersionCheckCachePath();
|
|
150
|
+
if (!existsSync(cachePath)) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
const parsed = JSON.parse(readFileSync2(cachePath, "utf8"));
|
|
155
|
+
if (!parsed.latest || typeof parsed.fetchedAt !== "number") {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
if (now - parsed.fetchedAt > VERSION_CHECK_CACHE_TTL_MS) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
return parsed;
|
|
162
|
+
} catch {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function writeVersionCheckCache(cache) {
|
|
167
|
+
const cachePath = getVersionCheckCachePath();
|
|
168
|
+
mkdirSync(dirname(cachePath), { recursive: true });
|
|
169
|
+
writeFileSync(cachePath, `${JSON.stringify(cache)}
|
|
170
|
+
`, "utf8");
|
|
171
|
+
}
|
|
172
|
+
async function fetchLatestKitVersion() {
|
|
173
|
+
try {
|
|
174
|
+
const response = await fetch(NPM_REGISTRY_URL, {
|
|
175
|
+
signal: AbortSignal.timeout(VERSION_CHECK_FETCH_TIMEOUT_MS),
|
|
176
|
+
headers: { accept: "application/json" }
|
|
177
|
+
});
|
|
178
|
+
if (!response.ok) {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
const body = await response.json();
|
|
182
|
+
const latest = body["dist-tags"]?.latest;
|
|
183
|
+
return typeof latest === "string" && latest.length > 0 ? latest : null;
|
|
184
|
+
} catch {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async function resolveLatestKitVersion(now = Date.now()) {
|
|
189
|
+
const cached = readVersionCheckCache(now);
|
|
190
|
+
if (cached) {
|
|
191
|
+
return cached.latest;
|
|
192
|
+
}
|
|
193
|
+
const latest = await fetchLatestKitVersion();
|
|
194
|
+
if (!latest) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
writeVersionCheckCache({ latest, fetchedAt: now });
|
|
198
|
+
return latest;
|
|
199
|
+
}
|
|
200
|
+
function isKitUpdateAvailable(current, latest) {
|
|
201
|
+
const currentCoerced = semver.coerce(current);
|
|
202
|
+
const latestCoerced = semver.coerce(latest);
|
|
203
|
+
if (!currentCoerced || !latestCoerced) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
return semver.lt(currentCoerced, latestCoerced);
|
|
207
|
+
}
|
|
208
|
+
function formatKitUpgradeCommand(projectRoot) {
|
|
209
|
+
const root = projectRoot || process.cwd();
|
|
210
|
+
const spec = `${KIT_PACKAGE_NAME}@latest`;
|
|
211
|
+
if (existsSync(join(root, "yarn.lock"))) {
|
|
212
|
+
return `yarn add -D ${spec}`;
|
|
213
|
+
}
|
|
214
|
+
if (existsSync(join(root, "pnpm-lock.yaml"))) {
|
|
215
|
+
return `pnpm add -D ${spec}`;
|
|
216
|
+
}
|
|
217
|
+
return `npm install -D ${spec}`;
|
|
218
|
+
}
|
|
219
|
+
function shouldUseWarnColor() {
|
|
220
|
+
if (process.env.NO_COLOR !== void 0) return false;
|
|
221
|
+
if (process.env.CI === "true") return false;
|
|
222
|
+
return process.stderr.isTTY === true;
|
|
223
|
+
}
|
|
224
|
+
function shouldSkipVersionCheck(options) {
|
|
225
|
+
if (options.ci || process.env.CI === "true") return true;
|
|
226
|
+
if (options.json || options.quiet) return true;
|
|
227
|
+
if (process.env.WP_SKIP_VERSION_CHECK === "1") return true;
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
function formatKitUpdateWarning(current, latest, projectRoot) {
|
|
231
|
+
const upgrade = formatKitUpgradeCommand(projectRoot);
|
|
232
|
+
const useColor = shouldUseWarnColor();
|
|
233
|
+
const warn = useColor ? pc2.yellow("warn") : "warn";
|
|
234
|
+
const dim = useColor ? pc2.dim : (text5) => text5;
|
|
235
|
+
return [
|
|
236
|
+
`[${warn}] ${KIT_PACKAGE_NAME} v${current} is installed; latest is v${latest}.`,
|
|
237
|
+
`${dim(" ")}Upgrade: ${upgrade}`
|
|
238
|
+
].join("\n");
|
|
239
|
+
}
|
|
240
|
+
async function maybeWarnKitUpdate(options) {
|
|
241
|
+
try {
|
|
242
|
+
if (shouldSkipVersionCheck(options)) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const latest = await resolveLatestKitVersion();
|
|
246
|
+
if (!latest || !isKitUpdateAvailable(options.current, latest)) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
process.stderr.write(`${formatKitUpdateWarning(options.current, latest, options.projectRoot)}
|
|
250
|
+
`);
|
|
251
|
+
} catch {
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// src/lib/kit-version.ts
|
|
256
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
257
|
+
import { resolve } from "path";
|
|
258
|
+
var cachedVersion = null;
|
|
259
|
+
function readKitVersion() {
|
|
260
|
+
if (cachedVersion) {
|
|
261
|
+
return cachedVersion;
|
|
262
|
+
}
|
|
263
|
+
try {
|
|
264
|
+
const pkg = JSON.parse(readFileSync3(resolve(getKitRoot(), "package.json"), "utf8"));
|
|
265
|
+
cachedVersion = pkg.version ?? "0.0.0";
|
|
266
|
+
} catch {
|
|
267
|
+
cachedVersion = "0.0.0";
|
|
268
|
+
}
|
|
269
|
+
return cachedVersion;
|
|
270
|
+
}
|
|
271
|
+
|
|
297
272
|
// src/lib/wphs-baseline.ts
|
|
298
|
-
import { resolve as
|
|
273
|
+
import { resolve as resolve2 } from "path";
|
|
299
274
|
function defaultBaselineDir(projectRoot, config) {
|
|
300
275
|
const workspace = config.dev.moduleConfigDir ?? WPHS_WORKSPACE_DIR;
|
|
301
|
-
return
|
|
276
|
+
return resolve2(projectRoot, workspace, "audit", "baseline");
|
|
302
277
|
}
|
|
303
278
|
|
|
304
279
|
// src/cli/commands/doctor.ts
|
|
305
|
-
import { existsSync as
|
|
306
|
-
import { join, resolve as
|
|
280
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
281
|
+
import { join as join2, resolve as resolve7 } from "path";
|
|
307
282
|
|
|
308
283
|
// src/lib/cli-spinner.ts
|
|
309
284
|
import * as p from "@clack/prompts";
|
|
310
|
-
import
|
|
285
|
+
import pc3 from "picocolors";
|
|
311
286
|
function isInteractiveCli(ctx) {
|
|
312
287
|
const { json, quiet, ci } = ctx.global;
|
|
313
288
|
return !json && !quiet && !ci && process.stdout.isTTY === true;
|
|
@@ -321,11 +296,11 @@ function isVerboseStepLog(ctx) {
|
|
|
321
296
|
return ctx.global.verbose === true && !ctx.global.quiet && !ctx.global.json;
|
|
322
297
|
}
|
|
323
298
|
async function yieldForSpinnerPaint() {
|
|
324
|
-
await new Promise((
|
|
325
|
-
setImmediate(
|
|
299
|
+
await new Promise((resolve23) => {
|
|
300
|
+
setImmediate(resolve23);
|
|
326
301
|
});
|
|
327
|
-
await new Promise((
|
|
328
|
-
setTimeout(
|
|
302
|
+
await new Promise((resolve23) => {
|
|
303
|
+
setTimeout(resolve23, 48);
|
|
329
304
|
});
|
|
330
305
|
}
|
|
331
306
|
async function runCliTasks(ctx, tasks, options = {}) {
|
|
@@ -372,7 +347,7 @@ async function runCliTasks(ctx, tasks, options = {}) {
|
|
|
372
347
|
await step.task();
|
|
373
348
|
spinner2.stop(step.title);
|
|
374
349
|
} catch (error) {
|
|
375
|
-
spinner2.stop(
|
|
350
|
+
spinner2.stop(pc3.red(step.title));
|
|
376
351
|
throw error;
|
|
377
352
|
}
|
|
378
353
|
}
|
|
@@ -384,7 +359,7 @@ function shouldQuietSubprocess(ctx, options = {}) {
|
|
|
384
359
|
}
|
|
385
360
|
function printCommandDone(ctx, message) {
|
|
386
361
|
if (isInteractiveCli(ctx)) {
|
|
387
|
-
process.stdout.write(`${
|
|
362
|
+
process.stdout.write(`${pc3.green("\u25C6")} ${message}
|
|
388
363
|
`);
|
|
389
364
|
return;
|
|
390
365
|
}
|
|
@@ -392,8 +367,8 @@ function printCommandDone(ctx, message) {
|
|
|
392
367
|
}
|
|
393
368
|
|
|
394
369
|
// src/lib/hubspot-cli-setup.ts
|
|
395
|
-
import { existsSync as
|
|
396
|
-
import { resolve as
|
|
370
|
+
import { existsSync as existsSync2, readFileSync as readFileSync4 } from "fs";
|
|
371
|
+
import { resolve as resolve3 } from "path";
|
|
397
372
|
import { spawnSync } from "child_process";
|
|
398
373
|
var HUBSPOT_CONFIG_FILE = "hubspot.config.yml";
|
|
399
374
|
var HUBSPOT_CONFIG_EXAMPLE = "hubspot.config.yml.example";
|
|
@@ -419,13 +394,13 @@ function hsTimedOut(result) {
|
|
|
419
394
|
return err?.code === "ETIMEDOUT";
|
|
420
395
|
}
|
|
421
396
|
function assessHubSpotConfigFiles(projectRoot) {
|
|
422
|
-
const configPath =
|
|
423
|
-
const examplePath =
|
|
424
|
-
const configPresent =
|
|
425
|
-
const examplePresent =
|
|
397
|
+
const configPath = resolve3(projectRoot, HUBSPOT_CONFIG_FILE);
|
|
398
|
+
const examplePath = resolve3(projectRoot, HUBSPOT_CONFIG_EXAMPLE);
|
|
399
|
+
const configPresent = existsSync2(configPath);
|
|
400
|
+
const examplePresent = existsSync2(examplePath);
|
|
426
401
|
let looksLikePlaceholder = false;
|
|
427
402
|
if (configPresent) {
|
|
428
|
-
const raw =
|
|
403
|
+
const raw = readFileSync4(configPath, "utf8");
|
|
429
404
|
looksLikePlaceholder = PLACEHOLDER_PATTERNS.some((pattern) => pattern.test(raw));
|
|
430
405
|
} else {
|
|
431
406
|
looksLikePlaceholder = true;
|
|
@@ -692,14 +667,14 @@ function validateHubSpotFieldsJson(fields) {
|
|
|
692
667
|
}
|
|
693
668
|
|
|
694
669
|
// src/lib/developer-slug.ts
|
|
695
|
-
import { existsSync as
|
|
696
|
-
import { resolve as
|
|
670
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
671
|
+
import { resolve as resolve4 } from "path";
|
|
697
672
|
import * as p2 from "@clack/prompts";
|
|
698
673
|
var SLUG_MAX_LEN = 12;
|
|
699
674
|
var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,10}[a-z0-9])?$/;
|
|
700
675
|
var DEVELOPER_CONFIG_FILE = "developer.json";
|
|
701
676
|
function developerConfigPath(projectRoot) {
|
|
702
|
-
return
|
|
677
|
+
return resolve4(projectRoot, ".wphs", "config", DEVELOPER_CONFIG_FILE);
|
|
703
678
|
}
|
|
704
679
|
function sanitizeDevSlug(input) {
|
|
705
680
|
if (!input?.trim()) return void 0;
|
|
@@ -711,9 +686,9 @@ function readDeveloperSlug(projectRoot) {
|
|
|
711
686
|
const fromEnv = sanitizeDevSlug(process.env.PORTAL_CDN_DEV_SLUG) ?? sanitizeDevSlug(process.env.MODULE_CDN_DEV_SLUG);
|
|
712
687
|
if (fromEnv) return fromEnv;
|
|
713
688
|
const path = developerConfigPath(projectRoot);
|
|
714
|
-
if (!
|
|
689
|
+
if (!existsSync3(path)) return void 0;
|
|
715
690
|
try {
|
|
716
|
-
const raw = JSON.parse(
|
|
691
|
+
const raw = JSON.parse(readFileSync5(path, "utf8"));
|
|
717
692
|
return sanitizeDevSlug(raw.slug);
|
|
718
693
|
} catch {
|
|
719
694
|
return void 0;
|
|
@@ -725,8 +700,8 @@ function writeDeveloperSlug(projectRoot, slug) {
|
|
|
725
700
|
throw new Error("Invalid CDN dev slug \u2014 use lowercase letters, numbers, and hyphens (max 12 chars)");
|
|
726
701
|
}
|
|
727
702
|
const path = developerConfigPath(projectRoot);
|
|
728
|
-
|
|
729
|
-
|
|
703
|
+
mkdirSync2(resolve4(path, ".."), { recursive: true });
|
|
704
|
+
writeFileSync2(path, `${JSON.stringify({ slug: normalized }, null, 2)}
|
|
730
705
|
`);
|
|
731
706
|
}
|
|
732
707
|
function needsDeveloperSlugSetup(projectRoot) {
|
|
@@ -788,37 +763,37 @@ function resolveModuleBuildIdMetaForCli(projectRoot, env = process.env) {
|
|
|
788
763
|
}
|
|
789
764
|
|
|
790
765
|
// src/lib/upload-only-module.ts
|
|
791
|
-
import { existsSync as
|
|
792
|
-
import { resolve as
|
|
766
|
+
import { existsSync as existsSync4 } from "fs";
|
|
767
|
+
import { resolve as resolve5 } from "path";
|
|
793
768
|
function isUploadOnlyPreexistingProject(projectRoot, moduleArtifactDirs) {
|
|
794
769
|
if (moduleArtifactDirs.length === 0) return false;
|
|
795
|
-
if (
|
|
796
|
-
if (
|
|
797
|
-
return moduleArtifactDirs.some((dir) =>
|
|
770
|
+
if (existsSync4(resolve5(projectRoot, "package.json"))) return false;
|
|
771
|
+
if (existsSync4(resolve5(projectRoot, "src"))) return false;
|
|
772
|
+
return moduleArtifactDirs.some((dir) => existsSync4(resolve5(dir, "fields.json")));
|
|
798
773
|
}
|
|
799
774
|
|
|
800
775
|
// src/vite/resolve-dev-config.ts
|
|
801
|
-
import { existsSync as
|
|
776
|
+
import { existsSync as existsSync5 } from "fs";
|
|
802
777
|
import { platform } from "os";
|
|
803
|
-
import { resolve as
|
|
778
|
+
import { resolve as resolve6 } from "path";
|
|
804
779
|
var VITE_JS_SEGMENTS = ["node_modules", "vite", "bin", "vite.js"];
|
|
805
780
|
function isProjectViteInstalled(projectRoot) {
|
|
806
|
-
return
|
|
781
|
+
return existsSync5(resolve6(projectRoot, ...VITE_JS_SEGMENTS));
|
|
807
782
|
}
|
|
808
783
|
function resolveProjectViteSpawn(projectRoot) {
|
|
809
|
-
const viteJs =
|
|
810
|
-
if (
|
|
784
|
+
const viteJs = resolve6(projectRoot, ...VITE_JS_SEGMENTS);
|
|
785
|
+
if (existsSync5(viteJs)) {
|
|
811
786
|
return { command: process.execPath, prefixArgs: [viteJs] };
|
|
812
787
|
}
|
|
813
|
-
const binDir =
|
|
788
|
+
const binDir = resolve6(projectRoot, "node_modules", ".bin");
|
|
814
789
|
if (platform() === "win32") {
|
|
815
|
-
const winCmd =
|
|
816
|
-
if (
|
|
790
|
+
const winCmd = resolve6(binDir, "vite.cmd");
|
|
791
|
+
if (existsSync5(winCmd)) {
|
|
817
792
|
return { command: winCmd, prefixArgs: [] };
|
|
818
793
|
}
|
|
819
794
|
}
|
|
820
|
-
const posixShim =
|
|
821
|
-
if (
|
|
795
|
+
const posixShim = resolve6(binDir, "vite");
|
|
796
|
+
if (existsSync5(posixShim)) {
|
|
822
797
|
return { command: posixShim, prefixArgs: [] };
|
|
823
798
|
}
|
|
824
799
|
return null;
|
|
@@ -905,10 +880,10 @@ function envFileForTier(tier) {
|
|
|
905
880
|
return ".env.prod";
|
|
906
881
|
}
|
|
907
882
|
function readEnvFileValue(projectRoot, file, key) {
|
|
908
|
-
const path =
|
|
909
|
-
if (!
|
|
883
|
+
const path = resolve7(projectRoot, file);
|
|
884
|
+
if (!existsSync6(path)) return void 0;
|
|
910
885
|
try {
|
|
911
|
-
const content =
|
|
886
|
+
const content = readFileSync6(path, "utf8");
|
|
912
887
|
const match = content.match(new RegExp(`^${key}=(.*)$`, "m"));
|
|
913
888
|
return match?.[1]?.trim();
|
|
914
889
|
} catch {
|
|
@@ -917,8 +892,8 @@ function readEnvFileValue(projectRoot, file, key) {
|
|
|
917
892
|
}
|
|
918
893
|
function checkEnvFiles(ctx) {
|
|
919
894
|
const checks = [];
|
|
920
|
-
const localPath =
|
|
921
|
-
const localOk =
|
|
895
|
+
const localPath = resolve7(ctx.projectRoot, ".env.local");
|
|
896
|
+
const localOk = existsSync6(localPath);
|
|
922
897
|
checks.push({
|
|
923
898
|
name: "env-local",
|
|
924
899
|
ok: localOk,
|
|
@@ -926,8 +901,8 @@ function checkEnvFiles(ctx) {
|
|
|
926
901
|
remediation: localOk ? void 0 : "wp setup or copy .env.local.example \u2192 .env.local",
|
|
927
902
|
severity: "warn"
|
|
928
903
|
});
|
|
929
|
-
const legacyLocalDev =
|
|
930
|
-
if (!localOk &&
|
|
904
|
+
const legacyLocalDev = resolve7(ctx.projectRoot, ".env.dev");
|
|
905
|
+
if (!localOk && existsSync6(legacyLocalDev)) {
|
|
931
906
|
const nodeEnv = readEnvFileValue(ctx.projectRoot, ".env.dev", "VITE_NODE_ENV");
|
|
932
907
|
const hasDevMode = readEnvFileValue(ctx.projectRoot, ".env.dev", "VITE_DEV_MODE") != null;
|
|
933
908
|
const looksLikeLocal = hasDevMode || nodeEnv === "local" || nodeEnv == null || nodeEnv === "development";
|
|
@@ -943,7 +918,7 @@ function checkEnvFiles(ctx) {
|
|
|
943
918
|
}
|
|
944
919
|
for (const tier of ctx.config.env.tiers) {
|
|
945
920
|
const file = envFileForTier(tier);
|
|
946
|
-
const ok =
|
|
921
|
+
const ok = existsSync6(resolve7(ctx.projectRoot, file));
|
|
947
922
|
if (tier === "stg") {
|
|
948
923
|
checks.push({
|
|
949
924
|
name: "env-stg",
|
|
@@ -966,8 +941,8 @@ function checkEnvFiles(ctx) {
|
|
|
966
941
|
}
|
|
967
942
|
function checkLegacyBuildIdFile(ctx) {
|
|
968
943
|
if (isThemeProject(ctx.config) || !usesCdnPipeline(ctx.config)) return null;
|
|
969
|
-
const legacy =
|
|
970
|
-
if (!
|
|
944
|
+
const legacy = resolve7(ctx.projectRoot, ".portal-build-id");
|
|
945
|
+
if (!existsSync6(legacy)) return null;
|
|
971
946
|
return {
|
|
972
947
|
name: "legacy-build-id",
|
|
973
948
|
ok: false,
|
|
@@ -978,8 +953,8 @@ function checkLegacyBuildIdFile(ctx) {
|
|
|
978
953
|
}
|
|
979
954
|
function checkBuildId(ctx) {
|
|
980
955
|
if (isThemeProject(ctx.config) || !usesCdnPipeline(ctx.config)) return null;
|
|
981
|
-
const file =
|
|
982
|
-
const ok =
|
|
956
|
+
const file = resolve7(ctx.projectRoot, ctx.config.cdn.buildIdFile);
|
|
957
|
+
const ok = existsSync6(file);
|
|
983
958
|
return {
|
|
984
959
|
name: "build-id",
|
|
985
960
|
ok,
|
|
@@ -1033,7 +1008,7 @@ function checkDevSlug(ctx) {
|
|
|
1033
1008
|
};
|
|
1034
1009
|
}
|
|
1035
1010
|
function isUploadOnlyProject(ctx) {
|
|
1036
|
-
const moduleDirs = [
|
|
1011
|
+
const moduleDirs = [resolve7(ctx.projectRoot, ctx.config.module.uploadPath)];
|
|
1037
1012
|
return isUploadOnlyPreexistingProject(ctx.projectRoot, moduleDirs);
|
|
1038
1013
|
}
|
|
1039
1014
|
function checkUploadOnlyNote(ctx) {
|
|
@@ -1047,8 +1022,8 @@ function checkUploadOnlyNote(ctx) {
|
|
|
1047
1022
|
};
|
|
1048
1023
|
}
|
|
1049
1024
|
function checkDevViteConfig(ctx) {
|
|
1050
|
-
const path =
|
|
1051
|
-
const ok =
|
|
1025
|
+
const path = resolve7(ctx.projectRoot, "vite.config.dev.mjs");
|
|
1026
|
+
const ok = existsSync6(path);
|
|
1052
1027
|
return {
|
|
1053
1028
|
name: "vite-dev-config",
|
|
1054
1029
|
ok,
|
|
@@ -1059,8 +1034,8 @@ function checkDevViteConfig(ctx) {
|
|
|
1059
1034
|
}
|
|
1060
1035
|
function checkMetaJson(ctx) {
|
|
1061
1036
|
if (isThemeProject(ctx.config)) return null;
|
|
1062
|
-
const path =
|
|
1063
|
-
if (!
|
|
1037
|
+
const path = resolve7(ctx.projectRoot, "public/meta.json");
|
|
1038
|
+
if (!existsSync6(path)) {
|
|
1064
1039
|
return {
|
|
1065
1040
|
name: "meta-json",
|
|
1066
1041
|
ok: false,
|
|
@@ -1070,7 +1045,7 @@ function checkMetaJson(ctx) {
|
|
|
1070
1045
|
};
|
|
1071
1046
|
}
|
|
1072
1047
|
try {
|
|
1073
|
-
const meta = JSON.parse(
|
|
1048
|
+
const meta = JSON.parse(readFileSync6(path, "utf8"));
|
|
1074
1049
|
const hasContentTypes = Array.isArray(meta.content_types) && meta.content_types.length > 0;
|
|
1075
1050
|
return {
|
|
1076
1051
|
name: "meta-json",
|
|
@@ -1092,8 +1067,8 @@ function checkMetaJson(ctx) {
|
|
|
1092
1067
|
}
|
|
1093
1068
|
async function checkCdnMirrorPublic(ctx) {
|
|
1094
1069
|
if (isThemeProject(ctx.config) || !usesCdnPipeline(ctx.config)) return null;
|
|
1095
|
-
const mirrorPath =
|
|
1096
|
-
if (!
|
|
1070
|
+
const mirrorPath = resolve7(ctx.projectRoot, ctx.config.cdn?.mirror ?? "portal.cdn.json");
|
|
1071
|
+
if (!existsSync6(mirrorPath)) {
|
|
1097
1072
|
return {
|
|
1098
1073
|
name: "cdn-mirror-public",
|
|
1099
1074
|
ok: false,
|
|
@@ -1104,7 +1079,7 @@ async function checkCdnMirrorPublic(ctx) {
|
|
|
1104
1079
|
}
|
|
1105
1080
|
let github;
|
|
1106
1081
|
try {
|
|
1107
|
-
const mirror = JSON.parse(
|
|
1082
|
+
const mirror = JSON.parse(readFileSync6(mirrorPath, "utf8"));
|
|
1108
1083
|
github = mirror.github?.trim();
|
|
1109
1084
|
} catch {
|
|
1110
1085
|
return {
|
|
@@ -1156,8 +1131,8 @@ async function checkCdnMirrorPublic(ctx) {
|
|
|
1156
1131
|
severity: "warn"
|
|
1157
1132
|
};
|
|
1158
1133
|
}
|
|
1159
|
-
const manifestPath =
|
|
1160
|
-
if (!
|
|
1134
|
+
const manifestPath = resolve7(ctx.projectRoot, "dist/cdn/manifest.json");
|
|
1135
|
+
if (!existsSync6(manifestPath)) {
|
|
1161
1136
|
return {
|
|
1162
1137
|
name: "cdn-mirror-public",
|
|
1163
1138
|
ok: true,
|
|
@@ -1166,7 +1141,7 @@ async function checkCdnMirrorPublic(ctx) {
|
|
|
1166
1141
|
};
|
|
1167
1142
|
}
|
|
1168
1143
|
try {
|
|
1169
|
-
const manifest = JSON.parse(
|
|
1144
|
+
const manifest = JSON.parse(readFileSync6(manifestPath, "utf8"));
|
|
1170
1145
|
const appUrl = manifest.scripts?.find((s) => s.name === "app")?.url;
|
|
1171
1146
|
if (!appUrl) {
|
|
1172
1147
|
return {
|
|
@@ -1219,8 +1194,8 @@ async function checkCdnMirrorPublic(ctx) {
|
|
|
1219
1194
|
}
|
|
1220
1195
|
function checkFieldsJson(ctx) {
|
|
1221
1196
|
if (isThemeProject(ctx.config)) return null;
|
|
1222
|
-
const path =
|
|
1223
|
-
if (!
|
|
1197
|
+
const path = resolve7(ctx.projectRoot, "public/fields.json");
|
|
1198
|
+
if (!existsSync6(path)) {
|
|
1224
1199
|
return {
|
|
1225
1200
|
name: "fields-json",
|
|
1226
1201
|
ok: false,
|
|
@@ -1230,7 +1205,7 @@ function checkFieldsJson(ctx) {
|
|
|
1230
1205
|
};
|
|
1231
1206
|
}
|
|
1232
1207
|
try {
|
|
1233
|
-
const fields = JSON.parse(
|
|
1208
|
+
const fields = JSON.parse(readFileSync6(path, "utf8"));
|
|
1234
1209
|
const issues = validateHubSpotFieldsJson(fields);
|
|
1235
1210
|
if (issues.length === 0) {
|
|
1236
1211
|
return {
|
|
@@ -1260,11 +1235,11 @@ function checkFieldsJson(ctx) {
|
|
|
1260
1235
|
}
|
|
1261
1236
|
}
|
|
1262
1237
|
function readProjectFile(projectRoot, relativePath) {
|
|
1263
|
-
const path =
|
|
1264
|
-
if (!
|
|
1238
|
+
const path = resolve7(projectRoot, relativePath);
|
|
1239
|
+
if (!existsSync6(path)) {
|
|
1265
1240
|
return null;
|
|
1266
1241
|
}
|
|
1267
|
-
return
|
|
1242
|
+
return readFileSync6(path, "utf8");
|
|
1268
1243
|
}
|
|
1269
1244
|
function checkCssPipeline(ctx) {
|
|
1270
1245
|
if (isThemeProject(ctx.config) || isUploadOnlyProject(ctx)) {
|
|
@@ -1331,9 +1306,9 @@ function checkCssPipeline(ctx) {
|
|
|
1331
1306
|
}
|
|
1332
1307
|
const moduleOutDir = ctx.config.module?.outDir;
|
|
1333
1308
|
if (moduleOutDir && usesCdnPipeline(ctx.config)) {
|
|
1334
|
-
const moduleCssPath =
|
|
1335
|
-
if (
|
|
1336
|
-
const css =
|
|
1309
|
+
const moduleCssPath = resolve7(ctx.projectRoot, moduleOutDir, "module.css");
|
|
1310
|
+
if (existsSync6(moduleCssPath)) {
|
|
1311
|
+
const css = readFileSync6(moduleCssPath, "utf8");
|
|
1337
1312
|
const hasDwUtilities = /dw\\:/.test(css) || /\.dw:/.test(css);
|
|
1338
1313
|
checks.push({
|
|
1339
1314
|
name: "css-module-dist-prefixed",
|
|
@@ -1348,8 +1323,8 @@ function checkCssPipeline(ctx) {
|
|
|
1348
1323
|
}
|
|
1349
1324
|
function checkHybridModuleHtml(ctx) {
|
|
1350
1325
|
if (isThemeProject(ctx.config) || !usesCdnPipeline(ctx.config)) return null;
|
|
1351
|
-
const path =
|
|
1352
|
-
if (!
|
|
1326
|
+
const path = resolve7(ctx.projectRoot, "public/module.html");
|
|
1327
|
+
if (!existsSync6(path)) {
|
|
1353
1328
|
return {
|
|
1354
1329
|
name: "module-html",
|
|
1355
1330
|
ok: false,
|
|
@@ -1358,7 +1333,7 @@ function checkHybridModuleHtml(ctx) {
|
|
|
1358
1333
|
severity: "error"
|
|
1359
1334
|
};
|
|
1360
1335
|
}
|
|
1361
|
-
const html =
|
|
1336
|
+
const html = readFileSync6(path, "utf8");
|
|
1362
1337
|
const needs = ["__PORTAL_CDN_BASE__", "require_js", "hubSpotData", 'id="app"'];
|
|
1363
1338
|
const missing = needs.filter((token) => !html.includes(token));
|
|
1364
1339
|
const usesUndeclaredCdnFields = /module\.portal_(cdn_base_url|vendor_script|app_script|app_css)/.test(
|
|
@@ -1376,8 +1351,8 @@ function checkHybridModuleHtml(ctx) {
|
|
|
1376
1351
|
function checkCdnViteConfigs(ctx) {
|
|
1377
1352
|
if (isThemeProject(ctx.config) || !usesCdnPipeline(ctx.config)) return [];
|
|
1378
1353
|
return ["vite.config.cdn-vendor.mjs", "vite.config.cdn-app.mjs"].map((file) => {
|
|
1379
|
-
const path =
|
|
1380
|
-
const ok =
|
|
1354
|
+
const path = resolve7(ctx.projectRoot, file);
|
|
1355
|
+
const ok = existsSync6(path);
|
|
1381
1356
|
return {
|
|
1382
1357
|
name: file.replace(/\./g, "-"),
|
|
1383
1358
|
ok,
|
|
@@ -1389,8 +1364,8 @@ function checkCdnViteConfigs(ctx) {
|
|
|
1389
1364
|
}
|
|
1390
1365
|
function checkDevEntry(ctx) {
|
|
1391
1366
|
const checks = [];
|
|
1392
|
-
const indexHtml =
|
|
1393
|
-
const hasIndex =
|
|
1367
|
+
const indexHtml = resolve7(ctx.projectRoot, "index.html");
|
|
1368
|
+
const hasIndex = existsSync6(indexHtml);
|
|
1394
1369
|
checks.push({
|
|
1395
1370
|
name: "index-html",
|
|
1396
1371
|
ok: hasIndex,
|
|
@@ -1398,8 +1373,8 @@ function checkDevEntry(ctx) {
|
|
|
1398
1373
|
remediation: hasIndex ? void 0 : "Re-run wp init or copy index.html from kit templates",
|
|
1399
1374
|
severity: "error"
|
|
1400
1375
|
});
|
|
1401
|
-
const localDev =
|
|
1402
|
-
const hasEntry =
|
|
1376
|
+
const localDev = resolve7(ctx.projectRoot, ctx.config.entries.localDev);
|
|
1377
|
+
const hasEntry = existsSync6(localDev);
|
|
1403
1378
|
checks.push({
|
|
1404
1379
|
name: "local-dev-entry",
|
|
1405
1380
|
ok: hasEntry,
|
|
@@ -1409,7 +1384,7 @@ function checkDevEntry(ctx) {
|
|
|
1409
1384
|
});
|
|
1410
1385
|
if (hasEntry) {
|
|
1411
1386
|
try {
|
|
1412
|
-
const source =
|
|
1387
|
+
const source = readFileSync6(localDev, "utf8");
|
|
1413
1388
|
const usesKitNodeBootstrap = source.includes("@woodsportal/hubspot-kit/dev/bootstrap-greenfield") || source.includes("@woodsportal/hubspot-kit/dev/bootstrap");
|
|
1414
1389
|
const usesWphsShim = source.includes(".wphs/.kit-bootstrap-greenfield") || source.includes(".wphs/.kit-bootstrap") || source.includes("@wphs/bootstrap-greenfield") || source.includes("@wphs/bootstrap");
|
|
1415
1390
|
checks.push({
|
|
@@ -1426,7 +1401,7 @@ function checkDevEntry(ctx) {
|
|
|
1426
1401
|
}
|
|
1427
1402
|
function checkKitScripts() {
|
|
1428
1403
|
const script = kitPath("scripts", "build-cdn.mjs");
|
|
1429
|
-
const ok =
|
|
1404
|
+
const ok = existsSync6(script);
|
|
1430
1405
|
return {
|
|
1431
1406
|
name: "kit-scripts",
|
|
1432
1407
|
ok,
|
|
@@ -1439,8 +1414,8 @@ function checkWphsWorkspace(ctx) {
|
|
|
1439
1414
|
const workspace = ctx.config.dev.moduleConfigDir;
|
|
1440
1415
|
const checks = [];
|
|
1441
1416
|
for (const legacy of [".dev", ".woodsportal"]) {
|
|
1442
|
-
const legacyPath =
|
|
1443
|
-
if (
|
|
1417
|
+
const legacyPath = resolve7(ctx.projectRoot, legacy);
|
|
1418
|
+
if (existsSync6(legacyPath)) {
|
|
1444
1419
|
checks.push({
|
|
1445
1420
|
name: `legacy-${legacy}`,
|
|
1446
1421
|
ok: false,
|
|
@@ -1450,7 +1425,7 @@ function checkWphsWorkspace(ctx) {
|
|
|
1450
1425
|
});
|
|
1451
1426
|
}
|
|
1452
1427
|
}
|
|
1453
|
-
if (
|
|
1428
|
+
if (existsSync6(resolve7(ctx.projectRoot, ".wp-hs-baseline"))) {
|
|
1454
1429
|
checks.push({
|
|
1455
1430
|
name: "legacy-baseline",
|
|
1456
1431
|
ok: false,
|
|
@@ -1469,10 +1444,10 @@ function checkWphsWorkspace(ctx) {
|
|
|
1469
1444
|
});
|
|
1470
1445
|
return checks;
|
|
1471
1446
|
}
|
|
1472
|
-
const wphsRoot =
|
|
1447
|
+
const wphsRoot = resolve7(ctx.projectRoot, workspace);
|
|
1473
1448
|
const required = ["config", "assets/branding", "fixtures", "cache", "audit/baseline", "state"];
|
|
1474
1449
|
for (const sub of required) {
|
|
1475
|
-
const ok =
|
|
1450
|
+
const ok = existsSync6(join2(wphsRoot, sub));
|
|
1476
1451
|
checks.push({
|
|
1477
1452
|
name: `wphs-${sub.replace(/\//g, "-")}`,
|
|
1478
1453
|
ok,
|
|
@@ -1485,8 +1460,8 @@ function checkWphsWorkspace(ctx) {
|
|
|
1485
1460
|
}
|
|
1486
1461
|
function checkAdapter(ctx) {
|
|
1487
1462
|
if (isThemeProject(ctx.config)) return null;
|
|
1488
|
-
const adapterPath =
|
|
1489
|
-
const ok =
|
|
1463
|
+
const adapterPath = resolve7(ctx.projectRoot, ctx.config.dev.adapter);
|
|
1464
|
+
const ok = existsSync6(adapterPath);
|
|
1490
1465
|
return {
|
|
1491
1466
|
name: "wphs-adapter",
|
|
1492
1467
|
ok,
|
|
@@ -1497,11 +1472,11 @@ function checkAdapter(ctx) {
|
|
|
1497
1472
|
}
|
|
1498
1473
|
function checkModuleConfigUiDeps(ctx) {
|
|
1499
1474
|
if (isThemeProject(ctx.config)) return [];
|
|
1500
|
-
const pkgPath =
|
|
1501
|
-
if (!
|
|
1475
|
+
const pkgPath = resolve7(ctx.projectRoot, "package.json");
|
|
1476
|
+
if (!existsSync6(pkgPath)) return [];
|
|
1502
1477
|
let deps = {};
|
|
1503
1478
|
try {
|
|
1504
|
-
const pkg = JSON.parse(
|
|
1479
|
+
const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
|
|
1505
1480
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
1506
1481
|
} catch {
|
|
1507
1482
|
return [];
|
|
@@ -1528,7 +1503,7 @@ function checkNodeLoaderInterop() {
|
|
|
1528
1503
|
};
|
|
1529
1504
|
}
|
|
1530
1505
|
function checkViteInstalled(ctx) {
|
|
1531
|
-
if (isThemeProject(ctx.config) && !
|
|
1506
|
+
if (isThemeProject(ctx.config) && !existsSync6(resolve7(ctx.projectRoot, "vite.config.dev.mjs"))) {
|
|
1532
1507
|
return null;
|
|
1533
1508
|
}
|
|
1534
1509
|
const ok = isProjectViteInstalled(ctx.projectRoot);
|
|
@@ -1685,29 +1660,10 @@ async function runConfigValidate(ctx) {
|
|
|
1685
1660
|
}
|
|
1686
1661
|
|
|
1687
1662
|
// src/cli/commands/build.ts
|
|
1688
|
-
import { resolve as
|
|
1689
|
-
|
|
1690
|
-
// src/lib/cli-banner.ts
|
|
1691
|
-
import pc3 from "picocolors";
|
|
1692
|
-
|
|
1693
|
-
// src/lib/kit-version.ts
|
|
1694
|
-
import { readFileSync as readFileSync6 } from "fs";
|
|
1695
|
-
import { resolve as resolve9 } from "path";
|
|
1696
|
-
var cachedVersion = null;
|
|
1697
|
-
function readKitVersion() {
|
|
1698
|
-
if (cachedVersion) {
|
|
1699
|
-
return cachedVersion;
|
|
1700
|
-
}
|
|
1701
|
-
try {
|
|
1702
|
-
const pkg = JSON.parse(readFileSync6(resolve9(getKitRoot(), "package.json"), "utf8"));
|
|
1703
|
-
cachedVersion = pkg.version ?? "0.0.0";
|
|
1704
|
-
} catch {
|
|
1705
|
-
cachedVersion = "0.0.0";
|
|
1706
|
-
}
|
|
1707
|
-
return cachedVersion;
|
|
1708
|
-
}
|
|
1663
|
+
import { resolve as resolve8 } from "path";
|
|
1709
1664
|
|
|
1710
1665
|
// src/lib/cli-banner.ts
|
|
1666
|
+
import pc4 from "picocolors";
|
|
1711
1667
|
var WOODSCLI_ASCII_BANNER = `
|
|
1712
1668
|
__ __ _ _____ _ _____
|
|
1713
1669
|
\\ \\ / / | | / ____| | |_ _|
|
|
@@ -1737,7 +1693,7 @@ function formatProjectLabel(config) {
|
|
|
1737
1693
|
return `${config.module.name} \xB7 ${config.projectType}${routerNote}`;
|
|
1738
1694
|
}
|
|
1739
1695
|
function section(color, title, body) {
|
|
1740
|
-
const bold = (
|
|
1696
|
+
const bold = (text5) => color ? pc4.bold(text5) : text5;
|
|
1741
1697
|
return ` ${bold(title)}
|
|
1742
1698
|
${body}`;
|
|
1743
1699
|
}
|
|
@@ -1746,10 +1702,10 @@ function printWoodsCliDevBanner(options) {
|
|
|
1746
1702
|
return;
|
|
1747
1703
|
}
|
|
1748
1704
|
const color = useCliColor();
|
|
1749
|
-
const cyan = (
|
|
1750
|
-
const dim = (
|
|
1751
|
-
const bold = (
|
|
1752
|
-
const yellow = (
|
|
1705
|
+
const cyan = (text5) => color ? pc4.cyan(text5) : text5;
|
|
1706
|
+
const dim = (text5) => color ? pc4.dim(text5) : text5;
|
|
1707
|
+
const bold = (text5) => color ? pc4.bold(text5) : text5;
|
|
1708
|
+
const yellow = (text5) => color ? pc4.yellow(text5) : text5;
|
|
1753
1709
|
const version = options.version ?? readKitVersion();
|
|
1754
1710
|
const project = formatProjectLabel(options.config);
|
|
1755
1711
|
const isTheme = isThemeProject(options.config);
|
|
@@ -1833,7 +1789,7 @@ function buildTasks(ctx, options, run) {
|
|
|
1833
1789
|
title: `Building monolith module (${tier})`,
|
|
1834
1790
|
task: async () => {
|
|
1835
1791
|
await run("run-vite-build.mjs", [
|
|
1836
|
-
|
|
1792
|
+
resolve8(ctx.projectRoot, "vite.config.monolith.mjs"),
|
|
1837
1793
|
...tierToViteModeFlag(tier)
|
|
1838
1794
|
]);
|
|
1839
1795
|
await run("run-hubspot-module-postbuild.mjs");
|
|
@@ -1847,7 +1803,7 @@ function buildTasks(ctx, options, run) {
|
|
|
1847
1803
|
task: async () => {
|
|
1848
1804
|
if (options.cdnEsm) {
|
|
1849
1805
|
await run("run-vite-build.mjs", [
|
|
1850
|
-
|
|
1806
|
+
resolve8(ctx.projectRoot, "vite.config.cdn-esm.mjs"),
|
|
1851
1807
|
...tierToViteModeFlag(tier)
|
|
1852
1808
|
]);
|
|
1853
1809
|
} else {
|
|
@@ -1862,7 +1818,7 @@ function buildTasks(ctx, options, run) {
|
|
|
1862
1818
|
title: `Building HubSpot module (${tier})`,
|
|
1863
1819
|
task: async () => {
|
|
1864
1820
|
await run("run-vite-build.mjs", [
|
|
1865
|
-
|
|
1821
|
+
resolve8(ctx.projectRoot, "vite.config.hubspot.mjs"),
|
|
1866
1822
|
...tierToViteModeFlag(tier)
|
|
1867
1823
|
]);
|
|
1868
1824
|
await run("run-hubspot-module-postbuild.mjs");
|
|
@@ -1959,7 +1915,7 @@ async function runBuild(ctx, options) {
|
|
|
1959
1915
|
command: "build",
|
|
1960
1916
|
tier,
|
|
1961
1917
|
ok: true,
|
|
1962
|
-
outDir:
|
|
1918
|
+
outDir: resolve8(ctx.projectRoot, ctx.config.module.outDir)
|
|
1963
1919
|
});
|
|
1964
1920
|
} else if (!options.quiet) {
|
|
1965
1921
|
printCommandDone(ctx, "Build complete");
|
|
@@ -2010,11 +1966,11 @@ async function runDev(ctx, options) {
|
|
|
2010
1966
|
}
|
|
2011
1967
|
|
|
2012
1968
|
// src/cli/commands/deploy.ts
|
|
2013
|
-
import { resolve as
|
|
1969
|
+
import { resolve as resolve11 } from "path";
|
|
2014
1970
|
|
|
2015
1971
|
// src/lib/project-env-files.ts
|
|
2016
|
-
import { existsSync as
|
|
2017
|
-
import { resolve as
|
|
1972
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
|
|
1973
|
+
import { resolve as resolve9 } from "path";
|
|
2018
1974
|
var ENV_LOCAL_FILE = ".env.local";
|
|
2019
1975
|
var ENV_GITIGNORE_LINES = [".env", ".env.*", "!.env*.example", ENV_LOCAL_FILE];
|
|
2020
1976
|
function parseEnvFile(content) {
|
|
@@ -2034,8 +1990,8 @@ function parseEnvFile(content) {
|
|
|
2034
1990
|
return result;
|
|
2035
1991
|
}
|
|
2036
1992
|
function readEnvLocal(projectRoot) {
|
|
2037
|
-
const path =
|
|
2038
|
-
if (!
|
|
1993
|
+
const path = resolve9(projectRoot, ENV_LOCAL_FILE);
|
|
1994
|
+
if (!existsSync7(path)) return {};
|
|
2039
1995
|
return parseEnvFile(readFileSync7(path, "utf8"));
|
|
2040
1996
|
}
|
|
2041
1997
|
function formatEnvLocal(entries) {
|
|
@@ -2055,9 +2011,9 @@ function formatEnvLocal(entries) {
|
|
|
2055
2011
|
`;
|
|
2056
2012
|
}
|
|
2057
2013
|
function writeEnvLocal(projectRoot, entries) {
|
|
2058
|
-
const path =
|
|
2059
|
-
const existing =
|
|
2060
|
-
|
|
2014
|
+
const path = resolve9(projectRoot, ENV_LOCAL_FILE);
|
|
2015
|
+
const existing = existsSync7(path) ? parseEnvFile(readFileSync7(path, "utf8")) : {};
|
|
2016
|
+
writeFileSync3(path, formatEnvLocal({ ...existing, ...entries }));
|
|
2061
2017
|
}
|
|
2062
2018
|
function mergeProjectEnv(projectRoot, base = process.env) {
|
|
2063
2019
|
const local = readEnvLocal(projectRoot);
|
|
@@ -2070,11 +2026,11 @@ function mergeProjectEnv(projectRoot, base = process.env) {
|
|
|
2070
2026
|
return merged;
|
|
2071
2027
|
}
|
|
2072
2028
|
function ensureEnvFilesGitignore(projectRoot) {
|
|
2073
|
-
const gitignorePath =
|
|
2074
|
-
const existing =
|
|
2029
|
+
const gitignorePath = resolve9(projectRoot, ".gitignore");
|
|
2030
|
+
const existing = existsSync7(gitignorePath) ? readFileSync7(gitignorePath, "utf8") : "";
|
|
2075
2031
|
const toAppend = ENV_GITIGNORE_LINES.filter((line) => !existing.includes(line)).join("\n");
|
|
2076
2032
|
if (!toAppend) return;
|
|
2077
|
-
|
|
2033
|
+
writeFileSync3(gitignorePath, `${existing.trimEnd()}
|
|
2078
2034
|
|
|
2079
2035
|
# Env files (secrets)
|
|
2080
2036
|
${toAppend}
|
|
@@ -2082,14 +2038,14 @@ ${toAppend}
|
|
|
2082
2038
|
}
|
|
2083
2039
|
|
|
2084
2040
|
// src/cli/commands/upload.ts
|
|
2085
|
-
import { resolve as
|
|
2041
|
+
import { resolve as resolve10 } from "path";
|
|
2086
2042
|
async function uploadModuleOrTheme(ctx) {
|
|
2087
2043
|
if (isThemeProject(ctx.config)) {
|
|
2088
2044
|
const theme = ctx.config.theme;
|
|
2089
2045
|
if (!theme) {
|
|
2090
2046
|
throw new WpHsError("theme block missing in woodscli.json", { exitCode: ExitCode.USER_ERROR });
|
|
2091
2047
|
}
|
|
2092
|
-
const srcDir =
|
|
2048
|
+
const srcDir = resolve10(ctx.projectRoot, theme.srcDir);
|
|
2093
2049
|
await runCommandAsync("hs", ["cms", "upload", srcDir, theme.uploadPath], {
|
|
2094
2050
|
cwd: ctx.projectRoot,
|
|
2095
2051
|
projectRoot: ctx.projectRoot,
|
|
@@ -2099,7 +2055,7 @@ async function uploadModuleOrTheme(ctx) {
|
|
|
2099
2055
|
});
|
|
2100
2056
|
return `Uploaded theme ${srcDir} \u2192 ${theme.uploadPath}`;
|
|
2101
2057
|
}
|
|
2102
|
-
const outDir =
|
|
2058
|
+
const outDir = resolve10(ctx.projectRoot, ctx.config.module.outDir);
|
|
2103
2059
|
const uploadPath = ctx.config.module.uploadPath;
|
|
2104
2060
|
await runCommandAsync("hs", ["cms", "upload", outDir, uploadPath], {
|
|
2105
2061
|
cwd: ctx.projectRoot,
|
|
@@ -2276,7 +2232,7 @@ async function runDeploy(ctx, options) {
|
|
|
2276
2232
|
if (!ctx.global.json) {
|
|
2277
2233
|
printCommandDone(
|
|
2278
2234
|
ctx,
|
|
2279
|
-
`Deploy complete (${
|
|
2235
|
+
`Deploy complete (${resolve11(ctx.projectRoot, ctx.config.module.outDir)} \u2192 ${ctx.config.module.uploadPath})`
|
|
2280
2236
|
);
|
|
2281
2237
|
}
|
|
2282
2238
|
return ExitCode.SUCCESS;
|
|
@@ -2349,8 +2305,8 @@ async function runPublish(ctx, options) {
|
|
|
2349
2305
|
|
|
2350
2306
|
// src/cli/commands/audit.ts
|
|
2351
2307
|
import { createHash } from "crypto";
|
|
2352
|
-
import { cpSync, existsSync as
|
|
2353
|
-
import { resolve as
|
|
2308
|
+
import { cpSync, existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync8, readdirSync, rmSync, statSync } from "fs";
|
|
2309
|
+
import { resolve as resolve12, join as join3 } from "path";
|
|
2354
2310
|
var FORBIDDEN_SHIP_TOKENS = [
|
|
2355
2311
|
"ModuleConfigOverlay",
|
|
2356
2312
|
"module-config-store",
|
|
@@ -2364,11 +2320,11 @@ function hashFile(path) {
|
|
|
2364
2320
|
return createHash("sha256").update(readFileSync8(path)).digest("hex");
|
|
2365
2321
|
}
|
|
2366
2322
|
function walkFiles(dir) {
|
|
2367
|
-
if (!
|
|
2323
|
+
if (!existsSync8(dir)) return [];
|
|
2368
2324
|
const entries = readdirSync(dir);
|
|
2369
2325
|
const files = [];
|
|
2370
2326
|
for (const entry of entries) {
|
|
2371
|
-
const full =
|
|
2327
|
+
const full = join3(dir, entry);
|
|
2372
2328
|
if (statSync(full).isDirectory()) {
|
|
2373
2329
|
files.push(...walkFiles(full));
|
|
2374
2330
|
} else {
|
|
@@ -2380,8 +2336,8 @@ function walkFiles(dir) {
|
|
|
2380
2336
|
async function runAuditShipBundle(ctx) {
|
|
2381
2337
|
const root = ctx.projectRoot;
|
|
2382
2338
|
const targets = [
|
|
2383
|
-
|
|
2384
|
-
|
|
2339
|
+
resolve12(root, "dist/cdn"),
|
|
2340
|
+
resolve12(root, ctx.config.module.outDir)
|
|
2385
2341
|
];
|
|
2386
2342
|
const violations = [];
|
|
2387
2343
|
for (const dir of targets) {
|
|
@@ -2427,23 +2383,23 @@ async function runAuditBaselineCapture(ctx, options) {
|
|
|
2427
2383
|
const root = ctx.projectRoot;
|
|
2428
2384
|
const baseline = options.baselineDir ?? defaultBaselineDir(root, ctx.config);
|
|
2429
2385
|
const sources = [
|
|
2430
|
-
{ label: "cdn", path:
|
|
2431
|
-
{ label: "hubspot", path:
|
|
2386
|
+
{ label: "cdn", path: resolve12(root, "dist/cdn") },
|
|
2387
|
+
{ label: "hubspot", path: resolve12(root, ctx.config.module.outDir) }
|
|
2432
2388
|
];
|
|
2433
2389
|
if (ctx.global.dryRun) {
|
|
2434
2390
|
ctx.logger.info(`[dry-run] Would capture baseline to ${baseline}`);
|
|
2435
2391
|
return ExitCode.SUCCESS;
|
|
2436
2392
|
}
|
|
2437
2393
|
for (const { label, path } of sources) {
|
|
2438
|
-
if (!
|
|
2394
|
+
if (!existsSync8(path)) {
|
|
2439
2395
|
throw new WpHsError(`Cannot capture baseline \u2014 missing ${path}`, {
|
|
2440
2396
|
exitCode: ExitCode.USER_ERROR,
|
|
2441
2397
|
remediation: "Run wp build before wp audit baseline --capture."
|
|
2442
2398
|
});
|
|
2443
2399
|
}
|
|
2444
|
-
const target =
|
|
2400
|
+
const target = resolve12(baseline, label);
|
|
2445
2401
|
rmSync(target, { recursive: true, force: true });
|
|
2446
|
-
|
|
2402
|
+
mkdirSync3(baseline, { recursive: true });
|
|
2447
2403
|
cpSync(path, target, { recursive: true });
|
|
2448
2404
|
}
|
|
2449
2405
|
if (ctx.global.json) {
|
|
@@ -2457,13 +2413,13 @@ async function runAuditParity(ctx, options) {
|
|
|
2457
2413
|
const root = ctx.projectRoot;
|
|
2458
2414
|
const baseline = options.baselineDir ?? defaultBaselineDir(root, ctx.config);
|
|
2459
2415
|
const currentDirs = [
|
|
2460
|
-
{ label: "cdn", path:
|
|
2461
|
-
{ label: "hubspot", path:
|
|
2416
|
+
{ label: "cdn", path: resolve12(root, "dist/cdn") },
|
|
2417
|
+
{ label: "hubspot", path: resolve12(root, ctx.config.module.outDir) }
|
|
2462
2418
|
];
|
|
2463
2419
|
const mismatches = [];
|
|
2464
2420
|
for (const { label, path } of currentDirs) {
|
|
2465
|
-
const baselinePath =
|
|
2466
|
-
if (!
|
|
2421
|
+
const baselinePath = resolve12(baseline, label);
|
|
2422
|
+
if (!existsSync8(baselinePath)) {
|
|
2467
2423
|
ctx.logger.warn(`No baseline at ${baselinePath} \u2014 skipping ${label} parity`);
|
|
2468
2424
|
continue;
|
|
2469
2425
|
}
|
|
@@ -2478,8 +2434,8 @@ async function runAuditParity(ctx, options) {
|
|
|
2478
2434
|
}
|
|
2479
2435
|
for (const file of baselineFiles) {
|
|
2480
2436
|
const rel = file.replace(baselinePath, "");
|
|
2481
|
-
const current =
|
|
2482
|
-
if (!
|
|
2437
|
+
const current = resolve12(path, rel.slice(1));
|
|
2438
|
+
if (!existsSync8(current)) continue;
|
|
2483
2439
|
const bh = hashFile(file);
|
|
2484
2440
|
const ch = hashFile(current);
|
|
2485
2441
|
if (bh !== ch) {
|
|
@@ -2506,7 +2462,7 @@ var SECRET_PATTERNS = [
|
|
|
2506
2462
|
];
|
|
2507
2463
|
async function runAuditSecrets(ctx) {
|
|
2508
2464
|
const root = ctx.projectRoot;
|
|
2509
|
-
const scanRoots = ["src", "public", ".wphs/config"].map((d) =>
|
|
2465
|
+
const scanRoots = ["src", "public", ".wphs/config"].map((d) => resolve12(root, d));
|
|
2510
2466
|
const findings = [];
|
|
2511
2467
|
for (const dir of scanRoots) {
|
|
2512
2468
|
for (const file of walkFiles(dir)) {
|
|
@@ -2538,13 +2494,13 @@ async function runAuditSecrets(ctx) {
|
|
|
2538
2494
|
}
|
|
2539
2495
|
|
|
2540
2496
|
// src/cli/commands/init.ts
|
|
2541
|
-
import { cpSync as
|
|
2542
|
-
import { basename as basename2, resolve as
|
|
2543
|
-
import * as
|
|
2497
|
+
import { cpSync as cpSync3, existsSync as existsSync13, mkdirSync as mkdirSync5, readdirSync as readdirSync2, readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "fs";
|
|
2498
|
+
import { basename as basename2, resolve as resolve17 } from "path";
|
|
2499
|
+
import * as p5 from "@clack/prompts";
|
|
2544
2500
|
|
|
2545
2501
|
// src/lib/init-router-scaffold.ts
|
|
2546
|
-
import { existsSync as
|
|
2547
|
-
import { resolve as
|
|
2502
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync9, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
2503
|
+
import { resolve as resolve13 } from "path";
|
|
2548
2504
|
var ROUTER_FILES = [
|
|
2549
2505
|
"src/routes",
|
|
2550
2506
|
"src/routeTree.gen.ts",
|
|
@@ -2558,10 +2514,10 @@ function isModuleTemplate(template) {
|
|
|
2558
2514
|
return template === "module-hybrid-cdn" || template === "module-monolith";
|
|
2559
2515
|
}
|
|
2560
2516
|
function writeRouterConfig(targetDir, mode) {
|
|
2561
|
-
const configDir =
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2517
|
+
const configDir = resolve13(targetDir, "src/config");
|
|
2518
|
+
mkdirSync4(configDir, { recursive: true });
|
|
2519
|
+
writeFileSync4(
|
|
2520
|
+
resolve13(configDir, "router.ts"),
|
|
2565
2521
|
`/** Router history \u2014 set by \`wp init\`. Hash matches woodsportal-client-frontend (HubSpot CMS embeds). */
|
|
2566
2522
|
export type RouterHistoryMode = 'hash' | 'browser'
|
|
2567
2523
|
|
|
@@ -2570,8 +2526,8 @@ export const ROUTER_HISTORY: RouterHistoryMode = '${mode}'
|
|
|
2570
2526
|
);
|
|
2571
2527
|
}
|
|
2572
2528
|
function writeMountDevPreviewRouter(targetDir) {
|
|
2573
|
-
|
|
2574
|
-
|
|
2529
|
+
writeFileSync4(
|
|
2530
|
+
resolve13(targetDir, "src/portal/mount-dev-preview.tsx"),
|
|
2575
2531
|
`import { mountGreenfieldDev } from '../../.wphs/.kit-bootstrap-greenfield.tsx'
|
|
2576
2532
|
|
|
2577
2533
|
import { App } from '@/app'
|
|
@@ -2584,8 +2540,8 @@ export async function mountDevPreview() {
|
|
|
2584
2540
|
);
|
|
2585
2541
|
}
|
|
2586
2542
|
function writeMountDevPreviewSinglePage(targetDir) {
|
|
2587
|
-
|
|
2588
|
-
|
|
2543
|
+
writeFileSync4(
|
|
2544
|
+
resolve13(targetDir, "src/portal/mount-dev-preview.tsx"),
|
|
2589
2545
|
`import { mountGreenfieldDev } from '../../.wphs/.kit-bootstrap-greenfield.tsx'
|
|
2590
2546
|
|
|
2591
2547
|
import { StarterSinglePage } from '@/features/starter'
|
|
@@ -2598,8 +2554,8 @@ export async function mountDevPreview() {
|
|
|
2598
2554
|
);
|
|
2599
2555
|
}
|
|
2600
2556
|
function writeMountPortalRouter(targetDir) {
|
|
2601
|
-
|
|
2602
|
-
|
|
2557
|
+
writeFileSync4(
|
|
2558
|
+
resolve13(targetDir, "src/portal/mount-portal.tsx"),
|
|
2603
2559
|
`import ReactDOM from 'react-dom/client'
|
|
2604
2560
|
|
|
2605
2561
|
import { App } from '@/app'
|
|
@@ -2616,8 +2572,8 @@ export async function mountPortal() {
|
|
|
2616
2572
|
);
|
|
2617
2573
|
}
|
|
2618
2574
|
function writeMountPortalSinglePage(targetDir) {
|
|
2619
|
-
|
|
2620
|
-
|
|
2575
|
+
writeFileSync4(
|
|
2576
|
+
resolve13(targetDir, "src/portal/mount-portal.tsx"),
|
|
2621
2577
|
`import ReactDOM from 'react-dom/client'
|
|
2622
2578
|
|
|
2623
2579
|
import { StarterSinglePage } from '@/features/starter'
|
|
@@ -2634,11 +2590,11 @@ export async function mountPortal() {
|
|
|
2634
2590
|
);
|
|
2635
2591
|
}
|
|
2636
2592
|
function writeStarterSinglePage(targetDir) {
|
|
2637
|
-
const file =
|
|
2638
|
-
if (
|
|
2593
|
+
const file = resolve13(targetDir, "src/features/starter/components/starter-single-page.tsx");
|
|
2594
|
+
if (existsSync9(file)) {
|
|
2639
2595
|
return;
|
|
2640
2596
|
}
|
|
2641
|
-
|
|
2597
|
+
writeFileSync4(
|
|
2642
2598
|
file,
|
|
2643
2599
|
`import { StarterShell } from '@/components/layout/starter-shell'
|
|
2644
2600
|
|
|
@@ -2661,15 +2617,15 @@ export function StarterSinglePage() {
|
|
|
2661
2617
|
);
|
|
2662
2618
|
}
|
|
2663
2619
|
function removePath(targetDir, relativePath) {
|
|
2664
|
-
const full =
|
|
2665
|
-
if (!
|
|
2620
|
+
const full = resolve13(targetDir, relativePath);
|
|
2621
|
+
if (!existsSync9(full)) {
|
|
2666
2622
|
return;
|
|
2667
2623
|
}
|
|
2668
2624
|
rmSync2(full, { recursive: true, force: true });
|
|
2669
2625
|
}
|
|
2670
2626
|
function stripTanStackFromPackage(targetDir) {
|
|
2671
|
-
const pkgPath =
|
|
2672
|
-
if (!
|
|
2627
|
+
const pkgPath = resolve13(targetDir, "package.json");
|
|
2628
|
+
if (!existsSync9(pkgPath)) {
|
|
2673
2629
|
return;
|
|
2674
2630
|
}
|
|
2675
2631
|
const pkg = JSON.parse(readFileSync9(pkgPath, "utf8"));
|
|
@@ -2679,7 +2635,7 @@ function stripTanStackFromPackage(targetDir) {
|
|
|
2679
2635
|
for (const dep of TANSTACK_DEPS) {
|
|
2680
2636
|
delete pkg.devDependencies[dep];
|
|
2681
2637
|
}
|
|
2682
|
-
|
|
2638
|
+
writeFileSync4(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
2683
2639
|
`);
|
|
2684
2640
|
}
|
|
2685
2641
|
function applyRouterScaffold(targetDir, mode, template) {
|
|
@@ -2699,126 +2655,510 @@ function applyRouterScaffold(targetDir, mode, template) {
|
|
|
2699
2655
|
writeRouterConfig(targetDir, mode);
|
|
2700
2656
|
writeMountDevPreviewRouter(targetDir);
|
|
2701
2657
|
writeMountPortalRouter(targetDir);
|
|
2702
|
-
const featuresIndex =
|
|
2703
|
-
if (
|
|
2658
|
+
const featuresIndex = resolve13(targetDir, "src/features/starter/index.ts");
|
|
2659
|
+
if (existsSync9(featuresIndex)) {
|
|
2704
2660
|
let content = readFileSync9(featuresIndex, "utf8");
|
|
2705
2661
|
if (!content.includes("StarterSinglePage")) {
|
|
2706
2662
|
content = content.replace(
|
|
2707
2663
|
"export type { StarterPreviewValues } from './hooks/use-starter-preview'\n",
|
|
2708
2664
|
"export type { StarterPreviewValues } from './hooks/use-starter-preview'\nexport { StarterSinglePage } from './components/starter-single-page'\n"
|
|
2709
2665
|
);
|
|
2710
|
-
|
|
2666
|
+
writeFileSync4(featuresIndex, content);
|
|
2711
2667
|
}
|
|
2712
2668
|
}
|
|
2713
2669
|
}
|
|
2714
2670
|
|
|
2715
|
-
// src/lib/
|
|
2716
|
-
import { existsSync as
|
|
2717
|
-
import { resolve as
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
var
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2671
|
+
// src/lib/init-auth-scaffold.ts
|
|
2672
|
+
import { cpSync as cpSync2, existsSync as existsSync10, readFileSync as readFileSync10, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
2673
|
+
import { resolve as resolve14 } from "path";
|
|
2674
|
+
|
|
2675
|
+
// src/lib/template-auth-deps.ts
|
|
2676
|
+
var AUTH_SCAFFOLD_DEPS = {
|
|
2677
|
+
"woodsportal-client-sdk": "^4.0.7-dev.11",
|
|
2678
|
+
"@tanstack/react-query": "^5.66.9",
|
|
2679
|
+
"@t3-oss/env-core": "^0.12.0",
|
|
2680
|
+
zod: "^3.24.2",
|
|
2681
|
+
sonner: "^2.0.1"
|
|
2682
|
+
};
|
|
2683
|
+
function mergeAuthDevDependencies(existing) {
|
|
2727
2684
|
return {
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
gitRemote: "https://github.com/your-org/your-portal-cdn-mirror.git",
|
|
2731
|
-
cdnPathInRepo: "dist/cdn",
|
|
2732
|
-
defaultBranch: "main"
|
|
2685
|
+
...existing,
|
|
2686
|
+
...AUTH_SCAFFOLD_DEPS
|
|
2733
2687
|
};
|
|
2734
2688
|
}
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
return null;
|
|
2742
|
-
}
|
|
2689
|
+
|
|
2690
|
+
// src/lib/init-auth-scaffold.ts
|
|
2691
|
+
var AUTH_SCAFFOLD_ROOT = kitPath("templates", "scaffold", "auth");
|
|
2692
|
+
function readTierManifest() {
|
|
2693
|
+
const path = resolve14(AUTH_SCAFFOLD_ROOT, "tier-manifest.json");
|
|
2694
|
+
return JSON.parse(readFileSync10(path, "utf8"));
|
|
2743
2695
|
}
|
|
2744
|
-
function
|
|
2745
|
-
const
|
|
2746
|
-
|
|
2747
|
-
if (parts.length !== 2) return null;
|
|
2748
|
-
return `${parts[0]}/${parts[1]}`;
|
|
2696
|
+
function readNavManifest() {
|
|
2697
|
+
const path = resolve14(AUTH_SCAFFOLD_ROOT, "nav-manifest.json");
|
|
2698
|
+
return JSON.parse(readFileSync10(path, "utf8"));
|
|
2749
2699
|
}
|
|
2750
|
-
function
|
|
2751
|
-
const
|
|
2752
|
-
|
|
2753
|
-
|
|
2700
|
+
function layersForMode(mode) {
|
|
2701
|
+
const manifest = readTierManifest();
|
|
2702
|
+
const entry = manifest.tiers[mode];
|
|
2703
|
+
if (!entry?.layers?.length) {
|
|
2704
|
+
throw new Error(`Unknown auth scaffold tier in tier-manifest.json: ${mode}`);
|
|
2754
2705
|
}
|
|
2755
|
-
return
|
|
2756
|
-
publishMode: base?.publishMode ?? "jsdelivr",
|
|
2757
|
-
github: slug,
|
|
2758
|
-
gitRemote: `https://github.com/${slug}.git`,
|
|
2759
|
-
cdnPathInRepo: base?.cdnPathInRepo ?? "dist/cdn",
|
|
2760
|
-
defaultBranch: base?.defaultBranch ?? "main"
|
|
2761
|
-
};
|
|
2762
|
-
}
|
|
2763
|
-
function hasCdnPublishToken(projectRoot) {
|
|
2764
|
-
if (process.env[PORTAL_CDN_PUBLISH_TOKEN_ENV]?.trim()) return true;
|
|
2765
|
-
if (process.env.GITHUB_TOKEN?.trim()) return true;
|
|
2766
|
-
return Boolean(readEnvLocal(projectRoot)[PORTAL_CDN_PUBLISH_TOKEN_ENV]?.trim());
|
|
2706
|
+
return entry.layers;
|
|
2767
2707
|
}
|
|
2768
|
-
function
|
|
2769
|
-
const
|
|
2770
|
-
|
|
2771
|
-
|
|
2708
|
+
function copyLayer(targetDir, layerName) {
|
|
2709
|
+
const layerRoot = resolve14(AUTH_SCAFFOLD_ROOT, layerName);
|
|
2710
|
+
if (!existsSync10(layerRoot)) {
|
|
2711
|
+
throw new Error(`Auth scaffold layer not found: ${layerName}`);
|
|
2712
|
+
}
|
|
2713
|
+
cpSync2(layerRoot, targetDir, { recursive: true, force: true });
|
|
2772
2714
|
}
|
|
2773
|
-
function
|
|
2774
|
-
const
|
|
2775
|
-
|
|
2776
|
-
|
|
2715
|
+
function layerForNavMode(nav) {
|
|
2716
|
+
const manifest = readNavManifest();
|
|
2717
|
+
const entry = manifest.modes[nav];
|
|
2718
|
+
if (!entry?.layer) {
|
|
2719
|
+
throw new Error(`Unknown nav scaffold mode in nav-manifest.json: ${nav}`);
|
|
2777
2720
|
}
|
|
2778
|
-
return
|
|
2721
|
+
return entry.layer;
|
|
2779
2722
|
}
|
|
2780
|
-
function
|
|
2781
|
-
|
|
2723
|
+
function copyNavLayer(targetDir, nav) {
|
|
2724
|
+
copyLayer(targetDir, layerForNavMode(nav));
|
|
2782
2725
|
}
|
|
2783
|
-
function
|
|
2784
|
-
const
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
`);
|
|
2788
|
-
if (input.token?.trim()) {
|
|
2789
|
-
writeEnvLocal(projectRoot, { [PORTAL_CDN_PUBLISH_TOKEN_ENV]: input.token.trim() });
|
|
2726
|
+
function patchNavScaffoldFile(targetDir, nav) {
|
|
2727
|
+
const navScaffoldPath = resolve14(targetDir, "src/config/nav-scaffold.ts");
|
|
2728
|
+
if (!existsSync10(navScaffoldPath)) {
|
|
2729
|
+
return;
|
|
2790
2730
|
}
|
|
2791
|
-
|
|
2731
|
+
const content = `/** Set by wp init --nav ${nav}. */
|
|
2732
|
+
export const NAV_SCAFFOLD_MODE = '${nav}' as const
|
|
2733
|
+
|
|
2734
|
+
export type NavScaffoldMode = typeof NAV_SCAFFOLD_MODE
|
|
2735
|
+
`;
|
|
2736
|
+
writeFileSync5(navScaffoldPath, content);
|
|
2792
2737
|
}
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
return null;
|
|
2738
|
+
function patchAuthTierFile(targetDir, mode) {
|
|
2739
|
+
const authTierPath = resolve14(targetDir, "src/config/auth-tier.ts");
|
|
2740
|
+
if (!existsSync10(authTierPath)) {
|
|
2741
|
+
return;
|
|
2798
2742
|
}
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2743
|
+
const content = `/** Set by wp init --auth ${mode}. */
|
|
2744
|
+
export const AUTH_SCAFFOLD_TIER = '${mode}' as const
|
|
2745
|
+
|
|
2746
|
+
export type AuthScaffoldTier = typeof AUTH_SCAFFOLD_TIER
|
|
2747
|
+
`;
|
|
2748
|
+
writeFileSync5(authTierPath, content);
|
|
2749
|
+
}
|
|
2750
|
+
function patchStarterOnlyBlocks(targetDir, mode) {
|
|
2751
|
+
const hasSso = mode === "sso" || mode === "full";
|
|
2752
|
+
const hasFull = mode === "full";
|
|
2753
|
+
const filesToPatch = [
|
|
2754
|
+
"src/integrations/configure-woodsportal-sdk.ts",
|
|
2755
|
+
"src/features/auth/login-session-bridge.ts",
|
|
2756
|
+
"src/app.tsx",
|
|
2757
|
+
"src/features/auth/components/starter-login.tsx",
|
|
2758
|
+
"src/portal/mount-portal.tsx",
|
|
2759
|
+
"src/features/auth/auth-route-paths.ts"
|
|
2760
|
+
];
|
|
2761
|
+
for (const relative of filesToPatch) {
|
|
2762
|
+
const filePath = resolve14(targetDir, relative);
|
|
2763
|
+
if (!existsSync10(filePath)) {
|
|
2764
|
+
continue;
|
|
2765
|
+
}
|
|
2766
|
+
let content = readFileSync10(filePath, "utf8");
|
|
2767
|
+
content = stripMarkedBlock(content, "WPHS_AUTH_SSO", hasSso);
|
|
2768
|
+
content = stripMarkedBlock(content, "WPHS_AUTH_FULL", hasFull);
|
|
2769
|
+
if (!hasFull && relative === "src/integrations/configure-woodsportal-sdk.ts") {
|
|
2770
|
+
content = content.replace(
|
|
2771
|
+
/^import \{ isMfaPendingSession \} from '@\/features\/auth\/auth-session'\n/m,
|
|
2772
|
+
""
|
|
2773
|
+
);
|
|
2774
|
+
}
|
|
2775
|
+
writeFileSync5(filePath, content);
|
|
2802
2776
|
}
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2777
|
+
}
|
|
2778
|
+
function stripMarkedBlock(content, marker, keep) {
|
|
2779
|
+
const start = `// ${marker}`;
|
|
2780
|
+
const end = `// /${marker}`;
|
|
2781
|
+
const pattern = new RegExp(
|
|
2782
|
+
`${escapeRegExp(start)}[\\s\\S]*?${escapeRegExp(end)}\\n?`,
|
|
2783
|
+
"g"
|
|
2784
|
+
);
|
|
2785
|
+
if (keep) {
|
|
2786
|
+
return content.replace(new RegExp(`${escapeRegExp(start)}\\n?`, "g"), "").replace(new RegExp(`${escapeRegExp(end)}\\n?`, "g"), "");
|
|
2787
|
+
}
|
|
2788
|
+
return content.replace(pattern, "");
|
|
2789
|
+
}
|
|
2790
|
+
function escapeRegExp(value) {
|
|
2791
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2792
|
+
}
|
|
2793
|
+
var LOWER_TIER_AUTH_GUARD_FILES = [
|
|
2794
|
+
"src/features/auth/login-session-bridge.ts",
|
|
2795
|
+
"src/integrations/configure-woodsportal-sdk.ts",
|
|
2796
|
+
"src/app.tsx",
|
|
2797
|
+
"src/portal/mount-portal.tsx",
|
|
2798
|
+
"src/features/auth/components/starter-login.tsx"
|
|
2799
|
+
];
|
|
2800
|
+
function assertLowerTierAuthScaffold(targetDir, mode) {
|
|
2801
|
+
const violations = [];
|
|
2802
|
+
for (const relative of LOWER_TIER_AUTH_GUARD_FILES) {
|
|
2803
|
+
const filePath = resolve14(targetDir, relative);
|
|
2804
|
+
if (!existsSync10(filePath)) {
|
|
2805
|
+
continue;
|
|
2806
|
+
}
|
|
2807
|
+
const content = readFileSync10(filePath, "utf8");
|
|
2808
|
+
if (content.includes("WPHS_AUTH_")) {
|
|
2809
|
+
violations.push(`${relative}: leftover WPHS_AUTH_* strip markers`);
|
|
2810
|
+
}
|
|
2811
|
+
if (relative === "src/features/auth/login-session-bridge.ts") {
|
|
2812
|
+
if (content.includes("mfa-pending-storage")) {
|
|
2813
|
+
violations.push(`${relative}: must not import mfa-pending-storage on --auth ${mode}`);
|
|
2814
|
+
}
|
|
2815
|
+
if (content.includes("navigateToMfaGate")) {
|
|
2816
|
+
violations.push(`${relative}: must not reference navigateToMfaGate on --auth ${mode}`);
|
|
2811
2817
|
}
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
if (existsSync10(resolve14(targetDir, "src/routes/_auth/two-fa.tsx"))) {
|
|
2821
|
+
violations.push("src/routes/_auth/two-fa.tsx: MFA route must not exist on --auth " + mode);
|
|
2822
|
+
}
|
|
2823
|
+
if (violations.length > 0) {
|
|
2824
|
+
throw new WpHsError(`Auth scaffold tier "${mode}" still contains full-tier artifacts:
|
|
2825
|
+
- ${violations.join("\n- ")}`, {
|
|
2826
|
+
exitCode: ExitCode.INTERNAL,
|
|
2827
|
+
remediation: "Fix strip markers in templates/scaffold/auth/_shared or report a hubspot-kit bug."
|
|
2812
2828
|
});
|
|
2813
|
-
if (p3.isCancel(answer)) return null;
|
|
2814
|
-
github = normalizeGithubSlug(answer) ?? void 0;
|
|
2815
2829
|
}
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2830
|
+
}
|
|
2831
|
+
function assertFullTierAuthScaffold(targetDir) {
|
|
2832
|
+
const required = [
|
|
2833
|
+
"src/features/auth/mfa-pending-storage.ts",
|
|
2834
|
+
"src/routes/_auth/two-fa.tsx"
|
|
2835
|
+
];
|
|
2836
|
+
const missing = required.filter((relative) => !existsSync10(resolve14(targetDir, relative)));
|
|
2837
|
+
if (missing.length > 0) {
|
|
2838
|
+
throw new WpHsError(`Auth scaffold tier "full" is missing required files:
|
|
2839
|
+
- ${missing.join("\n- ")}`, {
|
|
2840
|
+
exitCode: ExitCode.INTERNAL
|
|
2841
|
+
});
|
|
2819
2842
|
}
|
|
2820
|
-
const
|
|
2821
|
-
|
|
2843
|
+
const bridge = readFileSync10(resolve14(targetDir, "src/features/auth/login-session-bridge.ts"), "utf8");
|
|
2844
|
+
if (bridge.includes("WPHS_AUTH_")) {
|
|
2845
|
+
throw new WpHsError('Auth scaffold tier "full" left WPHS_AUTH_* markers in login-session-bridge.ts', {
|
|
2846
|
+
exitCode: ExitCode.INTERNAL
|
|
2847
|
+
});
|
|
2848
|
+
}
|
|
2849
|
+
if (!bridge.includes("navigateToMfaGate") || !bridge.includes("mfa-pending-storage")) {
|
|
2850
|
+
throw new WpHsError('Auth scaffold tier "full" login-session-bridge.ts is missing MFA wiring', {
|
|
2851
|
+
exitCode: ExitCode.INTERNAL
|
|
2852
|
+
});
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
function patchPackageJson(targetDir) {
|
|
2856
|
+
const pkgPath = resolve14(targetDir, "package.json");
|
|
2857
|
+
if (!existsSync10(pkgPath)) {
|
|
2858
|
+
return;
|
|
2859
|
+
}
|
|
2860
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf8"));
|
|
2861
|
+
pkg.devDependencies = mergeAuthDevDependencies(pkg.devDependencies);
|
|
2862
|
+
writeFileSync5(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
2863
|
+
`);
|
|
2864
|
+
}
|
|
2865
|
+
function writeAuthMountDevPreview(targetDir) {
|
|
2866
|
+
writeFileSync5(
|
|
2867
|
+
resolve14(targetDir, "src/portal/mount-dev-preview.tsx"),
|
|
2868
|
+
`import { mountGreenfieldDev } from '../../.wphs/.kit-bootstrap-greenfield.tsx'
|
|
2869
|
+
|
|
2870
|
+
import { App } from '@/app'
|
|
2871
|
+
import { getPortalRouter, mountPortal } from '@/portal/mount-portal'
|
|
2872
|
+
|
|
2873
|
+
/** Local dev: module-config overlay + auth-aware App shell. */
|
|
2874
|
+
export async function mountDevPreview() {
|
|
2875
|
+
await mountGreenfieldDev({
|
|
2876
|
+
beforePreview: () => mountPortal({ skipAppRender: true }),
|
|
2877
|
+
preview: <App router={getPortalRouter()} />,
|
|
2878
|
+
})
|
|
2879
|
+
}
|
|
2880
|
+
`
|
|
2881
|
+
);
|
|
2882
|
+
}
|
|
2883
|
+
function validateAuthRouterCombo(auth, router) {
|
|
2884
|
+
if (auth === "none") {
|
|
2885
|
+
return;
|
|
2886
|
+
}
|
|
2887
|
+
if (router === "none") {
|
|
2888
|
+
throw new Error(
|
|
2889
|
+
"Portal auth scaffold requires client-side routing. Use --router hash or --router browser."
|
|
2890
|
+
);
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2893
|
+
function validateNavAuthCombo(auth, nav) {
|
|
2894
|
+
if (!nav || nav === "top") {
|
|
2895
|
+
return;
|
|
2896
|
+
}
|
|
2897
|
+
if (auth === "none") {
|
|
2898
|
+
throw new Error(
|
|
2899
|
+
"Portal navigation shell requires auth scaffold. Use --auth starter|sso|full or omit --nav."
|
|
2900
|
+
);
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
function assertNavScaffold(targetDir, nav) {
|
|
2904
|
+
const mainLayoutPath = resolve14(targetDir, "src/components/Layouts/MainLayout.tsx");
|
|
2905
|
+
if (!existsSync10(mainLayoutPath)) {
|
|
2906
|
+
throw new WpHsError("Auth scaffold is missing MainLayout.tsx after nav layer copy", {
|
|
2907
|
+
exitCode: ExitCode.INTERNAL
|
|
2908
|
+
});
|
|
2909
|
+
}
|
|
2910
|
+
const sidebarPath = resolve14(targetDir, "src/components/layout/portal-sidebar.tsx");
|
|
2911
|
+
if (nav === "sidebar") {
|
|
2912
|
+
if (!existsSync10(sidebarPath)) {
|
|
2913
|
+
throw new WpHsError('Nav scaffold mode "sidebar" is missing portal-sidebar.tsx', {
|
|
2914
|
+
exitCode: ExitCode.INTERNAL
|
|
2915
|
+
});
|
|
2916
|
+
}
|
|
2917
|
+
} else if (existsSync10(sidebarPath)) {
|
|
2918
|
+
throw new WpHsError('Nav scaffold mode "top" must not include portal-sidebar.tsx', {
|
|
2919
|
+
exitCode: ExitCode.INTERNAL
|
|
2920
|
+
});
|
|
2921
|
+
}
|
|
2922
|
+
const navScaffold = readFileSync10(resolve14(targetDir, "src/config/nav-scaffold.ts"), "utf8");
|
|
2923
|
+
if (!navScaffold.includes(`'${nav}'`)) {
|
|
2924
|
+
throw new WpHsError(`nav-scaffold.ts was not patched for mode "${nav}"`, {
|
|
2925
|
+
exitCode: ExitCode.INTERNAL
|
|
2926
|
+
});
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
function applyAuthScaffold(targetDir, mode, routerMode, template, nav = "top") {
|
|
2930
|
+
if (mode === "none" || !isModuleTemplate(template)) {
|
|
2931
|
+
return;
|
|
2932
|
+
}
|
|
2933
|
+
validateAuthRouterCombo(mode, routerMode);
|
|
2934
|
+
validateNavAuthCombo(mode, nav);
|
|
2935
|
+
for (const layer of layersForMode(mode)) {
|
|
2936
|
+
copyLayer(targetDir, layer);
|
|
2937
|
+
}
|
|
2938
|
+
patchAuthTierFile(targetDir, mode);
|
|
2939
|
+
patchStarterOnlyBlocks(targetDir, mode);
|
|
2940
|
+
patchNavScaffoldFile(targetDir, nav);
|
|
2941
|
+
copyNavLayer(targetDir, nav);
|
|
2942
|
+
assertNavScaffold(targetDir, nav);
|
|
2943
|
+
if (mode === "starter" || mode === "sso") {
|
|
2944
|
+
assertLowerTierAuthScaffold(targetDir, mode);
|
|
2945
|
+
} else if (mode === "full") {
|
|
2946
|
+
assertFullTierAuthScaffold(targetDir);
|
|
2947
|
+
}
|
|
2948
|
+
patchPackageJson(targetDir);
|
|
2949
|
+
if (routerMode && routerMode !== "none") {
|
|
2950
|
+
writeAuthMountDevPreview(targetDir);
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
var AUTH_TIER_ORDER = ["none", "starter", "sso", "full"];
|
|
2954
|
+
function authTierRank(tier) {
|
|
2955
|
+
return AUTH_TIER_ORDER.indexOf(tier);
|
|
2956
|
+
}
|
|
2957
|
+
function readCurrentAuthTier(targetDir) {
|
|
2958
|
+
const authTierPath = resolve14(targetDir, "src/config/auth-tier.ts");
|
|
2959
|
+
if (!existsSync10(authTierPath)) {
|
|
2960
|
+
return "none";
|
|
2961
|
+
}
|
|
2962
|
+
const content = readFileSync10(authTierPath, "utf8");
|
|
2963
|
+
const match = content.match(/AUTH_SCAFFOLD_TIER = '(starter|sso|full)'/);
|
|
2964
|
+
return match?.[1] ?? "none";
|
|
2965
|
+
}
|
|
2966
|
+
function applyAuthTierUpgrade(targetDir, targetTier, routerMode, template, nav = "top") {
|
|
2967
|
+
if (!isModuleTemplate(template)) {
|
|
2968
|
+
throw new WpHsError("Auth upgrade is only supported for module projects.", {
|
|
2969
|
+
exitCode: ExitCode.USER_ERROR
|
|
2970
|
+
});
|
|
2971
|
+
}
|
|
2972
|
+
validateAuthRouterCombo(targetTier, routerMode);
|
|
2973
|
+
const currentTier = readCurrentAuthTier(targetDir);
|
|
2974
|
+
if (currentTier === "none") {
|
|
2975
|
+
applyAuthScaffold(targetDir, targetTier, routerMode, template, nav);
|
|
2976
|
+
return targetTier;
|
|
2977
|
+
}
|
|
2978
|
+
if (authTierRank(targetTier) <= authTierRank(currentTier)) {
|
|
2979
|
+
throw new WpHsError(
|
|
2980
|
+
`Auth upgrade requires a higher tier than "${currentTier}" (requested "${targetTier}").`,
|
|
2981
|
+
{
|
|
2982
|
+
exitCode: ExitCode.USER_ERROR,
|
|
2983
|
+
remediation: "Use starter \u2192 sso \u2192 full. Re-init for downgrades."
|
|
2984
|
+
}
|
|
2985
|
+
);
|
|
2986
|
+
}
|
|
2987
|
+
const targetLayers = layersForMode(targetTier);
|
|
2988
|
+
const currentLayers = layersForMode(currentTier);
|
|
2989
|
+
const layersToCopy = targetLayers.filter((layer) => !currentLayers.includes(layer));
|
|
2990
|
+
for (const layer of layersToCopy) {
|
|
2991
|
+
copyLayer(targetDir, layer);
|
|
2992
|
+
}
|
|
2993
|
+
patchAuthTierFile(targetDir, targetTier);
|
|
2994
|
+
patchStarterOnlyBlocks(targetDir, targetTier);
|
|
2995
|
+
if (targetTier === "starter" || targetTier === "sso") {
|
|
2996
|
+
assertLowerTierAuthScaffold(targetDir, targetTier);
|
|
2997
|
+
} else {
|
|
2998
|
+
assertFullTierAuthScaffold(targetDir);
|
|
2999
|
+
}
|
|
3000
|
+
patchPackageJson(targetDir);
|
|
3001
|
+
if (routerMode && routerMode !== "none") {
|
|
3002
|
+
writeAuthMountDevPreview(targetDir);
|
|
3003
|
+
}
|
|
3004
|
+
return targetTier;
|
|
3005
|
+
}
|
|
3006
|
+
var SIDEBAR_NAV_FILES = [
|
|
3007
|
+
"src/components/layout/portal-sidebar.tsx",
|
|
3008
|
+
"src/components/layout/portal-sidebar-nav.tsx"
|
|
3009
|
+
];
|
|
3010
|
+
function applyNavScaffoldUpgrade(targetDir, nav) {
|
|
3011
|
+
const authTierPath = resolve14(targetDir, "src/config/auth-tier.ts");
|
|
3012
|
+
if (!existsSync10(authTierPath)) {
|
|
3013
|
+
throw new WpHsError("Nav scaffold requires portal auth (missing src/config/auth-tier.ts).", {
|
|
3014
|
+
exitCode: ExitCode.USER_ERROR,
|
|
3015
|
+
remediation: "Run wp scaffold auth upgrade --tier starter first."
|
|
3016
|
+
});
|
|
3017
|
+
}
|
|
3018
|
+
if (nav === "top") {
|
|
3019
|
+
for (const relative of SIDEBAR_NAV_FILES) {
|
|
3020
|
+
const filePath = resolve14(targetDir, relative);
|
|
3021
|
+
if (existsSync10(filePath)) {
|
|
3022
|
+
rmSync3(filePath);
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
3025
|
+
}
|
|
3026
|
+
patchNavScaffoldFile(targetDir, nav);
|
|
3027
|
+
copyNavLayer(targetDir, nav);
|
|
3028
|
+
assertNavScaffold(targetDir, nav);
|
|
3029
|
+
}
|
|
3030
|
+
function readProjectAuthTier(targetDir) {
|
|
3031
|
+
return readCurrentAuthTier(targetDir);
|
|
3032
|
+
}
|
|
3033
|
+
|
|
3034
|
+
// src/lib/init-wizard.ts
|
|
3035
|
+
import * as p4 from "@clack/prompts";
|
|
3036
|
+
|
|
3037
|
+
// src/lib/project-name.ts
|
|
3038
|
+
var PROJECT_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
3039
|
+
function normalizeProjectName(input) {
|
|
3040
|
+
return input.trim().toLowerCase().replace(/[\s_]+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
3041
|
+
}
|
|
3042
|
+
function validateProjectName(name) {
|
|
3043
|
+
if (!name) return "Project name is required";
|
|
3044
|
+
if (!PROJECT_NAME_PATTERN.test(name)) {
|
|
3045
|
+
return "Use lowercase letters, numbers, and hyphens (e.g. my-portal-module)";
|
|
3046
|
+
}
|
|
3047
|
+
if (name.length > 64) return "Project name is too long (max 64 characters)";
|
|
3048
|
+
return void 0;
|
|
3049
|
+
}
|
|
3050
|
+
function moduleOutDirFromSlug(slug) {
|
|
3051
|
+
const pascal = slug.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
3052
|
+
return `dist/${pascal || "HubSpot"}.Module`;
|
|
3053
|
+
}
|
|
3054
|
+
|
|
3055
|
+
// src/lib/cdn-github-setup.ts
|
|
3056
|
+
import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "fs";
|
|
3057
|
+
import { resolve as resolve15 } from "path";
|
|
3058
|
+
import * as p3 from "@clack/prompts";
|
|
3059
|
+
var PORTAL_CDN_PUBLISH_TOKEN_ENV = "PORTAL_CDN_PUBLISH_TOKEN";
|
|
3060
|
+
var PORTAL_CDN_JSON = "portal.cdn.json";
|
|
3061
|
+
var PLACEHOLDER_GITHUB = /your-org|your-portal-cdn-mirror/i;
|
|
3062
|
+
function defaultPortalCdnJson() {
|
|
3063
|
+
const examplePath = kitPath("templates", "shared", "portal.cdn.json.example");
|
|
3064
|
+
if (existsSync11(examplePath)) {
|
|
3065
|
+
return JSON.parse(readFileSync11(examplePath, "utf8"));
|
|
3066
|
+
}
|
|
3067
|
+
return {
|
|
3068
|
+
publishMode: "jsdelivr",
|
|
3069
|
+
github: "your-org/your-portal-cdn-mirror",
|
|
3070
|
+
gitRemote: "https://github.com/your-org/your-portal-cdn-mirror.git",
|
|
3071
|
+
cdnPathInRepo: "dist/cdn",
|
|
3072
|
+
defaultBranch: "main"
|
|
3073
|
+
};
|
|
3074
|
+
}
|
|
3075
|
+
function readPortalCdnJson(projectRoot) {
|
|
3076
|
+
const path = resolve15(projectRoot, PORTAL_CDN_JSON);
|
|
3077
|
+
if (!existsSync11(path)) return null;
|
|
3078
|
+
try {
|
|
3079
|
+
return JSON.parse(readFileSync11(path, "utf8"));
|
|
3080
|
+
} catch {
|
|
3081
|
+
return null;
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
function normalizeGithubSlug(input) {
|
|
3085
|
+
const trimmed = input.trim().replace(/^https:\/\/github\.com\//, "").replace(/\.git$/, "");
|
|
3086
|
+
const parts = trimmed.split("/").filter(Boolean);
|
|
3087
|
+
if (parts.length !== 2) return null;
|
|
3088
|
+
return `${parts[0]}/${parts[1]}`;
|
|
3089
|
+
}
|
|
3090
|
+
function portalCdnJsonFromGithub(github, base) {
|
|
3091
|
+
const slug = normalizeGithubSlug(github);
|
|
3092
|
+
if (!slug) {
|
|
3093
|
+
throw new Error(`Invalid GitHub repo \u2014 use org/repo (got "${github}")`);
|
|
3094
|
+
}
|
|
3095
|
+
return {
|
|
3096
|
+
publishMode: base?.publishMode ?? "jsdelivr",
|
|
3097
|
+
github: slug,
|
|
3098
|
+
gitRemote: `https://github.com/${slug}.git`,
|
|
3099
|
+
cdnPathInRepo: base?.cdnPathInRepo ?? "dist/cdn",
|
|
3100
|
+
defaultBranch: base?.defaultBranch ?? "main"
|
|
3101
|
+
};
|
|
3102
|
+
}
|
|
3103
|
+
function hasCdnPublishToken(projectRoot) {
|
|
3104
|
+
if (process.env[PORTAL_CDN_PUBLISH_TOKEN_ENV]?.trim()) return true;
|
|
3105
|
+
if (process.env.GITHUB_TOKEN?.trim()) return true;
|
|
3106
|
+
return Boolean(readEnvLocal(projectRoot)[PORTAL_CDN_PUBLISH_TOKEN_ENV]?.trim());
|
|
3107
|
+
}
|
|
3108
|
+
function hasConfiguredCdnGithubMirror(projectRoot) {
|
|
3109
|
+
const config = readPortalCdnJson(projectRoot);
|
|
3110
|
+
const github = config?.github ?? "";
|
|
3111
|
+
return Boolean(github) && !PLACEHOLDER_GITHUB.test(github);
|
|
3112
|
+
}
|
|
3113
|
+
function githubDefaultForPrompt(projectRoot) {
|
|
3114
|
+
const existing = readPortalCdnJson(projectRoot);
|
|
3115
|
+
if (existing?.github && !PLACEHOLDER_GITHUB.test(existing.github)) {
|
|
3116
|
+
return existing.github;
|
|
3117
|
+
}
|
|
3118
|
+
return defaultPortalCdnJson().github;
|
|
3119
|
+
}
|
|
3120
|
+
function needsCdnGitHubSetup(projectRoot) {
|
|
3121
|
+
return !hasConfiguredCdnGithubMirror(projectRoot) || !hasCdnPublishToken(projectRoot);
|
|
3122
|
+
}
|
|
3123
|
+
function applyCdnGitHubSetup(projectRoot, input) {
|
|
3124
|
+
const existing = readPortalCdnJson(projectRoot) ?? defaultPortalCdnJson();
|
|
3125
|
+
const updated = portalCdnJsonFromGithub(input.github, existing);
|
|
3126
|
+
writeFileSync6(resolve15(projectRoot, PORTAL_CDN_JSON), `${JSON.stringify(updated, null, 2)}
|
|
3127
|
+
`);
|
|
3128
|
+
if (input.token?.trim()) {
|
|
3129
|
+
writeEnvLocal(projectRoot, { [PORTAL_CDN_PUBLISH_TOKEN_ENV]: input.token.trim() });
|
|
3130
|
+
}
|
|
3131
|
+
ensureEnvFilesGitignore(projectRoot);
|
|
3132
|
+
}
|
|
3133
|
+
async function promptCdnGitHubSetup(options = {}) {
|
|
3134
|
+
if (options.skip) return null;
|
|
3135
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
3136
|
+
if (!needsCdnGitHubSetup(projectRoot)) {
|
|
3137
|
+
return null;
|
|
3138
|
+
}
|
|
3139
|
+
let github = options.github?.trim();
|
|
3140
|
+
if (!github && hasConfiguredCdnGithubMirror(projectRoot)) {
|
|
3141
|
+
github = readPortalCdnJson(projectRoot)?.github;
|
|
3142
|
+
}
|
|
3143
|
+
if (!github) {
|
|
3144
|
+
const answer = await p3.text({
|
|
3145
|
+
message: "Public GitHub CDN mirror (org/repo)",
|
|
3146
|
+
placeholder: options.githubDefault ?? "your-org/your-portal-cdn-mirror",
|
|
3147
|
+
initialValue: options.githubDefault,
|
|
3148
|
+
validate: (value) => {
|
|
3149
|
+
const slug = normalizeGithubSlug(value ?? "");
|
|
3150
|
+
return slug ? void 0 : "Enter a public repo as org/repo (jsDelivr serves from it)";
|
|
3151
|
+
}
|
|
3152
|
+
});
|
|
3153
|
+
if (p3.isCancel(answer)) return null;
|
|
3154
|
+
github = normalizeGithubSlug(answer) ?? void 0;
|
|
3155
|
+
}
|
|
3156
|
+
if (!github) return null;
|
|
3157
|
+
if (hasCdnPublishToken(projectRoot)) {
|
|
3158
|
+
return { github };
|
|
3159
|
+
}
|
|
3160
|
+
const tokenAnswer = await p3.password({
|
|
3161
|
+
message: "GitHub token (repo push on CDN mirror)",
|
|
2822
3162
|
validate: (value) => {
|
|
2823
3163
|
if (!value?.trim()) return "Required for wp publish \u2014 create a fine-grained PAT with Contents: Read and write";
|
|
2824
3164
|
return void 0;
|
|
@@ -2831,9 +3171,185 @@ function formatCdnSetupNote(_projectRoot) {
|
|
|
2831
3171
|
return `CDN config written \u2014 edit ${PORTAL_CDN_JSON} if needed; token saved to ${ENV_LOCAL_FILE} (gitignored)`;
|
|
2832
3172
|
}
|
|
2833
3173
|
|
|
3174
|
+
// src/lib/init-wizard.ts
|
|
3175
|
+
function cancelIfNeeded(value) {
|
|
3176
|
+
if (p4.isCancel(value)) {
|
|
3177
|
+
throw new WpHsError("Init cancelled", { exitCode: ExitCode.USER_ERROR });
|
|
3178
|
+
}
|
|
3179
|
+
return value;
|
|
3180
|
+
}
|
|
3181
|
+
async function runInitWizard(options) {
|
|
3182
|
+
let template = options.template;
|
|
3183
|
+
if (!template) {
|
|
3184
|
+
const type = cancelIfNeeded(
|
|
3185
|
+
await p4.select({
|
|
3186
|
+
message: "Project type",
|
|
3187
|
+
options: [
|
|
3188
|
+
{
|
|
3189
|
+
value: "module-hybrid-cdn",
|
|
3190
|
+
label: "Module (hybrid CDN)",
|
|
3191
|
+
hint: "jsDelivr + thin module.js"
|
|
3192
|
+
},
|
|
3193
|
+
{
|
|
3194
|
+
value: "module-monolith",
|
|
3195
|
+
label: "Module (monolith)",
|
|
3196
|
+
hint: "Single module.js IIFE"
|
|
3197
|
+
},
|
|
3198
|
+
{
|
|
3199
|
+
value: "theme-starter",
|
|
3200
|
+
label: "Theme starter",
|
|
3201
|
+
hint: "CMS theme templates"
|
|
3202
|
+
}
|
|
3203
|
+
]
|
|
3204
|
+
})
|
|
3205
|
+
);
|
|
3206
|
+
template = type;
|
|
3207
|
+
}
|
|
3208
|
+
let projectName = options.name ? normalizeProjectName(options.name) : void 0;
|
|
3209
|
+
if (!options.directory && !projectName) {
|
|
3210
|
+
const nameAnswer = cancelIfNeeded(
|
|
3211
|
+
await p4.text({
|
|
3212
|
+
message: "Project name (folder name)",
|
|
3213
|
+
placeholder: "my-portal-module",
|
|
3214
|
+
validate: (value) => validateProjectName(normalizeProjectName(value ?? ""))
|
|
3215
|
+
})
|
|
3216
|
+
);
|
|
3217
|
+
projectName = normalizeProjectName(nameAnswer);
|
|
3218
|
+
}
|
|
3219
|
+
if (!projectName && options.directory) {
|
|
3220
|
+
projectName = normalizeProjectName(options.directory.split("/").pop() ?? "my-portal");
|
|
3221
|
+
}
|
|
3222
|
+
if (!projectName) {
|
|
3223
|
+
throw new WpHsError("Project name is required", {
|
|
3224
|
+
exitCode: ExitCode.USER_ERROR,
|
|
3225
|
+
remediation: "Pass --name or --directory."
|
|
3226
|
+
});
|
|
3227
|
+
}
|
|
3228
|
+
let router = options.router;
|
|
3229
|
+
if (!router && isModuleTemplate(template)) {
|
|
3230
|
+
const routerAnswer = cancelIfNeeded(
|
|
3231
|
+
await p4.select({
|
|
3232
|
+
message: "Client-side routing",
|
|
3233
|
+
initialValue: "hash",
|
|
3234
|
+
options: [
|
|
3235
|
+
{
|
|
3236
|
+
value: "hash",
|
|
3237
|
+
label: "Hash history (recommended)",
|
|
3238
|
+
hint: "Works in HubSpot CMS embeds"
|
|
3239
|
+
},
|
|
3240
|
+
{
|
|
3241
|
+
value: "browser",
|
|
3242
|
+
label: "Browser history",
|
|
3243
|
+
hint: "Clean URLs for local dev only"
|
|
3244
|
+
},
|
|
3245
|
+
{
|
|
3246
|
+
value: "none",
|
|
3247
|
+
label: "No router",
|
|
3248
|
+
hint: "Single-page starter"
|
|
3249
|
+
}
|
|
3250
|
+
]
|
|
3251
|
+
})
|
|
3252
|
+
);
|
|
3253
|
+
router = routerAnswer;
|
|
3254
|
+
}
|
|
3255
|
+
let auth = options.auth ?? "none";
|
|
3256
|
+
if (!options.auth && isModuleTemplate(template) && router && router !== "none") {
|
|
3257
|
+
const authAnswer = cancelIfNeeded(
|
|
3258
|
+
await p4.select({
|
|
3259
|
+
message: "Portal auth (woodsportal-api + SDK)",
|
|
3260
|
+
initialValue: "none",
|
|
3261
|
+
options: [
|
|
3262
|
+
{ value: "none", label: "No portal login", hint: "Routes only" },
|
|
3263
|
+
{ value: "starter", label: "Email + password", hint: "Login + sample protected page" },
|
|
3264
|
+
{ value: "sso", label: "Email + password + SSO", hint: "OAuth callback route" },
|
|
3265
|
+
{
|
|
3266
|
+
value: "full",
|
|
3267
|
+
label: "Email + password + SSO + MFA",
|
|
3268
|
+
hint: "Recommended for production portals"
|
|
3269
|
+
}
|
|
3270
|
+
]
|
|
3271
|
+
})
|
|
3272
|
+
);
|
|
3273
|
+
auth = authAnswer;
|
|
3274
|
+
} else if (router === "none" && isModuleTemplate(template)) {
|
|
3275
|
+
p4.log.info("Portal auth requires routing \u2014 auth tier set to none.");
|
|
3276
|
+
auth = "none";
|
|
3277
|
+
}
|
|
3278
|
+
let nav = options.nav ?? "top";
|
|
3279
|
+
if (!options.nav && auth !== "none" && isModuleTemplate(template)) {
|
|
3280
|
+
const navAnswer = cancelIfNeeded(
|
|
3281
|
+
await p4.select({
|
|
3282
|
+
message: "Portal navigation shell",
|
|
3283
|
+
initialValue: "top",
|
|
3284
|
+
options: [
|
|
3285
|
+
{
|
|
3286
|
+
value: "top",
|
|
3287
|
+
label: "Top navigation (recommended)",
|
|
3288
|
+
hint: "Simple header links"
|
|
3289
|
+
},
|
|
3290
|
+
{
|
|
3291
|
+
value: "sidebar",
|
|
3292
|
+
label: "Sidebar navigation",
|
|
3293
|
+
hint: "Collapsible left menu (production-shaped)"
|
|
3294
|
+
}
|
|
3295
|
+
]
|
|
3296
|
+
})
|
|
3297
|
+
);
|
|
3298
|
+
nav = navAnswer;
|
|
3299
|
+
} else if (auth === "none") {
|
|
3300
|
+
nav = "top";
|
|
3301
|
+
}
|
|
3302
|
+
let cdnSetup = null;
|
|
3303
|
+
if (template === "module-hybrid-cdn") {
|
|
3304
|
+
if (options.cdnGithub) {
|
|
3305
|
+
cdnSetup = { github: options.cdnGithub };
|
|
3306
|
+
} else {
|
|
3307
|
+
p4.log.step("Hybrid CDN \u2014 GitHub mirror (public repo for jsDelivr)");
|
|
3308
|
+
const prompted = await promptCdnGitHubSetup({
|
|
3309
|
+
projectRoot: options.cwd,
|
|
3310
|
+
githubDefault: githubDefaultForPrompt(options.cwd),
|
|
3311
|
+
github: options.cdnGithub
|
|
3312
|
+
});
|
|
3313
|
+
if (prompted) {
|
|
3314
|
+
cdnSetup = prompted;
|
|
3315
|
+
}
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
const folder = options.directory ?? `${options.cwd}/${projectName}`;
|
|
3319
|
+
const summary = [
|
|
3320
|
+
`Template: ${template}`,
|
|
3321
|
+
`Folder: ${folder}`,
|
|
3322
|
+
...isModuleTemplate(template) ? [
|
|
3323
|
+
`Router: ${router ?? "hash"}`,
|
|
3324
|
+
`Auth: ${auth}`,
|
|
3325
|
+
...auth !== "none" ? [`Nav: ${nav}`] : []
|
|
3326
|
+
] : [],
|
|
3327
|
+
...template === "module-hybrid-cdn" ? [`CDN repo: ${cdnSetup?.github ?? "(skipped \u2014 configure later)"}`] : []
|
|
3328
|
+
].join("\n");
|
|
3329
|
+
p4.note(summary, "Scaffold summary");
|
|
3330
|
+
const confirmed = cancelIfNeeded(
|
|
3331
|
+
await p4.confirm({
|
|
3332
|
+
message: "Proceed with scaffold?",
|
|
3333
|
+
initialValue: true
|
|
3334
|
+
})
|
|
3335
|
+
);
|
|
3336
|
+
if (!confirmed) {
|
|
3337
|
+
throw new WpHsError("Init cancelled", { exitCode: ExitCode.USER_ERROR });
|
|
3338
|
+
}
|
|
3339
|
+
return {
|
|
3340
|
+
template,
|
|
3341
|
+
projectName,
|
|
3342
|
+
router,
|
|
3343
|
+
auth,
|
|
3344
|
+
nav,
|
|
3345
|
+
cdnSetup,
|
|
3346
|
+
confirmed: true
|
|
3347
|
+
};
|
|
3348
|
+
}
|
|
3349
|
+
|
|
2834
3350
|
// src/lib/project-setup.ts
|
|
2835
|
-
import { copyFileSync, existsSync as existsSync12, writeFileSync as
|
|
2836
|
-
import { basename, resolve as
|
|
3351
|
+
import { copyFileSync, existsSync as existsSync12, writeFileSync as writeFileSync7 } from "fs";
|
|
3352
|
+
import { basename, resolve as resolve16 } from "path";
|
|
2837
3353
|
function envFileForTier2(tier) {
|
|
2838
3354
|
return tier === "dev" ? ".env.dev" : tier === "stg" ? ".env.stg" : ".env.prod";
|
|
2839
3355
|
}
|
|
@@ -2848,7 +3364,7 @@ function firstExisting(paths) {
|
|
|
2848
3364
|
return null;
|
|
2849
3365
|
}
|
|
2850
3366
|
function copyMissingFile(projectRoot, targetName, sourceCandidates, dryRun) {
|
|
2851
|
-
const target =
|
|
3367
|
+
const target = resolve16(projectRoot, targetName);
|
|
2852
3368
|
if (existsSync12(target)) {
|
|
2853
3369
|
return { action: "skipped", path: targetName, detail: "already present" };
|
|
2854
3370
|
}
|
|
@@ -2871,12 +3387,12 @@ function copyMissingFile(projectRoot, targetName, sourceCandidates, dryRun) {
|
|
|
2871
3387
|
};
|
|
2872
3388
|
}
|
|
2873
3389
|
function writeMissingFile(projectRoot, targetName, content, dryRun) {
|
|
2874
|
-
const target =
|
|
3390
|
+
const target = resolve16(projectRoot, targetName);
|
|
2875
3391
|
if (existsSync12(target)) {
|
|
2876
3392
|
return { action: "skipped", path: targetName, detail: "already present" };
|
|
2877
3393
|
}
|
|
2878
3394
|
if (!dryRun) {
|
|
2879
|
-
|
|
3395
|
+
writeFileSync7(target, content);
|
|
2880
3396
|
}
|
|
2881
3397
|
return {
|
|
2882
3398
|
action: "copied",
|
|
@@ -2902,15 +3418,15 @@ function applyProjectSetup(ctx, options = {}) {
|
|
|
2902
3418
|
const templateDir = kitTemplateDir(config);
|
|
2903
3419
|
results.push(
|
|
2904
3420
|
copyMissingFile(projectRoot, HUBSPOT_CONFIG_FILE, [
|
|
2905
|
-
|
|
2906
|
-
|
|
3421
|
+
resolve16(projectRoot, HUBSPOT_CONFIG_EXAMPLE),
|
|
3422
|
+
resolve16(templateDir, HUBSPOT_CONFIG_EXAMPLE),
|
|
2907
3423
|
kitPath("templates", HUBSPOT_CONFIG_EXAMPLE)
|
|
2908
3424
|
], dryRun)
|
|
2909
3425
|
);
|
|
2910
3426
|
results.push(
|
|
2911
3427
|
copyMissingFile(projectRoot, ".env.local", [
|
|
2912
|
-
|
|
2913
|
-
|
|
3428
|
+
resolve16(projectRoot, ".env.local.example"),
|
|
3429
|
+
resolve16(sharedDir, ".env.local.example")
|
|
2914
3430
|
], dryRun)
|
|
2915
3431
|
);
|
|
2916
3432
|
for (const tier of tiersToSetup(config, options.tier)) {
|
|
@@ -2918,8 +3434,8 @@ function applyProjectSetup(ctx, options = {}) {
|
|
|
2918
3434
|
const envExample = `${envFile}.example`;
|
|
2919
3435
|
results.push(
|
|
2920
3436
|
copyMissingFile(projectRoot, envFile, [
|
|
2921
|
-
|
|
2922
|
-
|
|
3437
|
+
resolve16(projectRoot, envExample),
|
|
3438
|
+
resolve16(sharedDir, envExample)
|
|
2923
3439
|
], dryRun)
|
|
2924
3440
|
);
|
|
2925
3441
|
}
|
|
@@ -2927,8 +3443,8 @@ function applyProjectSetup(ctx, options = {}) {
|
|
|
2927
3443
|
const mirror = config.cdn.mirror;
|
|
2928
3444
|
results.push(
|
|
2929
3445
|
copyMissingFile(projectRoot, mirror, [
|
|
2930
|
-
|
|
2931
|
-
|
|
3446
|
+
resolve16(projectRoot, `${mirror}.example`),
|
|
3447
|
+
resolve16(sharedDir, "portal.cdn.json.example")
|
|
2932
3448
|
], dryRun)
|
|
2933
3449
|
);
|
|
2934
3450
|
const buildIdFile = config.cdn.buildIdFile;
|
|
@@ -2943,14 +3459,14 @@ function applyProjectSetup(ctx, options = {}) {
|
|
|
2943
3459
|
}
|
|
2944
3460
|
for (const file of templateViteFiles(config)) {
|
|
2945
3461
|
results.push(
|
|
2946
|
-
copyMissingFile(projectRoot, file, [
|
|
3462
|
+
copyMissingFile(projectRoot, file, [resolve16(templateDir, file)], dryRun)
|
|
2947
3463
|
);
|
|
2948
3464
|
}
|
|
2949
3465
|
if (isModuleProject(config) && !isThemeProject(config)) {
|
|
2950
3466
|
const adapter = config.dev.adapter;
|
|
2951
|
-
const adapterPath =
|
|
3467
|
+
const adapterPath = resolve16(projectRoot, adapter);
|
|
2952
3468
|
if (!existsSync12(adapterPath)) {
|
|
2953
|
-
const templateAdapter =
|
|
3469
|
+
const templateAdapter = resolve16(templateDir, "wphs.adapter.ts");
|
|
2954
3470
|
if (existsSync12(templateAdapter)) {
|
|
2955
3471
|
results.push(copyMissingFile(projectRoot, adapter, [templateAdapter], dryRun));
|
|
2956
3472
|
} else {
|
|
@@ -2976,19 +3492,19 @@ function copySharedSetupExamples(targetDir) {
|
|
|
2976
3492
|
".env.prod.example",
|
|
2977
3493
|
"portal.cdn.json.example"
|
|
2978
3494
|
]) {
|
|
2979
|
-
const src =
|
|
2980
|
-
const dest =
|
|
3495
|
+
const src = resolve16(sharedDir, name);
|
|
3496
|
+
const dest = resolve16(targetDir, name);
|
|
2981
3497
|
if (existsSync12(src) && !existsSync12(dest)) {
|
|
2982
3498
|
copyFileSync(src, dest);
|
|
2983
3499
|
}
|
|
2984
3500
|
}
|
|
2985
3501
|
const hubspotExample = firstExisting([
|
|
2986
|
-
|
|
3502
|
+
resolve16(sharedDir, HUBSPOT_CONFIG_EXAMPLE),
|
|
2987
3503
|
kitPath("templates", HUBSPOT_CONFIG_EXAMPLE)
|
|
2988
3504
|
]);
|
|
2989
3505
|
if (!hubspotExample) return;
|
|
2990
|
-
const destHubspot =
|
|
2991
|
-
if (!existsSync12(destHubspot) && !existsSync12(
|
|
3506
|
+
const destHubspot = resolve16(targetDir, HUBSPOT_CONFIG_EXAMPLE);
|
|
3507
|
+
if (!existsSync12(destHubspot) && !existsSync12(resolve16(targetDir, HUBSPOT_CONFIG_FILE))) {
|
|
2992
3508
|
copyFileSync(hubspotExample, destHubspot);
|
|
2993
3509
|
}
|
|
2994
3510
|
}
|
|
@@ -3036,26 +3552,8 @@ var TEMPLATE_SCRIPTS = {
|
|
|
3036
3552
|
}
|
|
3037
3553
|
};
|
|
3038
3554
|
|
|
3039
|
-
// src/lib/project-name.ts
|
|
3040
|
-
var PROJECT_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
3041
|
-
function normalizeProjectName(input) {
|
|
3042
|
-
return input.trim().toLowerCase().replace(/[\s_]+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
3043
|
-
}
|
|
3044
|
-
function validateProjectName(name) {
|
|
3045
|
-
if (!name) return "Project name is required";
|
|
3046
|
-
if (!PROJECT_NAME_PATTERN.test(name)) {
|
|
3047
|
-
return "Use lowercase letters, numbers, and hyphens (e.g. my-portal-module)";
|
|
3048
|
-
}
|
|
3049
|
-
if (name.length > 64) return "Project name is too long (max 64 characters)";
|
|
3050
|
-
return void 0;
|
|
3051
|
-
}
|
|
3052
|
-
function moduleOutDirFromSlug(slug) {
|
|
3053
|
-
const pascal = slug.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
3054
|
-
return `dist/${pascal || "HubSpot"}.Module`;
|
|
3055
|
-
}
|
|
3056
|
-
|
|
3057
3555
|
// src/cli/commands/init.ts
|
|
3058
|
-
function buildConfigForProject(template, projectName, router) {
|
|
3556
|
+
function buildConfigForProject(template, projectName, router, auth = "none", nav = "top") {
|
|
3059
3557
|
const base = { ...DEFAULT_WOODSPORTAL_HS, projectType: template };
|
|
3060
3558
|
if (template === "theme-starter") {
|
|
3061
3559
|
return {
|
|
@@ -3080,8 +3578,12 @@ function buildConfigForProject(template, projectName, router) {
|
|
|
3080
3578
|
mode: template === "module-monolith" ? "none" : base.cdn.mode
|
|
3081
3579
|
}
|
|
3082
3580
|
};
|
|
3083
|
-
if (isModuleTemplate(template)
|
|
3084
|
-
config.scaffold = {
|
|
3581
|
+
if (isModuleTemplate(template)) {
|
|
3582
|
+
config.scaffold = {
|
|
3583
|
+
router: router ?? "hash",
|
|
3584
|
+
auth: auth ?? "none",
|
|
3585
|
+
...auth !== "none" ? { nav: nav ?? "top" } : {}
|
|
3586
|
+
};
|
|
3085
3587
|
}
|
|
3086
3588
|
return config;
|
|
3087
3589
|
}
|
|
@@ -3097,7 +3599,7 @@ function assertTargetDirIsScaffoldable(targetDir) {
|
|
|
3097
3599
|
}
|
|
3098
3600
|
function resolveInitTargetDir(options, interactiveName) {
|
|
3099
3601
|
if (options.directory) {
|
|
3100
|
-
return
|
|
3602
|
+
return resolve17(options.directory);
|
|
3101
3603
|
}
|
|
3102
3604
|
const parentDir = process.cwd();
|
|
3103
3605
|
const slug = options.name ?? interactiveName;
|
|
@@ -3111,67 +3613,31 @@ function resolveInitTargetDir(options, interactiveName) {
|
|
|
3111
3613
|
if (error) {
|
|
3112
3614
|
throw new WpHsError(error, { exitCode: ExitCode.USER_ERROR });
|
|
3113
3615
|
}
|
|
3114
|
-
return
|
|
3616
|
+
return resolve17(parentDir, slug);
|
|
3115
3617
|
}
|
|
3116
3618
|
async function runInit(ctx, options) {
|
|
3117
3619
|
const nonInteractive = ctx.global.ci || ctx.global.yes || ctx.global.json;
|
|
3118
3620
|
let template = options.template;
|
|
3119
3621
|
let projectName = options.name ? normalizeProjectName(options.name) : void 0;
|
|
3120
3622
|
let router = options.router;
|
|
3623
|
+
let auth = options.auth ?? "none";
|
|
3624
|
+
let nav = options.nav ?? "top";
|
|
3625
|
+
let cdnGithub = options.cdnGithub;
|
|
3626
|
+
let wizardCdnSetup = null;
|
|
3121
3627
|
if (!nonInteractive) {
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
if (p4.isCancel(type)) throw new WpHsError("Init cancelled", { exitCode: ExitCode.USER_ERROR });
|
|
3136
|
-
template = type;
|
|
3137
|
-
}
|
|
3138
|
-
if (!options.directory && !projectName) {
|
|
3139
|
-
const nameAnswer = await p4.text({
|
|
3140
|
-
message: "Project name (folder name)",
|
|
3141
|
-
placeholder: "my-portal-module",
|
|
3142
|
-
validate: (value) => validateProjectName(normalizeProjectName(value ?? ""))
|
|
3143
|
-
});
|
|
3144
|
-
if (p4.isCancel(nameAnswer)) {
|
|
3145
|
-
throw new WpHsError("Init cancelled", { exitCode: ExitCode.USER_ERROR });
|
|
3146
|
-
}
|
|
3147
|
-
projectName = normalizeProjectName(nameAnswer);
|
|
3148
|
-
}
|
|
3149
|
-
if (!router && template && isModuleTemplate(template)) {
|
|
3150
|
-
const routerAnswer = await p4.select({
|
|
3151
|
-
message: "Client-side routing",
|
|
3152
|
-
initialValue: "hash",
|
|
3153
|
-
options: [
|
|
3154
|
-
{
|
|
3155
|
-
value: "hash",
|
|
3156
|
-
label: "Hash history (recommended)",
|
|
3157
|
-
hint: "Same as woodsportal-client-frontend \u2014 works in HubSpot CMS embeds"
|
|
3158
|
-
},
|
|
3159
|
-
{
|
|
3160
|
-
value: "browser",
|
|
3161
|
-
label: "Browser history",
|
|
3162
|
-
hint: "Clean /guide URLs for local dev (not for HubSpot iframe embeds)"
|
|
3163
|
-
},
|
|
3164
|
-
{
|
|
3165
|
-
value: "none",
|
|
3166
|
-
label: "No router",
|
|
3167
|
-
hint: "Single-page starter \u2014 add TanStack Router later"
|
|
3168
|
-
}
|
|
3169
|
-
]
|
|
3170
|
-
});
|
|
3171
|
-
if (p4.isCancel(routerAnswer)) {
|
|
3172
|
-
throw new WpHsError("Init cancelled", { exitCode: ExitCode.USER_ERROR });
|
|
3173
|
-
}
|
|
3174
|
-
router = routerAnswer;
|
|
3628
|
+
const wizard = await runInitWizard({
|
|
3629
|
+
...options,
|
|
3630
|
+
cwd: process.cwd(),
|
|
3631
|
+
auth: options.auth
|
|
3632
|
+
});
|
|
3633
|
+
template = wizard.template;
|
|
3634
|
+
projectName = wizard.projectName;
|
|
3635
|
+
router = wizard.router;
|
|
3636
|
+
auth = wizard.auth;
|
|
3637
|
+
nav = wizard.nav ?? "top";
|
|
3638
|
+
wizardCdnSetup = wizard.cdnSetup;
|
|
3639
|
+
if (wizard.cdnSetup?.github) {
|
|
3640
|
+
cdnGithub = wizard.cdnSetup.github;
|
|
3175
3641
|
}
|
|
3176
3642
|
}
|
|
3177
3643
|
if (!template) {
|
|
@@ -3180,6 +3646,19 @@ async function runInit(ctx, options) {
|
|
|
3180
3646
|
if (!router && isModuleTemplate(template)) {
|
|
3181
3647
|
router = "hash";
|
|
3182
3648
|
}
|
|
3649
|
+
if (!options.auth && nonInteractive && isModuleTemplate(template)) {
|
|
3650
|
+
auth = "none";
|
|
3651
|
+
}
|
|
3652
|
+
try {
|
|
3653
|
+
validateAuthRouterCombo(auth, router);
|
|
3654
|
+
validateNavAuthCombo(auth, nav);
|
|
3655
|
+
} catch (error) {
|
|
3656
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3657
|
+
throw new WpHsError(message, {
|
|
3658
|
+
exitCode: ExitCode.USER_ERROR,
|
|
3659
|
+
remediation: "Use --router hash or --router browser with --auth starter|sso|full. Nav shell (--nav sidebar) requires auth."
|
|
3660
|
+
});
|
|
3661
|
+
}
|
|
3183
3662
|
const targetDir = resolveInitTargetDir(options, projectName);
|
|
3184
3663
|
const slug = projectName ?? basename2(targetDir);
|
|
3185
3664
|
assertTargetDirIsScaffoldable(targetDir);
|
|
@@ -3188,36 +3667,41 @@ async function runInit(ctx, options) {
|
|
|
3188
3667
|
throw new WpHsError(`Template not found: ${template}`, { exitCode: ExitCode.USER_ERROR });
|
|
3189
3668
|
}
|
|
3190
3669
|
if (ctx.global.dryRun) {
|
|
3191
|
-
ctx.logger.info(
|
|
3670
|
+
ctx.logger.info(
|
|
3671
|
+
`[dry-run] Would scaffold ${template} in ${targetDir} (router=${router ?? "n/a"}, auth=${auth}, nav=${auth !== "none" ? nav : "n/a"})`
|
|
3672
|
+
);
|
|
3192
3673
|
return ExitCode.SUCCESS;
|
|
3193
3674
|
}
|
|
3194
|
-
|
|
3195
|
-
|
|
3675
|
+
mkdirSync5(targetDir, { recursive: true });
|
|
3676
|
+
cpSync3(templateDir, targetDir, { recursive: true });
|
|
3196
3677
|
copySharedSetupExamples(targetDir);
|
|
3197
|
-
const config = buildConfigForProject(template, slug, router);
|
|
3198
|
-
|
|
3678
|
+
const config = buildConfigForProject(template, slug, router, auth, nav);
|
|
3679
|
+
writeFileSync8(resolve17(targetDir, WOODSCLI_CONFIG_FILE), `${JSON.stringify(config, null, 2)}
|
|
3199
3680
|
`);
|
|
3200
|
-
const pkgPath =
|
|
3681
|
+
const pkgPath = resolve17(targetDir, "package.json");
|
|
3201
3682
|
if (existsSync13(pkgPath)) {
|
|
3202
|
-
const pkg = JSON.parse(
|
|
3683
|
+
const pkg = JSON.parse(readFileSync12(pkgPath, "utf8"));
|
|
3203
3684
|
pkg.name = slug;
|
|
3204
3685
|
pkg.scripts = { ...pkg.scripts, ...TEMPLATE_SCRIPTS[template] };
|
|
3205
3686
|
pkg.devDependencies = {
|
|
3206
3687
|
...pkg.devDependencies,
|
|
3207
3688
|
...templateDevDependencies()
|
|
3208
3689
|
};
|
|
3209
|
-
|
|
3690
|
+
writeFileSync8(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
3210
3691
|
`);
|
|
3211
3692
|
}
|
|
3212
3693
|
if (router && isModuleTemplate(template)) {
|
|
3213
3694
|
applyRouterScaffold(targetDir, router, template);
|
|
3214
3695
|
}
|
|
3696
|
+
if (isModuleTemplate(template)) {
|
|
3697
|
+
applyAuthScaffold(targetDir, auth, router, template, nav);
|
|
3698
|
+
}
|
|
3215
3699
|
runNodeScript(kitPath("scripts/ensure-wphs-workspace.mjs"), [], {
|
|
3216
3700
|
cwd: targetDir,
|
|
3217
3701
|
logger: ctx.logger,
|
|
3218
3702
|
dryRun: false
|
|
3219
3703
|
});
|
|
3220
|
-
const gitignorePath =
|
|
3704
|
+
const gitignorePath = resolve17(targetDir, ".gitignore");
|
|
3221
3705
|
const gitignoreExtras = [
|
|
3222
3706
|
"hubspot.config.yml",
|
|
3223
3707
|
".wphs/config/module-config.local.json",
|
|
@@ -3230,40 +3714,27 @@ async function runInit(ctx, options) {
|
|
|
3230
3714
|
".wphs/.kit-bootstrap.tsx",
|
|
3231
3715
|
".wphs/.kit-bootstrap-greenfield.tsx"
|
|
3232
3716
|
];
|
|
3233
|
-
const existing = existsSync13(gitignorePath) ?
|
|
3717
|
+
const existing = existsSync13(gitignorePath) ? readFileSync12(gitignorePath, "utf8") : "";
|
|
3234
3718
|
const toAppend = gitignoreExtras.filter((line) => !existing.includes(line)).join("\n");
|
|
3235
3719
|
if (toAppend) {
|
|
3236
|
-
|
|
3720
|
+
writeFileSync8(gitignorePath, `${existing.trimEnd()}
|
|
3237
3721
|
${toAppend}
|
|
3238
3722
|
`);
|
|
3239
3723
|
}
|
|
3240
|
-
if (template === "module-hybrid-cdn" && !ctx.global.dryRun
|
|
3241
|
-
const
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
if (options.cdnGithub) {
|
|
3245
|
-
cdnSetup = { github: options.cdnGithub };
|
|
3246
|
-
}
|
|
3247
|
-
} else {
|
|
3248
|
-
p4.log.step("Hybrid CDN \u2014 GitHub mirror (public repo for jsDelivr)");
|
|
3249
|
-
cdnSetup = await promptCdnGitHubSetup({
|
|
3250
|
-
projectRoot: targetDir,
|
|
3251
|
-
githubDefault,
|
|
3252
|
-
github: options.cdnGithub
|
|
3253
|
-
});
|
|
3254
|
-
}
|
|
3255
|
-
if (cdnSetup) {
|
|
3256
|
-
applyCdnGitHubSetup(targetDir, cdnSetup);
|
|
3724
|
+
if (template === "module-hybrid-cdn" && !ctx.global.dryRun) {
|
|
3725
|
+
const setup = wizardCdnSetup ?? (cdnGithub ? { github: cdnGithub } : null);
|
|
3726
|
+
if (setup) {
|
|
3727
|
+
applyCdnGitHubSetup(targetDir, setup);
|
|
3257
3728
|
if (!nonInteractive) {
|
|
3258
|
-
|
|
3729
|
+
p5.log.success(formatCdnSetupNote(targetDir));
|
|
3259
3730
|
} else {
|
|
3260
3731
|
ctx.logger.success(formatCdnSetupNote(targetDir));
|
|
3261
3732
|
}
|
|
3262
3733
|
}
|
|
3263
3734
|
}
|
|
3264
3735
|
if (!nonInteractive) {
|
|
3265
|
-
|
|
3266
|
-
|
|
3736
|
+
p5.log.success(`Scaffolded ${template} in ${targetDir}`);
|
|
3737
|
+
p5.note(`cd ${slug} && yarn install && wp setup && wp doctor && wp dev`, "Getting started");
|
|
3267
3738
|
} else {
|
|
3268
3739
|
ctx.logger.success(`Scaffolded ${template} in ${targetDir}`);
|
|
3269
3740
|
}
|
|
@@ -3271,15 +3742,15 @@ ${toAppend}
|
|
|
3271
3742
|
}
|
|
3272
3743
|
|
|
3273
3744
|
// src/cli/commands/migrate.ts
|
|
3274
|
-
import { cpSync as
|
|
3275
|
-
import { join as
|
|
3745
|
+
import { cpSync as cpSync4, existsSync as existsSync14, mkdirSync as mkdirSync6, readFileSync as readFileSync13, readdirSync as readdirSync3, renameSync, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
3746
|
+
import { join as join4, resolve as resolve18 } from "path";
|
|
3276
3747
|
var LEGACY_DIRS = [".dev", ".woodsportal"];
|
|
3277
3748
|
var LEGACY_BASELINE = ".wp-hs-baseline";
|
|
3278
3749
|
var LEGACY_BUILD_ID_FILE = ".portal-build-id";
|
|
3279
3750
|
var MODULE_BUILD_ID_FILE = ".module-build-id";
|
|
3280
3751
|
function readFirstNonCommentLine(filePath) {
|
|
3281
3752
|
if (!existsSync14(filePath)) return void 0;
|
|
3282
|
-
const line =
|
|
3753
|
+
const line = readFileSync13(filePath, "utf8").split("\n").map((l) => l.trim()).find((l) => l.length > 0 && !l.startsWith("#"));
|
|
3283
3754
|
return line || void 0;
|
|
3284
3755
|
}
|
|
3285
3756
|
function stripToBaseline(raw) {
|
|
@@ -3293,71 +3764,71 @@ function stripToBaseline(raw) {
|
|
|
3293
3764
|
}
|
|
3294
3765
|
async function runMigrate(ctx, _options = {}) {
|
|
3295
3766
|
const root = ctx.projectRoot;
|
|
3296
|
-
const wphsRoot =
|
|
3767
|
+
const wphsRoot = resolve18(root, WPHS_WORKSPACE_DIR);
|
|
3297
3768
|
const actions = [];
|
|
3298
3769
|
for (const legacy of LEGACY_DIRS) {
|
|
3299
|
-
const legacyPath =
|
|
3770
|
+
const legacyPath = resolve18(root, legacy);
|
|
3300
3771
|
if (!existsSync14(legacyPath)) continue;
|
|
3301
3772
|
actions.push(`Move ${legacy}/ \u2192 .wphs/`);
|
|
3302
3773
|
if (ctx.global.dryRun) continue;
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
const localConfig =
|
|
3306
|
-
const exampleConfig =
|
|
3774
|
+
mkdirSync6(join4(wphsRoot, "config"), { recursive: true });
|
|
3775
|
+
mkdirSync6(join4(wphsRoot, "assets", "branding"), { recursive: true });
|
|
3776
|
+
const localConfig = join4(legacyPath, "module-config.local.json");
|
|
3777
|
+
const exampleConfig = join4(legacyPath, "module-config.example.json");
|
|
3307
3778
|
if (existsSync14(localConfig)) {
|
|
3308
|
-
|
|
3779
|
+
cpSync4(localConfig, join4(wphsRoot, "config", "module-config.local.json"));
|
|
3309
3780
|
}
|
|
3310
3781
|
if (existsSync14(exampleConfig)) {
|
|
3311
|
-
|
|
3782
|
+
cpSync4(exampleConfig, join4(wphsRoot, "config", "module-config.example.json"));
|
|
3312
3783
|
}
|
|
3313
|
-
const branding =
|
|
3784
|
+
const branding = join4(legacyPath, "branding-assets");
|
|
3314
3785
|
if (existsSync14(branding)) {
|
|
3315
3786
|
for (const file of readdirSync3(branding)) {
|
|
3316
|
-
|
|
3787
|
+
cpSync4(join4(branding, file), join4(wphsRoot, "assets", "branding", file));
|
|
3317
3788
|
}
|
|
3318
3789
|
}
|
|
3319
|
-
|
|
3790
|
+
rmSync4(legacyPath, { recursive: true, force: true });
|
|
3320
3791
|
}
|
|
3321
|
-
const legacyBaseline =
|
|
3322
|
-
const targetBaseline =
|
|
3792
|
+
const legacyBaseline = resolve18(root, LEGACY_BASELINE);
|
|
3793
|
+
const targetBaseline = join4(wphsRoot, "audit", "baseline");
|
|
3323
3794
|
if (existsSync14(legacyBaseline)) {
|
|
3324
3795
|
actions.push(`Move ${LEGACY_BASELINE}/ \u2192 .wphs/audit/baseline/`);
|
|
3325
3796
|
if (!ctx.global.dryRun) {
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3797
|
+
mkdirSync6(targetBaseline, { recursive: true });
|
|
3798
|
+
cpSync4(legacyBaseline, targetBaseline, { recursive: true });
|
|
3799
|
+
rmSync4(legacyBaseline, { recursive: true, force: true });
|
|
3329
3800
|
}
|
|
3330
3801
|
}
|
|
3331
|
-
const
|
|
3332
|
-
const woodscliPath =
|
|
3333
|
-
if (existsSync14(
|
|
3802
|
+
const legacyConfigPath = resolve18(root, LEGACY_WOODSPORTAL_HS_FILE);
|
|
3803
|
+
const woodscliPath = resolve18(root, WOODSCLI_CONFIG_FILE);
|
|
3804
|
+
if (existsSync14(legacyConfigPath) && !existsSync14(woodscliPath)) {
|
|
3334
3805
|
actions.push(`Rename ${LEGACY_WOODSPORTAL_HS_FILE} \u2192 ${WOODSCLI_CONFIG_FILE}`);
|
|
3335
3806
|
if (!ctx.global.dryRun) {
|
|
3336
|
-
renameSync(
|
|
3807
|
+
renameSync(legacyConfigPath, woodscliPath);
|
|
3337
3808
|
}
|
|
3338
|
-
} else if (existsSync14(
|
|
3809
|
+
} else if (existsSync14(legacyConfigPath) && existsSync14(woodscliPath)) {
|
|
3339
3810
|
actions.push(`Remove duplicate ${LEGACY_WOODSPORTAL_HS_FILE}`);
|
|
3340
3811
|
if (!ctx.global.dryRun) {
|
|
3341
|
-
|
|
3812
|
+
rmSync4(legacyConfigPath, { force: true });
|
|
3342
3813
|
}
|
|
3343
3814
|
}
|
|
3344
|
-
const legacyBuildIdPath =
|
|
3345
|
-
const moduleBuildIdPath =
|
|
3815
|
+
const legacyBuildIdPath = resolve18(root, LEGACY_BUILD_ID_FILE);
|
|
3816
|
+
const moduleBuildIdPath = resolve18(root, MODULE_BUILD_ID_FILE);
|
|
3346
3817
|
if (existsSync14(legacyBuildIdPath) && !existsSync14(moduleBuildIdPath)) {
|
|
3347
3818
|
const baseline = stripToBaseline(readFirstNonCommentLine(legacyBuildIdPath)) ?? "v0.0.1-dev";
|
|
3348
3819
|
actions.push(`Migrate ${LEGACY_BUILD_ID_FILE} \u2192 ${MODULE_BUILD_ID_FILE} (baseline ${baseline})`);
|
|
3349
3820
|
if (!ctx.global.dryRun) {
|
|
3350
|
-
|
|
3821
|
+
writeFileSync9(
|
|
3351
3822
|
moduleBuildIdPath,
|
|
3352
3823
|
`# Channel baseline for local CDN deploys (wp deploy auto-bumps per developer).
|
|
3353
3824
|
${baseline}
|
|
3354
3825
|
`
|
|
3355
3826
|
);
|
|
3356
|
-
|
|
3827
|
+
rmSync4(legacyBuildIdPath, { force: true });
|
|
3357
3828
|
}
|
|
3358
3829
|
}
|
|
3359
3830
|
if (existsSync14(woodscliPath)) {
|
|
3360
|
-
const raw = JSON.parse(
|
|
3831
|
+
const raw = JSON.parse(readFileSync13(woodscliPath, "utf8"));
|
|
3361
3832
|
let configChanged = false;
|
|
3362
3833
|
if (raw?.dev?.moduleConfigDir && raw.dev.moduleConfigDir !== WPHS_WORKSPACE_DIR) {
|
|
3363
3834
|
actions.push(`Update dev.moduleConfigDir \u2192 .wphs in project config`);
|
|
@@ -3374,7 +3845,7 @@ ${baseline}
|
|
|
3374
3845
|
}
|
|
3375
3846
|
}
|
|
3376
3847
|
if (configChanged && !ctx.global.dryRun) {
|
|
3377
|
-
|
|
3848
|
+
writeFileSync9(woodscliPath, `${JSON.stringify(raw, null, 2)}
|
|
3378
3849
|
`);
|
|
3379
3850
|
}
|
|
3380
3851
|
}
|
|
@@ -3402,10 +3873,10 @@ ${baseline}
|
|
|
3402
3873
|
}
|
|
3403
3874
|
|
|
3404
3875
|
// src/cli/commands/examples.ts
|
|
3405
|
-
import { readFileSync as
|
|
3876
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
3406
3877
|
function loadManifest() {
|
|
3407
3878
|
const manifestPath = kitPath("examples", "manifest.json");
|
|
3408
|
-
const raw = JSON.parse(
|
|
3879
|
+
const raw = JSON.parse(readFileSync14(manifestPath, "utf8"));
|
|
3409
3880
|
return raw.scenarios;
|
|
3410
3881
|
}
|
|
3411
3882
|
async function runExamplesList(ctx, options = {}) {
|
|
@@ -3438,13 +3909,13 @@ async function runExamplesSmoke(ctx, options = {}) {
|
|
|
3438
3909
|
}
|
|
3439
3910
|
|
|
3440
3911
|
// src/cli/commands/fields.ts
|
|
3441
|
-
import { cpSync as
|
|
3442
|
-
import { resolve as
|
|
3912
|
+
import { cpSync as cpSync5, existsSync as existsSync15, readFileSync as readFileSync15, writeFileSync as writeFileSync10 } from "fs";
|
|
3913
|
+
import { resolve as resolve19 } from "path";
|
|
3443
3914
|
async function runFieldsSync(ctx, options = {}) {
|
|
3444
3915
|
const direction = options.direction ?? "pull";
|
|
3445
|
-
const fieldsPath =
|
|
3446
|
-
const cacheDir =
|
|
3447
|
-
const cachePath =
|
|
3916
|
+
const fieldsPath = resolve19(ctx.projectRoot, ctx.config.dev.fieldsJson);
|
|
3917
|
+
const cacheDir = resolve19(ctx.projectRoot, ctx.config.dev.moduleConfigDir, "cache", "fields-sync");
|
|
3918
|
+
const cachePath = resolve19(cacheDir, "fields.json");
|
|
3448
3919
|
if (direction === "pull") {
|
|
3449
3920
|
if (!existsSync15(fieldsPath)) {
|
|
3450
3921
|
throw new WpHsError(`Missing ${ctx.config.dev.fieldsJson}`, {
|
|
@@ -3456,9 +3927,9 @@ async function runFieldsSync(ctx, options = {}) {
|
|
|
3456
3927
|
ctx.logger.info(`[dry-run] Would cache ${fieldsPath} \u2192 ${cachePath}`);
|
|
3457
3928
|
return ExitCode.SUCCESS;
|
|
3458
3929
|
}
|
|
3459
|
-
const { mkdirSync:
|
|
3460
|
-
|
|
3461
|
-
|
|
3930
|
+
const { mkdirSync: mkdirSync7 } = await import("fs");
|
|
3931
|
+
mkdirSync7(cacheDir, { recursive: true });
|
|
3932
|
+
cpSync5(fieldsPath, cachePath);
|
|
3462
3933
|
ctx.logger.success(`Cached fields \u2192 ${cachePath}`);
|
|
3463
3934
|
return ExitCode.SUCCESS;
|
|
3464
3935
|
}
|
|
@@ -3471,14 +3942,14 @@ async function runFieldsSync(ctx, options = {}) {
|
|
|
3471
3942
|
ctx.logger.info(`[dry-run] Would write ${cachePath} \u2192 ${fieldsPath}`);
|
|
3472
3943
|
return ExitCode.SUCCESS;
|
|
3473
3944
|
}
|
|
3474
|
-
|
|
3945
|
+
writeFileSync10(fieldsPath, readFileSync15(cachePath, "utf8"));
|
|
3475
3946
|
ctx.logger.success(`Restored fields from cache \u2192 ${fieldsPath}`);
|
|
3476
3947
|
return ExitCode.SUCCESS;
|
|
3477
3948
|
}
|
|
3478
3949
|
|
|
3479
3950
|
// src/cli/commands/regression.ts
|
|
3480
3951
|
import { existsSync as existsSync16 } from "fs";
|
|
3481
|
-
import { resolve as
|
|
3952
|
+
import { resolve as resolve20 } from "path";
|
|
3482
3953
|
var LEGACY_SHIM_SCRIPTS = [
|
|
3483
3954
|
"build-cdn.mjs",
|
|
3484
3955
|
"run-vite-build.mjs",
|
|
@@ -3501,7 +3972,7 @@ async function runRegressionSmoke(ctx, options = {}) {
|
|
|
3501
3972
|
if (!validateOk) failures.push("wp config validate failed");
|
|
3502
3973
|
if (options.checkLegacyShims !== false) {
|
|
3503
3974
|
for (const script of LEGACY_SHIM_SCRIPTS) {
|
|
3504
|
-
const shim =
|
|
3975
|
+
const shim = resolve20(ctx.projectRoot, "scripts", script);
|
|
3505
3976
|
const kitScript = kitPath("scripts", script);
|
|
3506
3977
|
const shimOk = existsSync16(shim) && existsSync16(kitScript);
|
|
3507
3978
|
checks.push({ name: `shim:${script}`, ok: shimOk });
|
|
@@ -3509,12 +3980,12 @@ async function runRegressionSmoke(ctx, options = {}) {
|
|
|
3509
3980
|
}
|
|
3510
3981
|
}
|
|
3511
3982
|
for (const rel of FORBIDDEN_CONSUMER_PATHS) {
|
|
3512
|
-
const forbidden =
|
|
3983
|
+
const forbidden = resolve20(ctx.projectRoot, rel);
|
|
3513
3984
|
const absent = !existsSync16(forbidden);
|
|
3514
3985
|
checks.push({ name: `no-${rel}`, ok: absent });
|
|
3515
3986
|
if (!absent) failures.push(`Forbidden dev path still present: ${rel}`);
|
|
3516
3987
|
}
|
|
3517
|
-
const woodscliConfig =
|
|
3988
|
+
const woodscliConfig = resolve20(ctx.projectRoot, WOODSCLI_CONFIG_FILE);
|
|
3518
3989
|
const configOk = existsSync16(woodscliConfig);
|
|
3519
3990
|
checks.push({ name: WOODSCLI_CONFIG_FILE, ok: configOk });
|
|
3520
3991
|
if (!configOk) failures.push(`${WOODSCLI_CONFIG_FILE} missing`);
|
|
@@ -3625,11 +4096,11 @@ async function runSetup(ctx, options = {}) {
|
|
|
3625
4096
|
}
|
|
3626
4097
|
|
|
3627
4098
|
// src/cli/commands/portal.ts
|
|
3628
|
-
import * as
|
|
4099
|
+
import * as p6 from "@clack/prompts";
|
|
3629
4100
|
|
|
3630
4101
|
// src/lib/hubspot-portals.ts
|
|
3631
|
-
import { existsSync as existsSync17, readFileSync as
|
|
3632
|
-
import { resolve as
|
|
4102
|
+
import { existsSync as existsSync17, readFileSync as readFileSync16 } from "fs";
|
|
4103
|
+
import { resolve as resolve21 } from "path";
|
|
3633
4104
|
function stripYamlScalar(value) {
|
|
3634
4105
|
const trimmed = value.trim();
|
|
3635
4106
|
if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
@@ -3667,20 +4138,20 @@ function parseHubSpotConfigPortals(raw) {
|
|
|
3667
4138
|
return { defaultPortal, portals };
|
|
3668
4139
|
}
|
|
3669
4140
|
function readHubSpotPortals(projectRoot) {
|
|
3670
|
-
const configPath =
|
|
4141
|
+
const configPath = resolve21(projectRoot, HUBSPOT_CONFIG_FILE);
|
|
3671
4142
|
if (!existsSync17(configPath)) {
|
|
3672
4143
|
throw new WpHsError(`${HUBSPOT_CONFIG_FILE} not found in project root.`, {
|
|
3673
4144
|
exitCode: ExitCode.USER_ERROR,
|
|
3674
4145
|
remediation: "Run wp setup, then add your portal + PAT."
|
|
3675
4146
|
});
|
|
3676
4147
|
}
|
|
3677
|
-
const raw =
|
|
4148
|
+
const raw = readFileSync16(configPath, "utf8");
|
|
3678
4149
|
const { defaultPortal, portals } = parseHubSpotConfigPortals(raw);
|
|
3679
4150
|
return { configPath, defaultPortal, portals };
|
|
3680
4151
|
}
|
|
3681
4152
|
function resolvePortalName(portals, nameOrId) {
|
|
3682
4153
|
const needle = nameOrId.trim();
|
|
3683
|
-
return portals.find((
|
|
4154
|
+
return portals.find((p9) => p9.name === needle) ?? portals.find((p9) => p9.portalId === needle) ?? null;
|
|
3684
4155
|
}
|
|
3685
4156
|
|
|
3686
4157
|
// src/cli/commands/portal.ts
|
|
@@ -3735,7 +4206,7 @@ async function runPortalUse(ctx, options = {}) {
|
|
|
3735
4206
|
remediation: "wp portal use <name> \xB7 wp portal list"
|
|
3736
4207
|
});
|
|
3737
4208
|
}
|
|
3738
|
-
const answer = await
|
|
4209
|
+
const answer = await p6.select({
|
|
3739
4210
|
message: "Set default HubSpot portal",
|
|
3740
4211
|
options: data.portals.map((portal2) => ({
|
|
3741
4212
|
value: portal2.name,
|
|
@@ -3743,7 +4214,7 @@ async function runPortalUse(ctx, options = {}) {
|
|
|
3743
4214
|
hint: portal2.name === data.defaultPortal ? `current default \xB7 ${portal2.portalId ?? "no id"}` : portal2.portalId ?? void 0
|
|
3744
4215
|
}))
|
|
3745
4216
|
});
|
|
3746
|
-
if (
|
|
4217
|
+
if (p6.isCancel(answer)) {
|
|
3747
4218
|
throw new WpHsError("Portal selection cancelled", { exitCode: ExitCode.USER_ERROR });
|
|
3748
4219
|
}
|
|
3749
4220
|
targetName = answer;
|
|
@@ -3777,6 +4248,311 @@ async function runPortalUse(ctx, options = {}) {
|
|
|
3777
4248
|
return ExitCode.SUCCESS;
|
|
3778
4249
|
}
|
|
3779
4250
|
|
|
4251
|
+
// src/cli/commands/page.ts
|
|
4252
|
+
import * as p7 from "@clack/prompts";
|
|
4253
|
+
function cancelIfNeeded2(value) {
|
|
4254
|
+
if (p7.isCancel(value)) {
|
|
4255
|
+
throw new WpHsError("Page scaffold cancelled", { exitCode: ExitCode.USER_ERROR });
|
|
4256
|
+
}
|
|
4257
|
+
return value;
|
|
4258
|
+
}
|
|
4259
|
+
function resolveAccessFlags(options) {
|
|
4260
|
+
if (options.isPublic && options.isProtected) {
|
|
4261
|
+
throw new WpHsError("Use only one of --public or --protected.", {
|
|
4262
|
+
exitCode: ExitCode.USER_ERROR
|
|
4263
|
+
});
|
|
4264
|
+
}
|
|
4265
|
+
if (options.isPublic) {
|
|
4266
|
+
return "public";
|
|
4267
|
+
}
|
|
4268
|
+
if (options.isProtected) {
|
|
4269
|
+
return "protected";
|
|
4270
|
+
}
|
|
4271
|
+
return void 0;
|
|
4272
|
+
}
|
|
4273
|
+
async function promptPageAddOptions(ctx, options) {
|
|
4274
|
+
const nonInteractive = ctx.global.ci || ctx.global.yes || ctx.global.json;
|
|
4275
|
+
const hasNavConfig = navConfigFileExists(ctx.projectRoot);
|
|
4276
|
+
let slug = options.slug ? normalizePageSlug(options.slug) : void 0;
|
|
4277
|
+
if (!slug && nonInteractive) {
|
|
4278
|
+
throw new WpHsError("Page slug is required in non-interactive mode.", {
|
|
4279
|
+
exitCode: ExitCode.USER_ERROR,
|
|
4280
|
+
remediation: "Pass a slug: wp page add settings"
|
|
4281
|
+
});
|
|
4282
|
+
}
|
|
4283
|
+
if (!slug) {
|
|
4284
|
+
const slugAnswer = cancelIfNeeded2(
|
|
4285
|
+
await p7.text({
|
|
4286
|
+
message: "Page slug (URL segment)",
|
|
4287
|
+
placeholder: "settings",
|
|
4288
|
+
validate: (value) => {
|
|
4289
|
+
const normalized = normalizePageSlug(value ?? "");
|
|
4290
|
+
if (!normalized || !/^[a-z][a-z0-9-]*$/.test(normalized)) {
|
|
4291
|
+
return "Use lowercase letters, numbers, and hyphens";
|
|
4292
|
+
}
|
|
4293
|
+
return void 0;
|
|
4294
|
+
}
|
|
4295
|
+
})
|
|
4296
|
+
);
|
|
4297
|
+
slug = normalizePageSlug(slugAnswer);
|
|
4298
|
+
}
|
|
4299
|
+
let path = options.path ? normalizePagePath(options.path, slug) : void 0;
|
|
4300
|
+
if (!path && !nonInteractive) {
|
|
4301
|
+
const pathAnswer = cancelIfNeeded2(
|
|
4302
|
+
await p7.text({
|
|
4303
|
+
message: "Route path",
|
|
4304
|
+
initialValue: `/${slug}`,
|
|
4305
|
+
validate: (value) => {
|
|
4306
|
+
const normalized = normalizePagePath(value ?? "", slug);
|
|
4307
|
+
if (!normalized.startsWith("/")) {
|
|
4308
|
+
return "Path must start with /";
|
|
4309
|
+
}
|
|
4310
|
+
return void 0;
|
|
4311
|
+
}
|
|
4312
|
+
})
|
|
4313
|
+
);
|
|
4314
|
+
path = normalizePagePath(pathAnswer, slug);
|
|
4315
|
+
}
|
|
4316
|
+
path ??= normalizePagePath(`/${slug}`, slug);
|
|
4317
|
+
let title = options.title?.trim() || titleFromSlug(slug);
|
|
4318
|
+
if (!options.title && !nonInteractive) {
|
|
4319
|
+
const titleAnswer = cancelIfNeeded2(
|
|
4320
|
+
await p7.text({
|
|
4321
|
+
message: "Page title",
|
|
4322
|
+
initialValue: title,
|
|
4323
|
+
validate: (value) => value?.trim() ? void 0 : "Title is required"
|
|
4324
|
+
})
|
|
4325
|
+
);
|
|
4326
|
+
title = titleAnswer.trim();
|
|
4327
|
+
}
|
|
4328
|
+
let access = resolveAccessFlags(options);
|
|
4329
|
+
if (!access && nonInteractive) {
|
|
4330
|
+
throw new WpHsError("Access mode is required in non-interactive mode.", {
|
|
4331
|
+
exitCode: ExitCode.USER_ERROR,
|
|
4332
|
+
remediation: "Pass --public or --protected."
|
|
4333
|
+
});
|
|
4334
|
+
}
|
|
4335
|
+
if (!access) {
|
|
4336
|
+
const accessAnswer = cancelIfNeeded2(
|
|
4337
|
+
await p7.select({
|
|
4338
|
+
message: "Who can access this page?",
|
|
4339
|
+
initialValue: "public",
|
|
4340
|
+
options: [
|
|
4341
|
+
{
|
|
4342
|
+
value: "public",
|
|
4343
|
+
label: "Public",
|
|
4344
|
+
hint: "Visible without signing in"
|
|
4345
|
+
},
|
|
4346
|
+
{
|
|
4347
|
+
value: "protected",
|
|
4348
|
+
label: "Protected",
|
|
4349
|
+
hint: "Requires portal sign-in"
|
|
4350
|
+
}
|
|
4351
|
+
]
|
|
4352
|
+
})
|
|
4353
|
+
);
|
|
4354
|
+
access = accessAnswer;
|
|
4355
|
+
}
|
|
4356
|
+
let addToNav;
|
|
4357
|
+
if (options.addToNav) {
|
|
4358
|
+
addToNav = true;
|
|
4359
|
+
} else if (options.skipNav) {
|
|
4360
|
+
addToNav = false;
|
|
4361
|
+
} else if (!hasNavConfig) {
|
|
4362
|
+
addToNav = false;
|
|
4363
|
+
} else if (nonInteractive) {
|
|
4364
|
+
throw new WpHsError("Navigation choice is required in non-interactive mode.", {
|
|
4365
|
+
exitCode: ExitCode.USER_ERROR,
|
|
4366
|
+
remediation: "Pass --nav or --no-nav when portal-nav.ts or starter-nav.ts exists."
|
|
4367
|
+
});
|
|
4368
|
+
} else {
|
|
4369
|
+
const navAnswer = cancelIfNeeded2(
|
|
4370
|
+
await p7.confirm({
|
|
4371
|
+
message: "Add to navigation menu?",
|
|
4372
|
+
initialValue: true
|
|
4373
|
+
})
|
|
4374
|
+
);
|
|
4375
|
+
addToNav = navAnswer;
|
|
4376
|
+
}
|
|
4377
|
+
return { slug, path, title, access, addToNav };
|
|
4378
|
+
}
|
|
4379
|
+
async function runPageAdd(ctx, options) {
|
|
4380
|
+
const resolved = await promptPageAddOptions(ctx, options);
|
|
4381
|
+
const result = await scaffoldPage({
|
|
4382
|
+
projectRoot: ctx.projectRoot,
|
|
4383
|
+
config: ctx.config,
|
|
4384
|
+
slug: resolved.slug,
|
|
4385
|
+
path: resolved.path,
|
|
4386
|
+
title: resolved.title,
|
|
4387
|
+
access: resolved.access,
|
|
4388
|
+
addToNav: resolved.addToNav,
|
|
4389
|
+
dryRun: ctx.global.dryRun,
|
|
4390
|
+
logger: ctx.logger
|
|
4391
|
+
});
|
|
4392
|
+
if (ctx.global.json) {
|
|
4393
|
+
ctx.logger.json({
|
|
4394
|
+
ok: true,
|
|
4395
|
+
slug: resolved.slug,
|
|
4396
|
+
path: resolved.path,
|
|
4397
|
+
access: resolved.access,
|
|
4398
|
+
addToNav: resolved.addToNav,
|
|
4399
|
+
...result
|
|
4400
|
+
});
|
|
4401
|
+
return ExitCode.SUCCESS;
|
|
4402
|
+
}
|
|
4403
|
+
if (ctx.global.dryRun) {
|
|
4404
|
+
ctx.logger.info("[dry-run] Page scaffold planned.");
|
|
4405
|
+
return ExitCode.SUCCESS;
|
|
4406
|
+
}
|
|
4407
|
+
const lines = [
|
|
4408
|
+
`Created ${result.routeFile}`,
|
|
4409
|
+
`Created ${result.pageFile}`,
|
|
4410
|
+
result.routeTreeRegenerated ? "Regenerated src/routeTree.gen.ts" : result.routeTreeWarning ?? "Restart wp dev if navigation types look stale."
|
|
4411
|
+
];
|
|
4412
|
+
if (!ctx.global.quiet) {
|
|
4413
|
+
p7.log.success(`Page "${resolved.title}" scaffolded at ${resolved.path}`);
|
|
4414
|
+
p7.note(lines.join("\n"), "Next steps");
|
|
4415
|
+
} else {
|
|
4416
|
+
ctx.logger.success(lines.join("\n"));
|
|
4417
|
+
}
|
|
4418
|
+
return ExitCode.SUCCESS;
|
|
4419
|
+
}
|
|
4420
|
+
async function runPageList(ctx) {
|
|
4421
|
+
const { listScaffoldedPages } = await import("./scaffold-page-lifecycle-EHMPWPPE.js");
|
|
4422
|
+
const pages = listScaffoldedPages(ctx.projectRoot, ctx.config);
|
|
4423
|
+
if (ctx.global.json) {
|
|
4424
|
+
ctx.logger.json({ pages });
|
|
4425
|
+
return ExitCode.SUCCESS;
|
|
4426
|
+
}
|
|
4427
|
+
if (pages.length === 0) {
|
|
4428
|
+
ctx.logger.info("No scaffolded pages in src/features/pages/ (use wp page add).");
|
|
4429
|
+
return ExitCode.SUCCESS;
|
|
4430
|
+
}
|
|
4431
|
+
if (!ctx.global.quiet) {
|
|
4432
|
+
p7.log.info(`Scaffolded pages (${pages.length})`);
|
|
4433
|
+
for (const page of pages) {
|
|
4434
|
+
const navFlag = page.inNav ? "nav" : "no-nav";
|
|
4435
|
+
p7.log.message(
|
|
4436
|
+
` ${page.slug} ${page.path} ${page.access} ${navFlag} ${page.routeFile}`
|
|
4437
|
+
);
|
|
4438
|
+
}
|
|
4439
|
+
} else {
|
|
4440
|
+
for (const page of pages) {
|
|
4441
|
+
ctx.logger.info(`${page.slug} ${page.path} ${page.access} ${page.inNav}`);
|
|
4442
|
+
}
|
|
4443
|
+
}
|
|
4444
|
+
return ExitCode.SUCCESS;
|
|
4445
|
+
}
|
|
4446
|
+
async function runPageRemove(ctx, options) {
|
|
4447
|
+
const { removeScaffoldedPage } = await import("./scaffold-page-lifecycle-EHMPWPPE.js");
|
|
4448
|
+
const result = removeScaffoldedPage({
|
|
4449
|
+
projectRoot: ctx.projectRoot,
|
|
4450
|
+
config: ctx.config,
|
|
4451
|
+
slug: options.slug,
|
|
4452
|
+
removeFromNav: !options.skipNav,
|
|
4453
|
+
dryRun: ctx.global.dryRun,
|
|
4454
|
+
logger: ctx.logger
|
|
4455
|
+
});
|
|
4456
|
+
if (ctx.global.json) {
|
|
4457
|
+
ctx.logger.json({ ok: true, ...result });
|
|
4458
|
+
return ExitCode.SUCCESS;
|
|
4459
|
+
}
|
|
4460
|
+
if (ctx.global.dryRun) {
|
|
4461
|
+
ctx.logger.info("[dry-run] Page removal planned.");
|
|
4462
|
+
return ExitCode.SUCCESS;
|
|
4463
|
+
}
|
|
4464
|
+
p7.log.success(`Removed page "${result.slug}"`);
|
|
4465
|
+
return ExitCode.SUCCESS;
|
|
4466
|
+
}
|
|
4467
|
+
|
|
4468
|
+
// src/cli/commands/scaffold.ts
|
|
4469
|
+
import * as p8 from "@clack/prompts";
|
|
4470
|
+
import { writeFileSync as writeFileSync11 } from "fs";
|
|
4471
|
+
import { resolve as resolve22 } from "path";
|
|
4472
|
+
function persistScaffoldConfig(projectRoot, config, patch) {
|
|
4473
|
+
const auth = patch.auth ?? config.scaffold?.auth ?? "none";
|
|
4474
|
+
const router = patch.router ?? config.scaffold?.router ?? "hash";
|
|
4475
|
+
const scaffold = { router, auth };
|
|
4476
|
+
if (auth !== "none") {
|
|
4477
|
+
scaffold.nav = patch.nav ?? config.scaffold?.nav ?? "top";
|
|
4478
|
+
}
|
|
4479
|
+
const next = { ...config, scaffold };
|
|
4480
|
+
writeFileSync11(resolve22(projectRoot, WOODSCLI_CONFIG_FILE), `${JSON.stringify(next, null, 2)}
|
|
4481
|
+
`);
|
|
4482
|
+
return next;
|
|
4483
|
+
}
|
|
4484
|
+
function parseAuthTier(value) {
|
|
4485
|
+
if (value === "starter" || value === "sso" || value === "full") {
|
|
4486
|
+
return value;
|
|
4487
|
+
}
|
|
4488
|
+
return void 0;
|
|
4489
|
+
}
|
|
4490
|
+
function runScaffoldAuthUpgrade(ctx, options) {
|
|
4491
|
+
const routerMode = ctx.config.scaffold?.router ?? "hash";
|
|
4492
|
+
const nav = ctx.config.scaffold?.nav ?? "top";
|
|
4493
|
+
const current = readProjectAuthTier(ctx.projectRoot);
|
|
4494
|
+
if (ctx.global.dryRun) {
|
|
4495
|
+
ctx.logger.info(
|
|
4496
|
+
`[dry-run] Would upgrade auth from "${current}" to "${options.tier}" in ${ctx.projectRoot}`
|
|
4497
|
+
);
|
|
4498
|
+
return ExitCode.SUCCESS;
|
|
4499
|
+
}
|
|
4500
|
+
const applied = applyAuthTierUpgrade(
|
|
4501
|
+
ctx.projectRoot,
|
|
4502
|
+
options.tier,
|
|
4503
|
+
routerMode,
|
|
4504
|
+
ctx.config.projectType,
|
|
4505
|
+
nav
|
|
4506
|
+
);
|
|
4507
|
+
persistScaffoldConfig(ctx.projectRoot, ctx.config, {
|
|
4508
|
+
auth: applied,
|
|
4509
|
+
nav: applied !== "none" ? nav : void 0
|
|
4510
|
+
});
|
|
4511
|
+
if (ctx.global.json) {
|
|
4512
|
+
ctx.logger.json({ ok: true, previousTier: current, tier: applied });
|
|
4513
|
+
return ExitCode.SUCCESS;
|
|
4514
|
+
}
|
|
4515
|
+
p8.log.success(`Portal auth upgraded to "${applied}" (was "${current}")`);
|
|
4516
|
+
p8.note("Review new routes under src/routes/_auth/ and run wp dev.", "Next steps");
|
|
4517
|
+
return ExitCode.SUCCESS;
|
|
4518
|
+
}
|
|
4519
|
+
function runScaffoldNav(ctx, options) {
|
|
4520
|
+
const currentNav = ctx.config.scaffold?.nav ?? "top";
|
|
4521
|
+
if (ctx.config.scaffold?.auth === "none" || readProjectAuthTier(ctx.projectRoot) === "none") {
|
|
4522
|
+
throw new WpHsError("Nav scaffold requires portal auth.", {
|
|
4523
|
+
exitCode: ExitCode.USER_ERROR,
|
|
4524
|
+
remediation: "Run wp scaffold auth upgrade --tier starter first."
|
|
4525
|
+
});
|
|
4526
|
+
}
|
|
4527
|
+
if (ctx.global.dryRun) {
|
|
4528
|
+
ctx.logger.info(`[dry-run] Would apply nav shell "${options.mode}" (was "${currentNav}")`);
|
|
4529
|
+
return ExitCode.SUCCESS;
|
|
4530
|
+
}
|
|
4531
|
+
applyNavScaffoldUpgrade(ctx.projectRoot, options.mode);
|
|
4532
|
+
persistScaffoldConfig(ctx.projectRoot, ctx.config, { nav: options.mode });
|
|
4533
|
+
if (ctx.global.json) {
|
|
4534
|
+
ctx.logger.json({ ok: true, previousMode: currentNav, mode: options.mode });
|
|
4535
|
+
return ExitCode.SUCCESS;
|
|
4536
|
+
}
|
|
4537
|
+
p8.log.success(`Navigation shell set to "${options.mode}" (was "${currentNav}")`);
|
|
4538
|
+
return ExitCode.SUCCESS;
|
|
4539
|
+
}
|
|
4540
|
+
function parseScaffoldAuthTierArg(value) {
|
|
4541
|
+
const tier = value ? parseAuthTier(value) : void 0;
|
|
4542
|
+
if (!tier) {
|
|
4543
|
+
throw new WpHsError("Auth tier is required: starter | sso | full", {
|
|
4544
|
+
exitCode: ExitCode.USER_ERROR
|
|
4545
|
+
});
|
|
4546
|
+
}
|
|
4547
|
+
return tier;
|
|
4548
|
+
}
|
|
4549
|
+
function parseScaffoldNavModeArg(value) {
|
|
4550
|
+
if (value === "top" || value === "sidebar") {
|
|
4551
|
+
return value;
|
|
4552
|
+
}
|
|
4553
|
+
throw new WpHsError("Nav mode is required: top | sidebar", { exitCode: ExitCode.USER_ERROR });
|
|
4554
|
+
}
|
|
4555
|
+
|
|
3780
4556
|
// src/cli/commands/watch.ts
|
|
3781
4557
|
async function runWatch(ctx, options) {
|
|
3782
4558
|
ensureHubSpotConfigForCms(ctx);
|
|
@@ -3802,14 +4578,19 @@ async function runWatch(ctx, options) {
|
|
|
3802
4578
|
}
|
|
3803
4579
|
|
|
3804
4580
|
// src/cli/program.ts
|
|
3805
|
-
function
|
|
4581
|
+
function resolveProjectRootForVersionCheck(project) {
|
|
4582
|
+
if (project) {
|
|
4583
|
+
return project;
|
|
4584
|
+
}
|
|
3806
4585
|
try {
|
|
3807
|
-
|
|
3808
|
-
return pkg.version ?? "0.0.0";
|
|
4586
|
+
return findProjectRoot();
|
|
3809
4587
|
} catch {
|
|
3810
|
-
return
|
|
4588
|
+
return process.cwd();
|
|
3811
4589
|
}
|
|
3812
4590
|
}
|
|
4591
|
+
function readVersion() {
|
|
4592
|
+
return readKitVersion();
|
|
4593
|
+
}
|
|
3813
4594
|
function buildContext(global) {
|
|
3814
4595
|
const logger = createLogger({
|
|
3815
4596
|
verbose: global.verbose,
|
|
@@ -3817,7 +4598,7 @@ function buildContext(global) {
|
|
|
3817
4598
|
json: global.json,
|
|
3818
4599
|
ci: global.ci
|
|
3819
4600
|
});
|
|
3820
|
-
const projectRoot = global.project ??
|
|
4601
|
+
const projectRoot = global.project ?? findProjectRoot();
|
|
3821
4602
|
const { config } = loadConfig(projectRoot);
|
|
3822
4603
|
return { projectRoot, config, logger, global };
|
|
3823
4604
|
}
|
|
@@ -3846,6 +4627,20 @@ async function handle(fn) {
|
|
|
3846
4627
|
function createProgram() {
|
|
3847
4628
|
const program2 = new Command().name("wp").description("WoodsCLI \u2014 HubSpot CMS dev kit by WoodsPortal (modules + themes)").version(readVersion()).addHelpText("after", "\nWoodsCLI \xB7 WoodsPortal HubSpot CLI \xB7 npm @woodsportal/hubspot-kit");
|
|
3848
4629
|
attachGlobals(program2);
|
|
4630
|
+
program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
4631
|
+
const global = program2.opts();
|
|
4632
|
+
const name = actionCommand.name();
|
|
4633
|
+
if (name === "version" || name === "help") {
|
|
4634
|
+
return;
|
|
4635
|
+
}
|
|
4636
|
+
await maybeWarnKitUpdate({
|
|
4637
|
+
current: readKitVersion(),
|
|
4638
|
+
projectRoot: resolveProjectRootForVersionCheck(global.project),
|
|
4639
|
+
ci: global.ci,
|
|
4640
|
+
json: global.json,
|
|
4641
|
+
quiet: global.quiet
|
|
4642
|
+
});
|
|
4643
|
+
});
|
|
3849
4644
|
program2.command("doctor").description("Check prerequisites (Node, HubSpot CLI, .wphs workspace, config)").action(async () => {
|
|
3850
4645
|
const global = program2.opts();
|
|
3851
4646
|
await handle(() => runDoctor(buildContext(global)));
|
|
@@ -3864,6 +4659,12 @@ function createProgram() {
|
|
|
3864
4659
|
await handle(() => runConfigValidate(buildContext(global)));
|
|
3865
4660
|
});
|
|
3866
4661
|
program2.command("init").description("Scaffold a new module or theme project").option("-t, --template <type>", "module-hybrid-cdn | module-monolith | theme-starter").option("-n, --name <slug>", "Project name (creates ./<name>/ under cwd)").option("-d, --directory <path>", "Full target directory (skips name \u2192 folder prompt)").option("--router <mode>", "hash (client-frontend default) | browser | none \u2014 modules only").option(
|
|
4662
|
+
"--auth <tier>",
|
|
4663
|
+
"Portal auth: none | starter | sso | full \u2014 modules with router \u2260 none"
|
|
4664
|
+
).option(
|
|
4665
|
+
"--nav <mode>",
|
|
4666
|
+
"Authenticated nav shell: top | sidebar (requires --auth starter|sso|full)"
|
|
4667
|
+
).option(
|
|
3867
4668
|
"--cdn-github <org/repo>",
|
|
3868
4669
|
"Public GitHub CDN mirror repo (hybrid CDN; use with --ci / -y)"
|
|
3869
4670
|
).action(
|
|
@@ -3882,12 +4683,16 @@ function createProgram() {
|
|
|
3882
4683
|
global
|
|
3883
4684
|
};
|
|
3884
4685
|
const routerMode = opts.router === "browser" || opts.router === "none" || opts.router === "hash" ? opts.router : void 0;
|
|
4686
|
+
const authMode = opts.auth === "none" || opts.auth === "starter" || opts.auth === "sso" || opts.auth === "full" ? opts.auth : void 0;
|
|
4687
|
+
const navMode = opts.nav === "top" || opts.nav === "sidebar" ? opts.nav : void 0;
|
|
3885
4688
|
await handle(
|
|
3886
4689
|
() => runInit(ctx, {
|
|
3887
4690
|
template: opts.template,
|
|
3888
4691
|
name: opts.name,
|
|
3889
4692
|
directory: opts.directory,
|
|
3890
4693
|
router: routerMode,
|
|
4694
|
+
auth: authMode,
|
|
4695
|
+
nav: navMode,
|
|
3891
4696
|
cdnGithub: opts.cdnGithub
|
|
3892
4697
|
})
|
|
3893
4698
|
);
|
|
@@ -4020,6 +4825,45 @@ function createProgram() {
|
|
|
4020
4825
|
const global = program2.opts();
|
|
4021
4826
|
await handle(() => runPortalUse(buildContext(global), { name }));
|
|
4022
4827
|
});
|
|
4828
|
+
const page = program2.command("page").description("Scaffold routed pages");
|
|
4829
|
+
page.command("add [slug]").description("Create a route file + page component").option("--path <path>", "URL path (default: /<slug>)").option("--title <title>", "Page heading").option("--public", "Public route (no sign-in)").option("--protected", "Protected route (requires auth)").option("--nav", "Add link to navigation menu (portal-nav.ts or starter-nav.ts)").option("--no-nav", "Skip navigation menu").action(
|
|
4830
|
+
async (slug, opts) => {
|
|
4831
|
+
const global = program2.opts();
|
|
4832
|
+
await handle(
|
|
4833
|
+
() => runPageAdd(buildContext(global), {
|
|
4834
|
+
slug,
|
|
4835
|
+
path: opts.path,
|
|
4836
|
+
title: opts.title,
|
|
4837
|
+
isPublic: opts.public,
|
|
4838
|
+
isProtected: opts.protected,
|
|
4839
|
+
addToNav: opts.nav,
|
|
4840
|
+
skipNav: opts.noNav
|
|
4841
|
+
})
|
|
4842
|
+
);
|
|
4843
|
+
}
|
|
4844
|
+
);
|
|
4845
|
+
page.command("list").alias("ls").description("List scaffolded pages under src/features/pages/").action(async () => {
|
|
4846
|
+
const global = program2.opts();
|
|
4847
|
+
await handle(() => runPageList(buildContext(global)));
|
|
4848
|
+
});
|
|
4849
|
+
page.command("remove <slug>").description("Remove a scaffolded page (route, component, routes.ts, nav)").option("--no-nav", "Leave navigation menu unchanged").action(async (slug, opts) => {
|
|
4850
|
+
const global = program2.opts();
|
|
4851
|
+
await handle(
|
|
4852
|
+
() => runPageRemove(buildContext(global), { slug, skipNav: opts.noNav })
|
|
4853
|
+
);
|
|
4854
|
+
});
|
|
4855
|
+
const scaffold = program2.command("scaffold").description("Re-apply portal scaffold layers");
|
|
4856
|
+
const scaffoldAuth = scaffold.command("auth").description("Portal auth tier upgrades");
|
|
4857
|
+
scaffoldAuth.command("upgrade").description("Upgrade portal auth tier on an existing module (starter \u2192 sso \u2192 full)").requiredOption("--tier <tier>", "starter | sso | full").action(async (opts) => {
|
|
4858
|
+
const global = program2.opts();
|
|
4859
|
+
const tier = parseScaffoldAuthTierArg(opts.tier);
|
|
4860
|
+
await handle(async () => runScaffoldAuthUpgrade(buildContext(global), { tier }));
|
|
4861
|
+
});
|
|
4862
|
+
scaffold.command("nav").description("Re-apply authenticated navigation shell (top | sidebar)").requiredOption("--mode <mode>", "top | sidebar").action(async (opts) => {
|
|
4863
|
+
const global = program2.opts();
|
|
4864
|
+
const mode = parseScaffoldNavModeArg(opts.mode);
|
|
4865
|
+
await handle(async () => runScaffoldNav(buildContext(global), { mode }));
|
|
4866
|
+
});
|
|
4023
4867
|
return program2;
|
|
4024
4868
|
}
|
|
4025
4869
|
|