bsmnt 0.0.2 → 0.1.1
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/.github/workflows/release.yml +2 -0
- package/CHANGELOG.md +31 -0
- package/CLAUDE.md +42 -24
- package/README.md +33 -12
- package/biome.json +1 -0
- package/bun.lock +2 -1
- package/docs/architecture.drawio +250 -0
- package/docs/architecture.mermaid +85 -0
- package/package.json +42 -42
- package/{bin → packages/cli/bin}/index.js +28 -31
- package/packages/cli/package.json +16 -0
- package/{src → packages/cli/src}/commands/add-integration.js +22 -37
- package/{src → packages/cli/src}/commands/create.js +125 -131
- package/packages/create-basement-app/integrations/sanity/config.js +46 -0
- package/{src → packages/create-basement-app/integrations/sanity}/mergers/check-integration-merger.js +1 -1
- package/{src → packages/create-basement-app/integrations/sanity}/mergers/layout-merger.js +1 -1
- package/{src → packages/create-basement-app/integrations/sanity}/mergers/sitemap-merger.js +1 -1
- package/packages/create-basement-app/package.json +10 -0
- package/packages/create-basement-app/src/configs/animations.js +28 -0
- package/packages/create-basement-app/src/index.js +15 -0
- package/packages/create-basement-app/src/mergers/check-integration-merger.js +105 -0
- package/{src → packages/create-basement-app/src}/mergers/config.js +5 -33
- package/{src → packages/create-basement-app/src}/mergers/index.js +89 -98
- package/packages/create-basement-app/src/mergers/layout-merger.js +223 -0
- package/packages/create-basement-app/src/mergers/sitemap-merger.js +121 -0
- package/packages/create-basement-app/template-hooks/config.js +38 -0
- package/packages/create-basement-app/templates/default/.biome/plugins/README.md +21 -0
- package/packages/create-basement-app/templates/default/.biome/plugins/no-anchor-element.grit +12 -0
- package/packages/create-basement-app/templates/default/.biome/plugins/no-relative-parent-imports.grit +10 -0
- package/packages/create-basement-app/templates/default/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
- package/packages/create-basement-app/templates/default/.cursor/rules/README.md +184 -0
- package/packages/create-basement-app/templates/default/.cursor/rules/architecture.mdc +437 -0
- package/packages/create-basement-app/templates/default/.cursor/rules/components.mdc +436 -0
- package/packages/create-basement-app/templates/default/.cursor/rules/integrations.mdc +447 -0
- package/packages/create-basement-app/templates/default/.cursor/rules/main.mdc +278 -0
- package/packages/create-basement-app/templates/default/.cursor/rules/styling.mdc +433 -0
- package/packages/create-basement-app/templates/default/.editorconfig +40 -0
- package/packages/create-basement-app/templates/default/.env.example +81 -0
- package/packages/create-basement-app/templates/default/.gitattributes +19 -0
- package/packages/create-basement-app/templates/default/.github/PULL_REQUEST_TEMPLATE.md +14 -0
- package/packages/create-basement-app/templates/default/.github/workflows/lighthouse-to-slack.yml +136 -0
- package/packages/create-basement-app/templates/default/.vscode/extensions.json +20 -0
- package/packages/create-basement-app/templates/default/.vscode/settings.json +105 -0
- package/packages/create-basement-app/templates/default/README.md +221 -0
- package/packages/create-basement-app/templates/default/_gitignore +67 -0
- package/packages/create-basement-app/templates/default/app/favicon.ico +0 -0
- package/packages/create-basement-app/templates/default/app/layout.tsx +104 -0
- package/packages/create-basement-app/templates/default/app/page.tsx +275 -0
- package/packages/create-basement-app/templates/default/app/robots.ts +15 -0
- package/packages/create-basement-app/templates/default/app/sitemap.ts +16 -0
- package/packages/create-basement-app/templates/default/biome.json +250 -0
- package/packages/create-basement-app/templates/default/components/basement.svg +1 -0
- package/packages/create-basement-app/templates/default/components/layout/footer/index.tsx +27 -0
- package/packages/create-basement-app/templates/default/components/layout/header/index.tsx +11 -0
- package/packages/create-basement-app/templates/default/components/layout/theme/index.tsx +66 -0
- package/packages/create-basement-app/templates/default/components/layout/wrapper/index.tsx +65 -0
- package/packages/create-basement-app/templates/default/components/ui/README.md +77 -0
- package/packages/create-basement-app/templates/default/components/ui/image/README.md +37 -0
- package/packages/create-basement-app/templates/default/components/ui/image/index.tsx +224 -0
- package/packages/create-basement-app/templates/default/components/ui/link/index.tsx +146 -0
- package/packages/create-basement-app/templates/default/lib/README.md +33 -0
- package/packages/create-basement-app/templates/default/lib/hooks/index.ts +12 -0
- package/packages/create-basement-app/templates/default/lib/hooks/use-device-detection.ts +81 -0
- package/packages/create-basement-app/templates/default/lib/hooks/use-media-breakpoint.ts +15 -0
- package/packages/create-basement-app/templates/default/lib/hooks/use-prefetch.ts +74 -0
- package/packages/create-basement-app/templates/default/lib/scripts/dev.ts +52 -0
- package/packages/create-basement-app/templates/default/lib/scripts/generate-component.ts +322 -0
- package/packages/create-basement-app/templates/default/lib/scripts/generate-page.ts +193 -0
- package/packages/create-basement-app/templates/default/lib/scripts/generate.ts +79 -0
- package/packages/create-basement-app/templates/default/lib/scripts/utils.ts +246 -0
- package/packages/create-basement-app/templates/default/lib/store/app.ts +11 -0
- package/packages/create-basement-app/templates/default/lib/store/index.ts +11 -0
- package/packages/create-basement-app/templates/default/lib/styles/README.md +64 -0
- package/packages/create-basement-app/templates/default/lib/styles/cn.ts +7 -0
- package/packages/create-basement-app/templates/default/lib/styles/colors.ts +63 -0
- package/packages/create-basement-app/templates/default/lib/styles/config.ts +34 -0
- package/packages/create-basement-app/templates/default/lib/styles/css/global.css +85 -0
- package/packages/create-basement-app/templates/default/lib/styles/css/index.css +6 -0
- package/packages/create-basement-app/templates/default/lib/styles/css/reset.css +166 -0
- package/packages/create-basement-app/templates/default/lib/styles/css/root.css +68 -0
- package/packages/create-basement-app/templates/default/lib/styles/css/tailwind.css +132 -0
- package/packages/create-basement-app/templates/default/lib/styles/easings.ts +21 -0
- package/packages/create-basement-app/templates/default/lib/styles/fonts.ts +28 -0
- package/packages/create-basement-app/templates/default/lib/styles/index.ts +12 -0
- package/packages/create-basement-app/templates/default/lib/styles/layout.mjs +27 -0
- package/packages/create-basement-app/templates/default/lib/styles/scripts/README.md +29 -0
- package/packages/create-basement-app/templates/default/lib/styles/scripts/generate-root.ts +57 -0
- package/packages/create-basement-app/templates/default/lib/styles/scripts/generate-tailwind.ts +162 -0
- package/packages/create-basement-app/templates/default/lib/styles/scripts/postcss-functions.mjs +168 -0
- package/packages/create-basement-app/templates/default/lib/styles/scripts/setup-styles.ts +24 -0
- package/packages/create-basement-app/templates/default/lib/styles/scripts/utils.ts +20 -0
- package/packages/create-basement-app/templates/default/lib/styles/typography.ts +36 -0
- package/packages/create-basement-app/templates/default/lib/utils/README.md +40 -0
- package/packages/create-basement-app/templates/default/lib/utils/css.d.ts +21 -0
- package/packages/create-basement-app/templates/default/lib/utils/easings.ts +240 -0
- package/packages/create-basement-app/templates/default/lib/utils/fetch.ts +84 -0
- package/packages/create-basement-app/templates/default/lib/utils/math.test.ts +221 -0
- package/packages/create-basement-app/templates/default/lib/utils/math.ts +236 -0
- package/packages/create-basement-app/templates/default/lib/utils/metadata.ts +126 -0
- package/packages/create-basement-app/templates/default/lib/utils/strings.test.ts +166 -0
- package/packages/create-basement-app/templates/default/lib/utils/strings.ts +246 -0
- package/packages/create-basement-app/templates/default/lib/utils/types.d.ts +15 -0
- package/packages/create-basement-app/templates/default/lib/utils/viewport.test.ts +256 -0
- package/packages/create-basement-app/templates/default/lib/utils/viewport.ts +193 -0
- package/packages/create-basement-app/templates/default/next.config.ts +142 -0
- package/packages/create-basement-app/templates/default/package.json +62 -0
- package/packages/create-basement-app/templates/default/postcss.config.mjs +42 -0
- package/packages/create-basement-app/templates/default/public/fonts/geist/Geist-Mono.woff2 +0 -0
- package/packages/create-basement-app/templates/default/tsconfig.json +43 -0
- package/packages/create-basement-app/templates/experiment/.biome/plugins/README.md +21 -0
- package/packages/create-basement-app/templates/experiment/.biome/plugins/no-anchor-element.grit +12 -0
- package/packages/create-basement-app/templates/experiment/.biome/plugins/no-relative-parent-imports.grit +10 -0
- package/packages/create-basement-app/templates/experiment/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
- package/packages/create-basement-app/templates/experiment/.cursor/rules/README.md +184 -0
- package/packages/create-basement-app/templates/experiment/.cursor/rules/architecture.mdc +437 -0
- package/packages/create-basement-app/templates/experiment/.cursor/rules/components.mdc +436 -0
- package/packages/create-basement-app/templates/experiment/.cursor/rules/integrations.mdc +447 -0
- package/packages/create-basement-app/templates/experiment/.cursor/rules/main.mdc +278 -0
- package/packages/create-basement-app/templates/experiment/.cursor/rules/styling.mdc +433 -0
- package/packages/create-basement-app/templates/experiment/.editorconfig +40 -0
- package/packages/create-basement-app/templates/experiment/.env.example +81 -0
- package/packages/create-basement-app/templates/experiment/.gitattributes +19 -0
- package/packages/create-basement-app/templates/experiment/.github/PULL_REQUEST_TEMPLATE.md +14 -0
- package/packages/create-basement-app/templates/experiment/.github/workflows/lighthouse-to-slack.yml +136 -0
- package/packages/create-basement-app/templates/experiment/.vscode/extensions.json +20 -0
- package/packages/create-basement-app/templates/experiment/.vscode/settings.json +105 -0
- package/packages/create-basement-app/templates/experiment/README.md +221 -0
- package/packages/create-basement-app/templates/experiment/_gitignore +67 -0
- package/packages/create-basement-app/templates/experiment/app/favicon.ico +0 -0
- package/packages/create-basement-app/templates/experiment/app/layout.tsx +104 -0
- package/packages/create-basement-app/templates/experiment/app/page.tsx +275 -0
- package/packages/create-basement-app/templates/experiment/app/robots.ts +15 -0
- package/packages/create-basement-app/templates/experiment/app/sitemap.ts +16 -0
- package/packages/create-basement-app/templates/experiment/biome.json +250 -0
- package/packages/create-basement-app/templates/experiment/components/basement.svg +1 -0
- package/packages/create-basement-app/templates/experiment/components/layout/footer/index.tsx +27 -0
- package/packages/create-basement-app/templates/experiment/components/layout/header/index.tsx +58 -0
- package/packages/create-basement-app/templates/experiment/components/layout/navigation-menu.tsx +127 -0
- package/packages/create-basement-app/templates/experiment/components/layout/theme/index.tsx +66 -0
- package/packages/create-basement-app/templates/experiment/components/layout/wrapper/index.tsx +65 -0
- package/packages/create-basement-app/templates/experiment/components/ui/README.md +77 -0
- package/packages/create-basement-app/templates/experiment/components/ui/image/README.md +37 -0
- package/packages/create-basement-app/templates/experiment/components/ui/image/index.tsx +224 -0
- package/packages/create-basement-app/templates/experiment/components/ui/link/index.tsx +146 -0
- package/packages/create-basement-app/templates/experiment/lib/README.md +33 -0
- package/packages/create-basement-app/templates/experiment/lib/constants.ts +12 -0
- package/packages/create-basement-app/templates/experiment/lib/hooks/index.ts +12 -0
- package/packages/create-basement-app/templates/experiment/lib/hooks/use-device-detection.ts +81 -0
- package/packages/create-basement-app/templates/experiment/lib/hooks/use-media-breakpoint.ts +15 -0
- package/packages/create-basement-app/templates/experiment/lib/hooks/use-prefetch.ts +74 -0
- package/packages/create-basement-app/templates/experiment/lib/integrations/.gitkeep +0 -0
- package/packages/create-basement-app/templates/experiment/lib/scripts/dev.ts +52 -0
- package/packages/create-basement-app/templates/experiment/lib/scripts/generate-component.ts +322 -0
- package/packages/create-basement-app/templates/experiment/lib/scripts/generate-page.ts +193 -0
- package/packages/create-basement-app/templates/experiment/lib/scripts/generate.ts +79 -0
- package/packages/create-basement-app/templates/experiment/lib/scripts/utils.ts +246 -0
- package/packages/create-basement-app/templates/experiment/lib/store/app.ts +11 -0
- package/packages/create-basement-app/templates/experiment/lib/store/index.ts +11 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/README.md +64 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/cn.ts +7 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/colors.ts +63 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/config.ts +34 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/css/global.css +85 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/css/index.css +6 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/css/reset.css +166 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/css/root.css +68 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/css/tailwind.css +132 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/easings.ts +21 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/fonts.ts +28 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/index.ts +12 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/layout.mjs +27 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/scripts/README.md +29 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/scripts/generate-root.ts +57 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/scripts/generate-tailwind.ts +162 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/scripts/postcss-functions.mjs +168 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/scripts/setup-styles.ts +24 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/scripts/utils.ts +20 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/typography.ts +36 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/README.md +40 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/css.d.ts +21 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/easings.ts +240 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/fetch.ts +84 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/math.test.ts +221 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/math.ts +236 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/metadata.ts +126 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/strings.test.ts +166 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/strings.ts +246 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/types.d.ts +15 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/viewport.test.ts +256 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/viewport.ts +193 -0
- package/packages/create-basement-app/templates/experiment/next.config.ts +142 -0
- package/packages/create-basement-app/templates/experiment/package.json +69 -0
- package/packages/create-basement-app/templates/experiment/postcss.config.mjs +42 -0
- package/packages/create-basement-app/templates/experiment/public/fonts/geist/Geist-Mono.woff2 +0 -0
- package/packages/create-basement-app/templates/experiment/tsconfig.json +43 -0
- package/packages/create-basement-app/templates/webgl/.biome/plugins/README.md +21 -0
- package/packages/create-basement-app/templates/webgl/.biome/plugins/no-anchor-element.grit +12 -0
- package/packages/create-basement-app/templates/webgl/.biome/plugins/no-relative-parent-imports.grit +10 -0
- package/packages/create-basement-app/templates/webgl/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
- package/packages/create-basement-app/templates/webgl/.cursor/rules/README.md +184 -0
- package/packages/create-basement-app/templates/webgl/.cursor/rules/architecture.mdc +437 -0
- package/packages/create-basement-app/templates/webgl/.cursor/rules/components.mdc +436 -0
- package/packages/create-basement-app/templates/webgl/.cursor/rules/integrations.mdc +447 -0
- package/packages/create-basement-app/templates/webgl/.cursor/rules/main.mdc +278 -0
- package/packages/create-basement-app/templates/webgl/.cursor/rules/styling.mdc +433 -0
- package/packages/create-basement-app/templates/webgl/.editorconfig +40 -0
- package/packages/create-basement-app/templates/webgl/.env.example +81 -0
- package/packages/create-basement-app/templates/webgl/.gitattributes +19 -0
- package/packages/create-basement-app/templates/webgl/.github/PULL_REQUEST_TEMPLATE.md +14 -0
- package/packages/create-basement-app/templates/webgl/.github/workflows/lighthouse-to-slack.yml +136 -0
- package/packages/create-basement-app/templates/webgl/.vscode/extensions.json +20 -0
- package/packages/create-basement-app/templates/webgl/.vscode/settings.json +105 -0
- package/packages/create-basement-app/templates/webgl/README.md +221 -0
- package/packages/create-basement-app/templates/webgl/_gitignore +67 -0
- package/packages/create-basement-app/templates/webgl/app/favicon.ico +0 -0
- package/packages/create-basement-app/templates/webgl/app/layout.tsx +104 -0
- package/packages/create-basement-app/templates/webgl/app/page.tsx +10 -0
- package/packages/create-basement-app/templates/webgl/app/robots.ts +15 -0
- package/packages/create-basement-app/templates/webgl/app/sitemap.ts +16 -0
- package/packages/create-basement-app/templates/webgl/biome.json +250 -0
- package/packages/create-basement-app/templates/webgl/components/basement.svg +1 -0
- package/packages/create-basement-app/templates/webgl/components/layout/footer/index.tsx +27 -0
- package/packages/create-basement-app/templates/webgl/components/layout/header/index.tsx +11 -0
- package/packages/create-basement-app/templates/webgl/components/layout/theme/index.tsx +66 -0
- package/packages/create-basement-app/templates/webgl/components/layout/wrapper/index.tsx +65 -0
- package/packages/create-basement-app/templates/webgl/components/ui/README.md +77 -0
- package/packages/create-basement-app/templates/webgl/components/ui/image/README.md +37 -0
- package/packages/create-basement-app/templates/webgl/components/ui/image/index.tsx +224 -0
- package/packages/create-basement-app/templates/webgl/components/ui/link/index.tsx +146 -0
- package/packages/create-basement-app/templates/webgl/components/webgl/canvas/dynamic.tsx +34 -0
- package/packages/create-basement-app/templates/webgl/components/webgl/canvas/index.tsx +38 -0
- package/packages/create-basement-app/templates/webgl/components/webgl/components/scene/index.tsx +29 -0
- package/packages/create-basement-app/templates/webgl/lib/README.md +33 -0
- package/packages/create-basement-app/templates/webgl/lib/hooks/index.ts +12 -0
- package/packages/create-basement-app/templates/webgl/lib/hooks/use-device-detection.ts +81 -0
- package/packages/create-basement-app/templates/webgl/lib/hooks/use-media-breakpoint.ts +15 -0
- package/packages/create-basement-app/templates/webgl/lib/hooks/use-prefetch.ts +74 -0
- package/packages/create-basement-app/templates/webgl/lib/integrations/.gitkeep +0 -0
- package/packages/create-basement-app/templates/webgl/lib/renderer.ts +7 -0
- package/packages/create-basement-app/templates/webgl/lib/scripts/dev.ts +52 -0
- package/packages/create-basement-app/templates/webgl/lib/scripts/generate-component.ts +322 -0
- package/packages/create-basement-app/templates/webgl/lib/scripts/generate-page.ts +193 -0
- package/packages/create-basement-app/templates/webgl/lib/scripts/generate.ts +79 -0
- package/packages/create-basement-app/templates/webgl/lib/scripts/utils.ts +246 -0
- package/packages/create-basement-app/templates/webgl/lib/store/app.ts +11 -0
- package/packages/create-basement-app/templates/webgl/lib/store/index.ts +11 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/README.md +64 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/cn.ts +7 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/colors.ts +63 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/config.ts +34 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/css/global.css +85 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/css/index.css +6 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/css/reset.css +166 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/css/root.css +68 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/css/tailwind.css +132 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/easings.ts +21 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/fonts.ts +28 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/index.ts +12 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/layout.mjs +27 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/scripts/README.md +29 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/scripts/generate-root.ts +57 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/scripts/generate-tailwind.ts +162 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/scripts/postcss-functions.mjs +168 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/scripts/setup-styles.ts +24 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/scripts/utils.ts +20 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/typography.ts +36 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/README.md +40 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/css.d.ts +21 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/easings.ts +240 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/fetch.ts +84 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/math.test.ts +221 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/math.ts +236 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/metadata.ts +126 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/strings.test.ts +166 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/strings.ts +246 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/types.d.ts +15 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/viewport.test.ts +256 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/viewport.ts +193 -0
- package/packages/create-basement-app/templates/webgl/next.config.ts +142 -0
- package/packages/create-basement-app/templates/webgl/package.json +68 -0
- package/packages/create-basement-app/templates/webgl/postcss.config.mjs +42 -0
- package/packages/create-basement-app/templates/webgl/public/fonts/geist/Geist-Mono.woff2 +0 -0
- package/packages/create-basement-app/templates/webgl/tsconfig.json +43 -0
- package/tasks/.last-branch +1 -0
- package/tasks/CLAUDE.md +104 -0
- package/tasks/archive/2026-02-09-next-starter-dynamic-layers/prd.json +153 -0
- package/tasks/archive/2026-02-09-next-starter-dynamic-layers/progress.txt +115 -0
- package/tasks/prd-project-restructure.md +375 -0
- package/tasks/prd.json +227 -91
- package/tasks/progress.txt +281 -87
- package/tasks/ralph.sh +113 -0
- package/integrations/basehub/README.md +0 -3
- package/layers/experiment/components/layout/header/index.tsx +0 -58
- package/layers/experiment/components/layout/navigation-menu.tsx +0 -127
- package/layers/experiment/lib/constants.ts +0 -12
- package/layers/webgl/app/page.tsx +0 -10
- package/layers/webgl/components/webgl/canvas/dynamic.tsx +0 -34
- package/layers/webgl/components/webgl/canvas/index.tsx +0 -43
- package/layers/webgl/components/webgl/components/scene/index.tsx +0 -21
- package/src/mergers/next-config-merger.js +0 -63
- /package/{src → packages/cli/src}/commands/setup-sanity.js +0 -0
- /package/{src → packages/cli/src}/commands/worktree.js +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/api/draft-mode/disable/route.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/api/draft-mode/enable/route.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/api/revalidate/route.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/layout.tsx +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/sitemap.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/studio/[[...tool]]/page.tsx +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/studio/layout.tsx +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/components/ui/sanity-image/index.tsx +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/README.md +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/check-integration.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/README.md +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/client.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/components/disable-draft-mode.tsx +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/components/rich-text.tsx +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/env.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/live/index.tsx +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/queries.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/sanity.cli.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/sanity.config.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/sanity.types.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schema.json +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/article.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/example.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/index.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/link.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/metadata.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/navigation.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/page.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/richText.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/structure.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/utils/image.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/utils/link.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/scripts/copy-sanity-mcp.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/scripts/generate-page.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/utils/metadata.ts +0 -0
- /package/{plugins → packages/create-basement-app/plugins}/README.md +0 -0
- /package/{plugins → packages/create-basement-app/plugins}/no-anchor-element.grit +0 -0
- /package/{plugins → packages/create-basement-app/plugins}/no-relative-parent-imports.grit +0 -0
- /package/{plugins → packages/create-basement-app/plugins}/no-unnecessary-forwardref.grit +0 -0
- /package/{template-hooks → packages/create-basement-app/template-hooks}/use-battery.ts +0 -0
- /package/{template-hooks → packages/create-basement-app/template-hooks}/use-device-perf.ts +0 -0
- /package/{template-hooks → packages/create-basement-app/template-hooks}/use-intersection-observer.ts +0 -0
- /package/{template-hooks → packages/create-basement-app/template-hooks}/use-media.ts +0 -0
- /package/{layers/webgpu → packages/create-basement-app/templates/default/lib/integrations}/.gitkeep +0 -0
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
// src/mergers/index.js
|
|
1
|
+
// packages/create-basement-app/src/mergers/index.js
|
|
2
2
|
|
|
3
|
-
import os from "node:os";
|
|
4
3
|
import path from "node:path";
|
|
5
4
|
import { fileURLToPath } from "node:url";
|
|
6
5
|
import fs from "fs-extra";
|
|
7
|
-
import tiged from "tiged";
|
|
8
6
|
import { getCmsConfig, getLayerConfig } from "./config.js";
|
|
9
7
|
|
|
10
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -17,16 +15,17 @@ const __dirname = path.dirname(__filename);
|
|
|
17
15
|
const CMS_MERGERS = {
|
|
18
16
|
sanity: {
|
|
19
17
|
"app/layout.tsx": () =>
|
|
20
|
-
import("
|
|
18
|
+
import("../../integrations/sanity/mergers/layout-merger.js").then(
|
|
19
|
+
(m) => m.mergeLayout,
|
|
20
|
+
),
|
|
21
21
|
"app/sitemap.ts": () =>
|
|
22
|
-
import("
|
|
23
|
-
|
|
24
|
-
import("./check-integration-merger.js").then(
|
|
25
|
-
(m) => m.mergeCheckIntegration,
|
|
22
|
+
import("../../integrations/sanity/mergers/sitemap-merger.js").then(
|
|
23
|
+
(m) => m.mergeSitemap,
|
|
26
24
|
),
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
"lib/integrations/check-integration.ts": () =>
|
|
26
|
+
import(
|
|
27
|
+
"../../integrations/sanity/mergers/check-integration-merger.js"
|
|
28
|
+
).then((m) => m.mergeCheckIntegration),
|
|
30
29
|
},
|
|
31
30
|
};
|
|
32
31
|
|
|
@@ -90,7 +89,7 @@ async function getMerger(cms, basePath) {
|
|
|
90
89
|
/**
|
|
91
90
|
* Inject CMS integration using smart merge
|
|
92
91
|
* @param {string} targetDir - The project directory
|
|
93
|
-
* @param {string} cms - The CMS name (e.g., 'sanity'
|
|
92
|
+
* @param {string} cms - The CMS name (e.g., 'sanity')
|
|
94
93
|
* @param {object} spinner - Ora spinner for progress
|
|
95
94
|
* @returns {object} Results of the merge operation
|
|
96
95
|
*/
|
|
@@ -108,12 +107,12 @@ export async function injectIntegration(targetDir, cms, spinner) {
|
|
|
108
107
|
if (!cmsConfig) {
|
|
109
108
|
results.failed.push({
|
|
110
109
|
path: cms,
|
|
111
|
-
error: `CMS "${cms}" is not supported. Supported: sanity
|
|
110
|
+
error: `CMS "${cms}" is not supported. Supported: sanity`,
|
|
112
111
|
});
|
|
113
112
|
return results;
|
|
114
113
|
}
|
|
115
114
|
|
|
116
|
-
const {
|
|
115
|
+
const { mergeFiles, additivePaths } = cmsConfig;
|
|
117
116
|
|
|
118
117
|
// Detect if project uses src/ prefix (e.g., src/app/ instead of app/)
|
|
119
118
|
const pathPrefix = await detectPathPrefix(targetDir);
|
|
@@ -121,111 +120,103 @@ export async function injectIntegration(targetDir, cms, spinner) {
|
|
|
121
120
|
spinner.text = `Detected src/ directory structure...`;
|
|
122
121
|
}
|
|
123
122
|
|
|
124
|
-
//
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
123
|
+
// Resolve local integration files directory
|
|
124
|
+
const integrationDir = path.resolve(
|
|
125
|
+
__dirname,
|
|
126
|
+
"../../integrations",
|
|
127
|
+
cms,
|
|
128
|
+
"files",
|
|
129
|
+
);
|
|
129
130
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
force: true,
|
|
135
|
-
mode: "git",
|
|
131
|
+
if (!(await fs.pathExists(integrationDir))) {
|
|
132
|
+
results.failed.push({
|
|
133
|
+
path: cms,
|
|
134
|
+
error: `Integration directory not found: ${integrationDir}`,
|
|
136
135
|
});
|
|
136
|
+
return results;
|
|
137
|
+
}
|
|
137
138
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
// 2. Copy additive files/directories (CMS-specific paths)
|
|
141
|
-
spinner.text = `Adding ${cms} files...`;
|
|
139
|
+
// 1. Copy additive files/directories (CMS-specific paths)
|
|
140
|
+
spinner.text = `Adding ${cms} files...`;
|
|
142
141
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
142
|
+
for (const additivePath of additivePaths) {
|
|
143
|
+
const src = path.join(integrationDir, additivePath);
|
|
144
|
+
// Transform destination path based on project structure
|
|
145
|
+
const transformedPath = transformPath(additivePath, pathPrefix);
|
|
146
|
+
const dest = path.join(targetDir, transformedPath);
|
|
148
147
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
} catch (error) {
|
|
155
|
-
results.failed.push({ path: transformedPath, error: error.message });
|
|
148
|
+
try {
|
|
149
|
+
if (await fs.pathExists(src)) {
|
|
150
|
+
await fs.copy(src, dest, { overwrite: false });
|
|
151
|
+
results.copied.push(transformedPath);
|
|
156
152
|
}
|
|
153
|
+
} catch (error) {
|
|
154
|
+
results.failed.push({ path: transformedPath, error: error.message });
|
|
157
155
|
}
|
|
156
|
+
}
|
|
158
157
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
try {
|
|
170
|
-
// Check if template file exists
|
|
171
|
-
if (!(await fs.pathExists(templateFile))) {
|
|
172
|
-
// Template doesn't have this file - copy from integration
|
|
173
|
-
if (await fs.pathExists(integrationFile)) {
|
|
174
|
-
await fs.ensureDir(path.dirname(templateFile));
|
|
175
|
-
await fs.copy(integrationFile, templateFile);
|
|
176
|
-
results.copied.push(transformedPath);
|
|
177
|
-
} else {
|
|
178
|
-
results.skipped.push({
|
|
179
|
-
path: transformedPath,
|
|
180
|
-
reason: "Not in integration",
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
158
|
+
// 2. Smart merge files that exist in both (CMS-specific mergers)
|
|
159
|
+
if (mergeFiles.length > 0) {
|
|
160
|
+
spinner.text = `Merging ${cms} integration...`;
|
|
161
|
+
|
|
162
|
+
for (const mergeFile of mergeFiles) {
|
|
163
|
+
// Transform path for the target project
|
|
164
|
+
const transformedPath = transformPath(mergeFile, pathPrefix);
|
|
165
|
+
const templateFile = path.join(targetDir, transformedPath);
|
|
166
|
+
const integrationFile = path.join(integrationDir, mergeFile);
|
|
185
167
|
|
|
186
|
-
|
|
187
|
-
|
|
168
|
+
try {
|
|
169
|
+
// Check if template file exists
|
|
170
|
+
if (!(await fs.pathExists(templateFile))) {
|
|
171
|
+
// Template doesn't have this file - copy from integration
|
|
172
|
+
if (await fs.pathExists(integrationFile)) {
|
|
173
|
+
await fs.ensureDir(path.dirname(templateFile));
|
|
174
|
+
await fs.copy(integrationFile, templateFile);
|
|
175
|
+
results.copied.push(transformedPath);
|
|
176
|
+
} else {
|
|
188
177
|
results.skipped.push({
|
|
189
178
|
path: transformedPath,
|
|
190
179
|
reason: "Not in integration",
|
|
191
180
|
});
|
|
192
|
-
continue;
|
|
193
181
|
}
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
194
184
|
|
|
195
|
-
|
|
196
|
-
|
|
185
|
+
// Check if integration file exists
|
|
186
|
+
if (!(await fs.pathExists(integrationFile))) {
|
|
187
|
+
results.skipped.push({
|
|
188
|
+
path: transformedPath,
|
|
189
|
+
reason: "Not in integration",
|
|
190
|
+
});
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
197
193
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
});
|
|
208
|
-
} else {
|
|
209
|
-
results.merged.push(transformedPath);
|
|
210
|
-
}
|
|
211
|
-
} else {
|
|
194
|
+
// Get the CMS-specific merger function (use base path for lookup)
|
|
195
|
+
const merger = await getMerger(cms, mergeFile);
|
|
196
|
+
|
|
197
|
+
if (merger) {
|
|
198
|
+
const result = await merger(templateFile, {
|
|
199
|
+
targetDir,
|
|
200
|
+
pathPrefix,
|
|
201
|
+
});
|
|
202
|
+
if (result.skipped) {
|
|
212
203
|
results.skipped.push({
|
|
213
204
|
path: transformedPath,
|
|
214
|
-
reason:
|
|
205
|
+
reason: result.reason,
|
|
215
206
|
});
|
|
207
|
+
} else {
|
|
208
|
+
results.merged.push(transformedPath);
|
|
216
209
|
}
|
|
217
|
-
}
|
|
218
|
-
results.
|
|
210
|
+
} else {
|
|
211
|
+
results.skipped.push({
|
|
212
|
+
path: transformedPath,
|
|
213
|
+
reason: `No merger for ${cms}`,
|
|
214
|
+
});
|
|
219
215
|
}
|
|
216
|
+
} catch (error) {
|
|
217
|
+
results.failed.push({ path: transformedPath, error: error.message });
|
|
220
218
|
}
|
|
221
219
|
}
|
|
222
|
-
} finally {
|
|
223
|
-
// 4. Cleanup temp directory
|
|
224
|
-
try {
|
|
225
|
-
await fs.remove(tempDir);
|
|
226
|
-
} catch {
|
|
227
|
-
// Ignore cleanup errors
|
|
228
|
-
}
|
|
229
220
|
}
|
|
230
221
|
|
|
231
222
|
return results;
|
|
@@ -253,8 +244,8 @@ export async function injectLayer(targetDir, layer, spinner) {
|
|
|
253
244
|
return results;
|
|
254
245
|
}
|
|
255
246
|
|
|
256
|
-
// Resolve the layer directory from
|
|
257
|
-
const layerDir = path.resolve(__dirname, "../../
|
|
247
|
+
// Resolve the layer directory from create-basement-app's templates/{type}/ path
|
|
248
|
+
const layerDir = path.resolve(__dirname, "../../templates", layer);
|
|
258
249
|
|
|
259
250
|
if (!(await fs.pathExists(layerDir))) {
|
|
260
251
|
results.skipped.push({
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
// packages/create-basement-app/src/mergers/layout-merger.js
|
|
2
|
+
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import fs from "fs-extra";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Sanity-specific imports to inject into layout.
|
|
8
|
+
* Only two lightweight components — no dynamic functions (draftMode, headers, etc.)
|
|
9
|
+
*/
|
|
10
|
+
const SANITY_IMPORTS = `import { SanityStudioGuard } from "@/components/sanity/studio-guard"
|
|
11
|
+
import { SanityVisualEditing } from "@/components/sanity/visual-editing"`;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Client component that hides site chrome on /studio routes.
|
|
15
|
+
* Uses useSelectedLayoutSegment() — a stable Next.js client API.
|
|
16
|
+
*/
|
|
17
|
+
const STUDIO_GUARD_COMPONENT = `"use client"
|
|
18
|
+
|
|
19
|
+
import { useSelectedLayoutSegment } from "next/navigation"
|
|
20
|
+
|
|
21
|
+
export function SanityStudioGuard({ children }: { children: React.ReactNode }) {
|
|
22
|
+
const segment = useSelectedLayoutSegment()
|
|
23
|
+
if (segment === "studio") return null
|
|
24
|
+
return <>{children}</>
|
|
25
|
+
}
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Server component that handles Sanity visual editing.
|
|
30
|
+
* Keeps draftMode() inside its own <Suspense> boundary so the root layout
|
|
31
|
+
* never calls dynamic functions directly — preserving static rendering.
|
|
32
|
+
*/
|
|
33
|
+
const VISUAL_EDITING_COMPONENT = `import { Suspense } from "react"
|
|
34
|
+
import { draftMode } from "next/headers"
|
|
35
|
+
import { VisualEditing } from "next-sanity/visual-editing"
|
|
36
|
+
import { isSanityConfigured } from "@/lib/integrations/check-integration"
|
|
37
|
+
import { SanityLive } from "@/lib/integrations/sanity/live"
|
|
38
|
+
|
|
39
|
+
async function VisualEditingInner() {
|
|
40
|
+
const { isEnabled: isDraftMode } = await draftMode()
|
|
41
|
+
const sanityConfigured = isSanityConfigured()
|
|
42
|
+
|
|
43
|
+
if (!sanityConfigured || !isDraftMode) return null
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<>
|
|
47
|
+
<VisualEditing />
|
|
48
|
+
<SanityLive />
|
|
49
|
+
</>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function SanityVisualEditing() {
|
|
54
|
+
return (
|
|
55
|
+
<Suspense fallback={null}>
|
|
56
|
+
<VisualEditingInner />
|
|
57
|
+
</Suspense>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Merge Sanity integration into template layout.
|
|
64
|
+
* The layout itself stays free of dynamic functions (draftMode, headers, etc.)
|
|
65
|
+
* All dynamic behavior is encapsulated in generated components.
|
|
66
|
+
*
|
|
67
|
+
* Strategy:
|
|
68
|
+
* 1. Inject two imports (SanityStudioGuard, SanityVisualEditing)
|
|
69
|
+
* 2. Wrap body chrome in <SanityStudioGuard>
|
|
70
|
+
* 3. Add <SanityVisualEditing /> before </body>
|
|
71
|
+
* 4. Generate the two component files
|
|
72
|
+
*
|
|
73
|
+
* @param {string} templatePath - Path to the template's layout.tsx
|
|
74
|
+
* @param {object} options - Options from the merge orchestrator
|
|
75
|
+
* @param {string} options.targetDir - The project root directory
|
|
76
|
+
* @param {string} options.pathPrefix - Path prefix (e.g., 'src/' or '')
|
|
77
|
+
* @returns {Promise<{skipped?: boolean, reason?: string, success?: boolean}>}
|
|
78
|
+
*/
|
|
79
|
+
export async function mergeLayout(
|
|
80
|
+
templatePath,
|
|
81
|
+
{ targetDir, pathPrefix } = {},
|
|
82
|
+
) {
|
|
83
|
+
let content = await fs.readFile(templatePath, "utf-8");
|
|
84
|
+
|
|
85
|
+
// Skip if already has Sanity integration
|
|
86
|
+
if (
|
|
87
|
+
content.includes("SanityStudioGuard") ||
|
|
88
|
+
content.includes("isSanityConfigured")
|
|
89
|
+
) {
|
|
90
|
+
return { skipped: true, reason: "Already has Sanity integration" };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 1. Add Sanity imports after existing imports
|
|
94
|
+
const importMatches = [...content.matchAll(/^import .+$/gm)];
|
|
95
|
+
if (importMatches.length > 0) {
|
|
96
|
+
const lastImport = importMatches[importMatches.length - 1];
|
|
97
|
+
const insertPos = lastImport.index + lastImport[0].length;
|
|
98
|
+
content =
|
|
99
|
+
content.slice(0, insertPos) +
|
|
100
|
+
"\n" +
|
|
101
|
+
SANITY_IMPORTS +
|
|
102
|
+
content.slice(insertPos);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 2. Ensure Suspense is imported from react
|
|
106
|
+
if (!content.match(/import\s+.*Suspense.*from\s+["']react["']/)) {
|
|
107
|
+
const reactImportMatch = content.match(
|
|
108
|
+
/import\s+\{([^}]*)\}\s+from\s+["']react["']/,
|
|
109
|
+
);
|
|
110
|
+
if (reactImportMatch) {
|
|
111
|
+
if (!reactImportMatch[1].includes("Suspense")) {
|
|
112
|
+
content = content.replace(
|
|
113
|
+
reactImportMatch[0],
|
|
114
|
+
reactImportMatch[0].replace(
|
|
115
|
+
reactImportMatch[1],
|
|
116
|
+
`${reactImportMatch[1].trim()}, Suspense`,
|
|
117
|
+
),
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
const lastImportMatch = content.match(/^(import\s+.+\n)/gm);
|
|
122
|
+
if (lastImportMatch) {
|
|
123
|
+
const lastImport = lastImportMatch[lastImportMatch.length - 1];
|
|
124
|
+
content = content.replace(
|
|
125
|
+
lastImport,
|
|
126
|
+
`${lastImport}import { Suspense } from "react"\n`,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 3. Wrap body chrome in <SanityStudioGuard> and add <SanityVisualEditing />
|
|
133
|
+
content = content.replace(
|
|
134
|
+
/(\s*)(<body[^>]*>)([\s\S]*?)(\{children\})([\s\S]*?)(<\/body>)/m,
|
|
135
|
+
(
|
|
136
|
+
_match,
|
|
137
|
+
bodyIndent,
|
|
138
|
+
bodyOpen,
|
|
139
|
+
beforeChildren,
|
|
140
|
+
_childrenExpr,
|
|
141
|
+
afterChildren,
|
|
142
|
+
bodyClose,
|
|
143
|
+
) => {
|
|
144
|
+
const baseIndent = bodyIndent || " ";
|
|
145
|
+
const indent = `${baseIndent} `;
|
|
146
|
+
const innerIndent = `${indent} `;
|
|
147
|
+
|
|
148
|
+
const trimmedBefore = beforeChildren.trim();
|
|
149
|
+
const trimmedAfter = afterChildren.trim();
|
|
150
|
+
const hasChrome = trimmedBefore.length > 0 || trimmedAfter.length > 0;
|
|
151
|
+
|
|
152
|
+
// Preserve relative indentation of chrome content
|
|
153
|
+
const reindent = (text, targetIndent) => {
|
|
154
|
+
const lines = text.split("\n").filter((line) => line.trim());
|
|
155
|
+
if (lines.length === 0) return "";
|
|
156
|
+
const minIndent = Math.min(
|
|
157
|
+
...lines
|
|
158
|
+
.map((line) => line.match(/^(\s*)/)[1].length)
|
|
159
|
+
.filter((n) => n > 0),
|
|
160
|
+
Infinity,
|
|
161
|
+
);
|
|
162
|
+
return lines
|
|
163
|
+
.map((line) => {
|
|
164
|
+
const currentIndent = line.match(/^(\s*)/)[1].length;
|
|
165
|
+
const relativeIndent = Math.max(
|
|
166
|
+
0,
|
|
167
|
+
currentIndent - (minIndent === Infinity ? 0 : minIndent),
|
|
168
|
+
);
|
|
169
|
+
return targetIndent + " ".repeat(relativeIndent) + line.trim();
|
|
170
|
+
})
|
|
171
|
+
.join("\n");
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
let result = `${baseIndent}${bodyOpen}\n`;
|
|
175
|
+
|
|
176
|
+
if (hasChrome && trimmedBefore) {
|
|
177
|
+
result += `${indent}<Suspense fallback={null}>\n`;
|
|
178
|
+
result += `${innerIndent}<SanityStudioGuard>\n`;
|
|
179
|
+
result += reindent(trimmedBefore, `${innerIndent} `);
|
|
180
|
+
result += `\n${innerIndent}</SanityStudioGuard>\n`;
|
|
181
|
+
result += `${indent}</Suspense>\n\n`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
result += `${indent}{children}\n`;
|
|
185
|
+
|
|
186
|
+
if (hasChrome && trimmedAfter) {
|
|
187
|
+
result += `\n${indent}<Suspense fallback={null}>\n`;
|
|
188
|
+
result += `${innerIndent}<SanityStudioGuard>\n`;
|
|
189
|
+
result += reindent(trimmedAfter, `${innerIndent} `);
|
|
190
|
+
result += `\n${innerIndent}</SanityStudioGuard>\n`;
|
|
191
|
+
result += `${indent}</Suspense>\n`;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
result += `\n${indent}<SanityVisualEditing />\n${baseIndent}${bodyClose}`;
|
|
195
|
+
return result;
|
|
196
|
+
},
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
await fs.writeFile(templatePath, content);
|
|
200
|
+
|
|
201
|
+
// 3. Generate component files
|
|
202
|
+
if (targetDir) {
|
|
203
|
+
const sanityDir = path.join(
|
|
204
|
+
targetDir,
|
|
205
|
+
pathPrefix || "",
|
|
206
|
+
"components",
|
|
207
|
+
"sanity",
|
|
208
|
+
);
|
|
209
|
+
await fs.ensureDir(sanityDir);
|
|
210
|
+
|
|
211
|
+
const guardPath = path.join(sanityDir, "studio-guard.tsx");
|
|
212
|
+
if (!(await fs.pathExists(guardPath))) {
|
|
213
|
+
await fs.writeFile(guardPath, STUDIO_GUARD_COMPONENT);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const visualEditingPath = path.join(sanityDir, "visual-editing.tsx");
|
|
217
|
+
if (!(await fs.pathExists(visualEditingPath))) {
|
|
218
|
+
await fs.writeFile(visualEditingPath, VISUAL_EDITING_COMPONENT);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return { success: true };
|
|
223
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// packages/create-basement-app/src/mergers/sitemap-merger.js
|
|
2
|
+
import fs from "fs-extra";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Sanity-specific import to add
|
|
6
|
+
*/
|
|
7
|
+
const SANITY_IMPORT = `import { isSanityConfigured } from "@/lib/integrations/check-integration"`;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Sanity fetch logic to inject before return statement
|
|
11
|
+
* Uses early return pattern and spread operator for cleaner code
|
|
12
|
+
*/
|
|
13
|
+
const SANITY_FETCH_LOGIC = `
|
|
14
|
+
// Only fetch Sanity pages if Sanity is configured
|
|
15
|
+
if (isSanityConfigured()) {
|
|
16
|
+
try {
|
|
17
|
+
const sanityModule = await import("@/lib/integrations/sanity/client")
|
|
18
|
+
const sanityGroq = await import("next-sanity")
|
|
19
|
+
|
|
20
|
+
const client = sanityModule?.client
|
|
21
|
+
const groq = sanityGroq?.groq
|
|
22
|
+
|
|
23
|
+
// Skip if client is null (shouldn't happen since we check isSanityConfigured)
|
|
24
|
+
if (!(client && groq)) return baseRoutes
|
|
25
|
+
|
|
26
|
+
type SanityDocument = {
|
|
27
|
+
slug: { current: string }
|
|
28
|
+
_updatedAt: string
|
|
29
|
+
metadata?: { noIndex?: boolean }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Fetch all published pages and articles
|
|
33
|
+
const pages = (await client.fetch(
|
|
34
|
+
groq\`*[_type == "page" && defined(slug.current)] {
|
|
35
|
+
slug,
|
|
36
|
+
_updatedAt,
|
|
37
|
+
metadata
|
|
38
|
+
}\`
|
|
39
|
+
)) as SanityDocument[]
|
|
40
|
+
|
|
41
|
+
const articles = (await client.fetch(
|
|
42
|
+
groq\`*[_type == "article" && defined(slug.current)] {
|
|
43
|
+
slug,
|
|
44
|
+
_updatedAt,
|
|
45
|
+
metadata
|
|
46
|
+
}\`
|
|
47
|
+
)) as SanityDocument[]
|
|
48
|
+
|
|
49
|
+
// Add pages to sitemap (exclude noIndex pages)
|
|
50
|
+
const pageEntries: MetadataRoute.Sitemap = pages
|
|
51
|
+
.filter((page: SanityDocument) => !page.metadata?.noIndex)
|
|
52
|
+
.map((page: SanityDocument) => ({
|
|
53
|
+
url: \`\${APP_BASE_URL}/\${page.slug.current}\`,
|
|
54
|
+
lastModified: new Date(page._updatedAt),
|
|
55
|
+
changeFrequency: "weekly" as const,
|
|
56
|
+
priority: 0.8,
|
|
57
|
+
}))
|
|
58
|
+
|
|
59
|
+
// Add articles to sitemap (exclude noIndex articles)
|
|
60
|
+
const articleEntries: MetadataRoute.Sitemap = articles
|
|
61
|
+
.filter((article: SanityDocument) => !article.metadata?.noIndex)
|
|
62
|
+
.map((article: SanityDocument) => ({
|
|
63
|
+
url: \`\${APP_BASE_URL}/blog/\${article.slug.current}\`,
|
|
64
|
+
lastModified: new Date(article._updatedAt),
|
|
65
|
+
changeFrequency: "weekly" as const,
|
|
66
|
+
priority: 0.7,
|
|
67
|
+
}))
|
|
68
|
+
|
|
69
|
+
return [...baseRoutes, ...pageEntries, ...articleEntries]
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error("Error generating sitemap from Sanity:", error)
|
|
72
|
+
return baseRoutes
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
`;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Merge Sanity integration into template sitemap
|
|
80
|
+
* Preserves all existing agnostic code (routes, baseRoutes structure)
|
|
81
|
+
* and injects Sanity-specific fetching logic
|
|
82
|
+
*
|
|
83
|
+
* @param {string} templatePath - Path to the template's sitemap.ts
|
|
84
|
+
* @returns {Promise<{skipped?: boolean, reason?: string, success?: boolean}>}
|
|
85
|
+
*/
|
|
86
|
+
export async function mergeSitemap(templatePath) {
|
|
87
|
+
let content = await fs.readFile(templatePath, "utf-8");
|
|
88
|
+
|
|
89
|
+
// Skip if already has Sanity integration
|
|
90
|
+
if (content.includes("isSanityConfigured")) {
|
|
91
|
+
return { skipped: true, reason: "Already has Sanity integration" };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 1. Add Sanity import after existing imports
|
|
95
|
+
const importMatches = [...content.matchAll(/^import .+$/gm)];
|
|
96
|
+
if (importMatches.length > 0) {
|
|
97
|
+
const lastImport = importMatches[importMatches.length - 1];
|
|
98
|
+
const insertPos = lastImport.index + lastImport[0].length;
|
|
99
|
+
content =
|
|
100
|
+
content.slice(0, insertPos) +
|
|
101
|
+
"\n" +
|
|
102
|
+
SANITY_IMPORT +
|
|
103
|
+
content.slice(insertPos);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 2. Add Sanity fetch logic before the final return statement
|
|
107
|
+
// Find the last "return baseRoutes" or "return [...baseRoutes" pattern
|
|
108
|
+
const returnMatch = content.match(
|
|
109
|
+
/(\n)([ \t]*)(return\s+(?:baseRoutes|\[\.\.\.baseRoutes))/m,
|
|
110
|
+
);
|
|
111
|
+
if (returnMatch) {
|
|
112
|
+
const returnIndex = content.indexOf(returnMatch[0]);
|
|
113
|
+
content =
|
|
114
|
+
content.slice(0, returnIndex) +
|
|
115
|
+
SANITY_FETCH_LOGIC +
|
|
116
|
+
content.slice(returnIndex);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
await fs.writeFile(templatePath, content);
|
|
120
|
+
return { success: true };
|
|
121
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// packages/create-basement-app/template-hooks/config.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hook configuration manifest.
|
|
5
|
+
* Each entry describes a template hook, its source file, and npm dependencies.
|
|
6
|
+
*/
|
|
7
|
+
export const hooksConfig = [
|
|
8
|
+
{
|
|
9
|
+
name: "use-battery",
|
|
10
|
+
file: "use-battery.ts",
|
|
11
|
+
dependencies: {
|
|
12
|
+
"lodash-es": "^4.17.21",
|
|
13
|
+
},
|
|
14
|
+
devDependencies: {},
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: "use-device-perf",
|
|
18
|
+
file: "use-device-perf.ts",
|
|
19
|
+
dependencies: {
|
|
20
|
+
"detect-gpu": "^5.0.0",
|
|
21
|
+
"react-device-detect": "^2.2.3",
|
|
22
|
+
zustand: "^5.0.0",
|
|
23
|
+
},
|
|
24
|
+
devDependencies: {},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: "use-intersection-observer",
|
|
28
|
+
file: "use-intersection-observer.ts",
|
|
29
|
+
dependencies: {},
|
|
30
|
+
devDependencies: {},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: "use-media",
|
|
34
|
+
file: "use-media.ts",
|
|
35
|
+
dependencies: {},
|
|
36
|
+
devDependencies: {},
|
|
37
|
+
},
|
|
38
|
+
];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
## Plugins
|
|
2
|
+
|
|
3
|
+
### 1. `no-anchor-element.grit`
|
|
4
|
+
Enforces using Next.js `<Link>` component instead of HTML `<a>` elements.
|
|
5
|
+
|
|
6
|
+
### 2. `no-unnecessary-forwardref.grit`
|
|
7
|
+
Checks for unnecessary `forwardRef` usage in React 19 with the compiler.
|
|
8
|
+
|
|
9
|
+
### 3. `no-relative-parent-imports.grit`
|
|
10
|
+
Forbids relative parent imports (`../`) and encourages alias imports (`@/`).
|
|
11
|
+
|
|
12
|
+
## Plugin Configuration
|
|
13
|
+
|
|
14
|
+
The plugins are configured in `biome.json`:
|
|
15
|
+
```json
|
|
16
|
+
"plugins": [
|
|
17
|
+
"./biome-plugins/no-anchor-element.grit",
|
|
18
|
+
"./biome-plugins/no-unnecessary-forwardref.grit",
|
|
19
|
+
"./biome-plugins/no-relative-parent-imports.grit"
|
|
20
|
+
]
|
|
21
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
language js;
|
|
2
|
+
|
|
3
|
+
`<a $attrs>$content</a>` as $anchor where {
|
|
4
|
+
!$anchor <: within `if ($condition) { return ($jsx) }` where {
|
|
5
|
+
$condition <: contains or { `isExternal`, `isExternalSSR` }
|
|
6
|
+
},
|
|
7
|
+
register_diagnostic(
|
|
8
|
+
span = $anchor,
|
|
9
|
+
message = "Use custom Link component instead of <a> element. The Link component handles both internal and external links automatically.",
|
|
10
|
+
severity = "error"
|
|
11
|
+
)
|
|
12
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
language js;
|
|
2
|
+
|
|
3
|
+
`import $imports from $source` as $import where {
|
|
4
|
+
$source <: r"['\"]\.\.\/\.\.\/.*['\"]",
|
|
5
|
+
register_diagnostic(
|
|
6
|
+
span = $import,
|
|
7
|
+
message = "Use alias imports (~/dir/) instead of deep relative imports (../../). Single level imports (../) are allowed for colocated files.",
|
|
8
|
+
severity = "error"
|
|
9
|
+
)
|
|
10
|
+
}
|