create-ailk 0.7.0 → 0.9.0
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/README.md +112 -10
- package/component-catalog.json +798 -49
- package/component-catalog.md +9 -6
- package/dist/add-module.d.ts +20 -0
- package/dist/add-module.js +43 -7
- package/dist/add-route.d.ts +56 -0
- package/dist/add-route.js +286 -0
- package/dist/apply-design-md.d.ts +7 -0
- package/dist/apply-design-md.js +50 -0
- package/dist/apply-design.d.ts +64 -0
- package/dist/apply-design.js +273 -0
- package/dist/claude-md.d.ts +111 -0
- package/dist/claude-md.js +334 -0
- package/dist/cli.js +114 -28
- package/dist/derive-architecture.js +41 -36
- package/dist/design-compile.d.ts +71 -0
- package/dist/design-compile.js +98 -0
- package/dist/design-package.d.ts +83 -0
- package/dist/design-package.js +468 -0
- package/dist/design-presets.d.ts +86 -0
- package/dist/design-presets.js +642 -0
- package/dist/design-scaffold.d.ts +76 -0
- package/dist/design-scaffold.js +220 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -1
- package/dist/legacy-nav-renderers.d.ts +45 -0
- package/dist/legacy-nav-renderers.js +545 -0
- package/dist/lib/auth-secret-file.d.ts +19 -0
- package/dist/lib/auth-secret-file.js +112 -0
- package/dist/lib/design-checks.d.ts +78 -0
- package/dist/lib/design-checks.js +174 -0
- package/dist/lib/extract-module-bundle.js +5 -1
- package/dist/lib/license-key-files.d.ts +9 -0
- package/dist/lib/license-key-files.js +2 -2
- package/dist/lib/module-license-gate.d.ts +89 -0
- package/dist/lib/module-license-gate.js +171 -12
- package/dist/lib/module-tier.d.ts +45 -16
- package/dist/lib/module-tier.js +76 -0
- package/dist/lib/pro-callout.js +10 -2
- package/dist/lib/render-verdict.d.ts +52 -0
- package/dist/lib/render-verdict.js +77 -0
- package/dist/lib/scaffold-render-sweep.d.ts +105 -0
- package/dist/lib/scaffold-render-sweep.js +236 -0
- package/dist/module-architecture.d.ts +19 -16
- package/dist/module-architecture.js +64 -21
- package/dist/parse-args.d.ts +88 -10
- package/dist/parse-args.js +272 -21
- package/dist/programmatic.d.ts +66 -12
- package/dist/programmatic.js +361 -34
- package/dist/site-chrome.d.ts +19 -9
- package/dist/site-chrome.js +23 -13
- package/dist/site-manifest.d.ts +25 -0
- package/dist/site-manifest.js +254 -0
- package/dist/site-roster.d.ts +41 -0
- package/dist/site-roster.js +149 -11
- package/dist/starter-grid.d.ts +232 -0
- package/dist/starter-grid.js +545 -0
- package/dist/surfaces.d.ts +41 -9
- package/dist/surfaces.js +268 -92
- package/dist/template-manifest.d.ts +65 -29
- package/dist/template-manifest.js +161 -382
- package/dist/template-names.d.ts +14 -12
- package/dist/template-names.js +13 -17
- package/package.json +5 -4
- package/site-templates/_base/content/en/Feature/feature-1.mdx +69 -0
- package/site-templates/_base/content/en/Feature/feature-2.mdx +69 -0
- package/site-templates/_base/content/en/Feature/feature-3.mdx +69 -0
- package/site-templates/_base/content/en/FeaturesList/features.mdx +91 -0
- package/site-templates/_base/content/en/GlossaryList/glossary.mdx +0 -1
- package/site-templates/_base/content/en/NewsletterSignup/newsletter.mdx +0 -1
- package/site-templates/_base/content/en/PortfolioItem/sample-four.mdx +105 -0
- package/site-templates/_base/content/en/PortfolioItem/sample-one.mdx +34 -0
- package/site-templates/_base/content/en/PortfolioItem/sample-three.mdx +94 -0
- package/site-templates/_base/content/en/PortfolioItem/sample-two.mdx +45 -0
- package/site-templates/_base/content/en/PortfolioList/portfolio.mdx +169 -0
- package/site-templates/_base/content/en/PrivacyPage/privacy.mdx +15 -0
- package/site-templates/_base/content/en/UseCase/persona-1.mdx +70 -0
- package/site-templates/_base/content/en/UseCase/persona-2.mdx +70 -0
- package/site-templates/_base/content/en/UseCase/persona-3.mdx +70 -0
- package/site-templates/{minimalism-site/TEMPLATE-MANIFEST.json → aliases/minimalism-site.TEMPLATE-MANIFEST.json} +72 -40
- package/site-templates/designs/minimalism/DESIGN.md +98 -6
- package/site-templates/designs/minimalism/sample/README.md +48 -0
- package/site-templates/designs/standard/DESIGN.md +103 -6
- package/site-templates/marketing-site/DESIGN.md +103 -6
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +144 -47
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +82 -41
- package/site-templates/marketing-site-free/DESIGN.md +103 -6
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +150 -48
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +114 -67
- package/site-templates/marketing-site-free/ailk-template.json +1 -1
- package/site-templates/marketing-site-free/content/en/NewsletterSignup/newsletter.mdx +59 -0
- package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +199 -0
- package/site-templates/saas-product/DESIGN.md +103 -6
- package/site-templates/saas-product/TEMPLATE-MANIFEST.md +9 -1
- package/site-templates/{simple-service → shapes/local-business-service}/TEMPLATE-MANIFEST.json +210 -81
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.md +356 -0
- package/site-templates/shapes/local-business-service/ailk-template.json +10 -0
- package/site-templates/{simple-service → shapes/local-business-service}/content/en/AboutPage/about.mdx +4 -4
- package/site-templates/shapes/local-business-service/content/en/HomePage/home.mdx +70 -0
- package/site-templates/shapes/local-business-service/content/en/NewsletterSignup/newsletter.mdx +59 -0
- package/site-templates/shapes/local-business-service/content/en/PortfolioList/portfolio.mdx +171 -0
- package/site-templates/{minimalism-site → shapes/local-business-service}/content/en/PurchasePolicyPage/purchase-policy.mdx +7 -8
- package/site-templates/shapes/local-business-service/content/en/Service/first-job.mdx +39 -0
- package/site-templates/shapes/local-business-service/content/en/Service/second-job.mdx +39 -0
- package/site-templates/shapes/local-business-service/content/en/Service/third-job.mdx +39 -0
- package/site-templates/shapes/local-business-service/content/en/ServicesList/services.mdx +61 -0
- package/site-templates/shapes/local-business-service/provenance.json +4 -0
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.json +471 -0
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.md +354 -0
- package/site-templates/shapes/organization-service/ailk-template.json +10 -0
- package/site-templates/shapes/organization-service/content/en/AboutPage/about.mdx +38 -0
- package/site-templates/shapes/organization-service/content/en/HomePage/home.mdx +85 -0
- package/site-templates/shapes/organization-service/content/en/NewsletterSignup/newsletter.mdx +59 -0
- package/site-templates/shapes/organization-service/content/en/PortfolioList/portfolio.mdx +171 -0
- package/site-templates/shapes/organization-service/content/en/PurchasePolicyPage/purchase-policy.mdx +123 -0
- package/site-templates/{minimalism-site → shapes/organization-service}/content/en/ServicesList/services.mdx +3 -4
- package/site-templates/shapes/organization-service/provenance.json +4 -0
- package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.json +478 -0
- package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.md +353 -0
- package/site-templates/shapes/person-service/ailk-template.json +10 -0
- package/site-templates/shapes/person-service/content/_site.mdx +85 -0
- package/site-templates/shapes/person-service/content/en/AboutPage/about.mdx +39 -0
- package/site-templates/shapes/person-service/content/en/HomePage/home.mdx +97 -0
- package/site-templates/shapes/person-service/content/en/NewsletterSignup/newsletter.mdx +59 -0
- package/site-templates/shapes/person-service/content/en/PortfolioList/portfolio.mdx +171 -0
- package/site-templates/{simple-service → shapes/person-service}/content/en/PurchasePolicyPage/purchase-policy.mdx +10 -11
- package/site-templates/shapes/person-service/content/en/Service/first-engagement.mdx +38 -0
- package/site-templates/shapes/person-service/content/en/Service/second-engagement.mdx +38 -0
- package/site-templates/shapes/person-service/content/en/Service/third-engagement.mdx +38 -0
- package/site-templates/{simple-service → shapes/person-service}/content/en/ServicesList/services.mdx +16 -16
- package/site-templates/shapes/person-service/provenance.json +4 -0
- package/templates/.claude/rules/architecture.md +7 -0
- package/templates/.claude/skills/README.md +5 -3
- package/templates/.claude/skills/add-auth/references/env-vars.md +22 -15
- package/templates/.claude/skills/add-auth/references/oauth-app-setup.md +3 -2
- package/templates/.claude/skills/add-page/SKILL.md +241 -238
- package/templates/.claude/skills/add-page/references/page-types.md +85 -72
- package/templates/.claude/skills/add-page/scripts/site-pages.mjs +87 -0
- package/templates/.claude/skills/add-page/templates/comparison-page.mdx.tmpl +99 -0
- package/templates/.claude/skills/add-page/templates/content.mdx.tmpl +8 -18
- package/templates/.claude/skills/add-page/templates/flow-landing-page.mdx.tmpl +43 -0
- package/templates/.claude/skills/add-page/templates/report-page.mdx.tmpl +52 -0
- package/templates/.claude/skills/add-page/templates/reports-list.mdx.tmpl +38 -0
- package/templates/.claude/skills/check-site/SKILL.md +23 -0
- package/templates/.claude/skills/fill-page/SKILL.md +26 -7
- package/templates/.claude/skills/fill-page/profiles/plumbing.yaml +1 -1
- package/templates/.claude/skills/setup-database/SKILL.md +156 -0
- package/templates/.claude/skills/setup-database/scripts/verify-inbox.ts +154 -0
- package/templates/.claude/skills/setup-deploy/SKILL.md +64 -32
- package/templates/.claude/skills/setup-deploy/references/env-vars-template.md +44 -36
- package/templates/.claude/skills/setup-deploy/references/runbook-template.md +58 -102
- package/templates/.claude/skills/update-page/SKILL.md +117 -0
- package/templates/.claude/skills/update-page/scripts/update-page.ts +640 -0
- package/templates/.claude/skills/update-page/templates/redirects.mjs.tmpl +31 -0
- package/templates/.claude/skills/upgrade-plan/SKILL.md +12 -8
- package/templates/.github/workflows/ci.yml +4 -0
- package/templates/.prettierignore +1 -0
- package/templates/CLAUDE.md +146 -103
- package/templates/DESIGN.md +103 -6
- package/templates/README.md +63 -43
- package/templates/apps/api/.env.example +2 -1
- package/templates/apps/api/CLAUDE.md +3 -2
- package/templates/apps/api/src/__tests__/commerce-claim-registration.test.ts +2 -0
- package/templates/apps/api/src/__tests__/server.test.ts +116 -0
- package/templates/apps/api/src/config/__tests__/index.test.ts +115 -14
- package/templates/apps/api/src/config/index.ts +35 -16
- package/templates/apps/api/src/lib/__tests__/lead-capture-store.test.ts +30 -0
- package/templates/apps/api/src/lib/__tests__/lead-delivery.test.ts +96 -0
- package/templates/apps/api/src/lib/lead-capture-store.ts +12 -0
- package/templates/apps/api/src/lib/lead-delivery.ts +52 -0
- package/templates/apps/api/src/lib/stripe.ts +9 -0
- package/templates/apps/api/src/middleware/__tests__/auth.optional-cookie.test.ts +140 -0
- package/templates/apps/api/src/middleware/auth.ts +54 -0
- package/templates/apps/api/src/openapi/spec.ts +63 -12
- package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +5 -2
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +628 -0
- package/templates/apps/api/src/routes/checkout/sessions.ts +222 -24
- package/templates/apps/api/src/routes/leads/__tests__/capture.test.ts +88 -0
- package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +100 -0
- package/templates/apps/api/src/routes/leads/index.ts +46 -1
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +1 -1
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +5 -1
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +521 -20
- package/templates/apps/api/src/routes/webhooks/stripe.ts +124 -32
- package/templates/apps/api/src/server.ts +35 -0
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +194 -0
- package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +124 -0
- package/templates/apps/api/src/services/__tests__/revalidation.test.ts +90 -15
- package/templates/apps/api/src/services/checkout-buyer.ts +72 -0
- package/templates/apps/api/src/services/lead-routing.ts +140 -30
- package/templates/apps/api/src/services/leads-inbox.ts +48 -11
- package/templates/apps/api/src/services/revalidation.ts +60 -15
- package/templates/apps/mcp/__tests__/parity.test.ts +5 -4
- package/templates/apps/mcp/__tests__/tools/create_project.test.ts +30 -24
- package/templates/apps/mcp/src/tools/create_project.ts +21 -6
- package/templates/apps/mcp/src/tools/purchase.ts +4 -2
- package/templates/apps/web/.env.example +13 -0
- package/templates/apps/web/CLAUDE.md +22 -28
- package/templates/apps/web/app/[locale]/(auth)/layout.tsx +1 -1
- package/templates/apps/web/app/[locale]/(authed)/leads/__tests__/page.test.tsx +24 -0
- package/templates/apps/web/app/[locale]/(authed)/leads/page.tsx +17 -6
- package/templates/apps/web/app/[locale]/[slug]/[detail]/__tests__/page.test.tsx +110 -0
- package/templates/apps/web/app/[locale]/[slug]/[detail]/page.tsx +60 -0
- package/templates/apps/web/app/[locale]/[slug]/page.tsx +1 -1
- package/templates/apps/web/app/[locale]/alternatives/__tests__/page.test.tsx +161 -0
- package/templates/apps/web/app/[locale]/alternatives/page.tsx +104 -0
- package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +14 -6
- package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +10 -9
- package/templates/apps/web/app/[locale]/contact/__tests__/contact-form-text.test.ts +32 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +129 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +227 -84
- package/templates/apps/web/app/[locale]/contact/contact-form-text.ts +50 -0
- package/templates/apps/web/app/[locale]/contact/contact.actions.ts +73 -20
- package/templates/apps/web/app/[locale]/contact/page.tsx +105 -15
- package/templates/apps/web/app/[locale]/contact/site-contact-email.ts +54 -0
- package/templates/apps/web/app/[locale]/features/[slug]/page.tsx +64 -0
- package/templates/apps/web/app/[locale]/features/page.tsx +52 -0
- package/templates/apps/web/app/[locale]/{use-cases → for}/[slug]/page.tsx +21 -5
- package/templates/apps/web/app/[locale]/for/__tests__/page-renderer-migration.test.tsx +73 -0
- package/templates/apps/web/app/[locale]/{use-cases → for}/page.tsx +8 -7
- package/templates/apps/web/app/[locale]/glossary/page.tsx +1 -1
- package/templates/apps/web/app/[locale]/layout.tsx +65 -17
- package/templates/apps/web/app/[locale]/newsletter/__tests__/newsletter.actions.test.ts +75 -0
- package/templates/apps/web/app/[locale]/newsletter/__tests__/page.test.tsx +163 -0
- package/templates/apps/web/app/[locale]/newsletter/newsletter.actions.ts +66 -0
- package/templates/apps/web/app/[locale]/newsletter/page.tsx +106 -14
- package/templates/apps/web/app/[locale]/portfolio/[slug]/page.tsx +59 -0
- package/templates/apps/web/app/[locale]/{book-a-call → portfolio}/page.tsx +12 -13
- package/templates/apps/web/app/[locale]/vs/[slug]/__tests__/page.test.tsx +112 -0
- package/templates/apps/web/app/[locale]/vs/[slug]/page.tsx +70 -0
- package/templates/apps/web/app/api/revalidate/__tests__/route.test.ts +236 -44
- package/templates/apps/web/app/api/revalidate/route.ts +78 -36
- package/templates/apps/web/lib/__tests__/site-brand.test.ts +79 -0
- package/templates/apps/web/lib/__tests__/site-theme.test.ts +104 -11
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +21 -4
- package/templates/apps/web/lib/locale-guard.ts +18 -0
- package/templates/apps/web/lib/meet-placeholder.ts +5 -0
- package/templates/apps/web/lib/nav-config.ts +50 -37
- package/templates/apps/web/lib/site-brand.tsx +13 -9
- package/templates/apps/web/lib/site-identity.ts +14 -0
- package/templates/apps/web/lib/site-logo.tsx +36 -0
- package/templates/apps/web/lib/site-theme.ts +46 -26
- package/templates/apps/web/messages/en.json +45 -5
- package/templates/apps/web/middleware.ts +28 -4
- package/templates/content/_site.mdx +4 -2
- package/templates/content/en/BlogList/blog.mdx +37 -16
- package/templates/content/en/BookCall/book-a-call.mdx +80 -0
- package/templates/content/en/BookCall/onsite-estimate.mdx +56 -0
- package/templates/content/en/BookCall/onsite-service.mdx +56 -0
- package/templates/content/en/BookCall/start-project.mdx +56 -0
- package/templates/content/en/ContactPage/contact.mdx +43 -34
- package/templates/content/en/HomePage/home.mdx +10 -22
- package/templates/content/en/LandingPage/meet.mdx +59 -0
- package/templates/content/en/PurchasePolicyPage/purchase-policy.mdx +0 -1
- package/templates/content/en/UseCasesList/{use-cases.mdx → for.mdx} +19 -18
- package/templates/database/CHANGELOG.md +14 -0
- package/templates/database/package.json +1 -1
- package/templates/package.json +2 -1
- package/templates/tools/check-claude-md.ts +128 -0
- package/versions.json +33 -33
- package/site-templates/_base/content/en/BookCall/book-call.mdx +0 -53
- package/site-templates/_base/content/en/UseCase/first-use-case.mdx +0 -23
- package/site-templates/_base/content/en/UseCase/second-use-case.mdx +0 -23
- package/site-templates/_base/content/en/UseCase/third-use-case.mdx +0 -23
- package/site-templates/minimalism-site/DESIGN.md +0 -162
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +0 -470
- package/site-templates/minimalism-site/ailk-template.json +0 -23
- package/site-templates/minimalism-site/content/en/AboutPage/about.mdx +0 -110
- package/site-templates/minimalism-site/content/en/BookCall/book-call.mdx +0 -53
- package/site-templates/minimalism-site/content/en/ContactPage/contact.mdx +0 -34
- package/site-templates/minimalism-site/content/en/FAQPage/faq.mdx +0 -57
- package/site-templates/minimalism-site/content/en/LandingPage/referral.mdx +0 -77
- package/site-templates/minimalism-site/content/en/LandingPage/reports.mdx +0 -73
- package/site-templates/minimalism-site/content/en/ReportPage/annual-letter-2024.mdx +0 -52
- package/site-templates/minimalism-site/content/en/ReportPage/field-notes-2023.mdx +0 -52
- package/site-templates/minimalism-site/content/en/ReportPage/platform-shift-2025.mdx +0 -52
- package/site-templates/minimalism-site/content/en/ReportPage/state-of-the-market-2026.mdx +0 -52
- package/site-templates/minimalism-site/flows/referral.flow.json +0 -71
- package/site-templates/minimalism-site/provenance.json +0 -4
- package/site-templates/simple-service/DESIGN.md +0 -162
- package/site-templates/simple-service/TEMPLATE-MANIFEST.md +0 -333
- package/site-templates/simple-service/ailk-template.json +0 -11
- package/site-templates/simple-service/content/en/BookCall/book-call.mdx +0 -53
- package/site-templates/simple-service/content/en/ContactPage/contact.mdx +0 -32
- package/site-templates/simple-service/content/en/FAQPage/faq.mdx +0 -56
- package/site-templates/simple-service/content/en/HomePage/home.mdx +0 -85
- package/site-templates/simple-service/content/en/Service/landscape-design.mdx +0 -40
- package/site-templates/simple-service/content/en/Service/lawn-maintenance.mdx +0 -40
- package/site-templates/simple-service/content/en/Service/seasonal-cleanup.mdx +0 -40
- package/site-templates/simple-service/provenance.json +0 -4
- package/templates/.claude/skills/add-page/templates/route.tsx.tmpl +0 -28
- package/templates/apps/web/app/[locale]/contact/ContactForm.tsx +0 -211
- package/templates/apps/web/app/[locale]/contact/__tests__/ContactForm.test.tsx +0 -96
- package/templates/apps/web/app/[locale]/use-cases/__tests__/page-renderer-migration.test.tsx +0 -72
- package/templates/content/en/SoftwareProduct/product.mdx +0 -175
- /package/site-templates/{minimalism-site → designs/minimalism/sample}/content/en/HomePage/home.mdx +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/avatar-placeholder-1.svg +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/avatar-placeholder-2.svg +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/avatar-placeholder-3.svg +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/avatar-placeholder-4.svg +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/avatar-placeholder-5.svg +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/avatar-placeholder-6.svg +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/hero-backdrop.svg +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/logo-placeholder-1.svg +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/logo-placeholder-2.svg +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/logo-placeholder-3.svg +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/wordmark-placeholder.svg +0 -0
- /package/site-templates/{minimalism-site → shapes/organization-service}/content/en/Service/first-engagement.mdx +0 -0
- /package/site-templates/{minimalism-site → shapes/organization-service}/content/en/Service/second-engagement.mdx +0 -0
- /package/site-templates/{minimalism-site → shapes/organization-service}/content/en/Service/third-engagement.mdx +0 -0
package/dist/programmatic.js
CHANGED
|
@@ -19,19 +19,28 @@ import { existsSync, readdirSync, readFileSync, rmdirSync, rmSync, statSync, wri
|
|
|
19
19
|
import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
20
20
|
import { fileURLToPath } from "node:url";
|
|
21
21
|
import { AddModuleRefusedError, applyModuleToScaffold, } from "./add-module.js";
|
|
22
|
+
import { planDesignMd } from "./apply-design-md.js";
|
|
23
|
+
import { renderClaudeMd, syncClaudeMd, } from "./claude-md.js";
|
|
22
24
|
import { CATALOG_TARGET_PATH, writeComponentCatalog, } from "./component-catalog.js";
|
|
23
25
|
import { buildScaffoldMarkers, copyTemplate } from "./copy.js";
|
|
24
26
|
import { deriveArchitecture } from "./derive-architecture.js";
|
|
27
|
+
import { compileDesignTheme } from "./design-compile.js";
|
|
28
|
+
import { applyDesignPresets, readSectionLookCatalog, shippedPageTypes, validateDesignPresets, } from "./design-presets.js";
|
|
29
|
+
import { installDesignSample, resolveDesign, spliceThemeBlock, } from "./design-scaffold.js";
|
|
30
|
+
import { writeAuthSecretFile } from "./lib/auth-secret-file.js";
|
|
25
31
|
import { extractModuleBundle, installBundleTree, } from "./lib/extract-module-bundle.js";
|
|
26
32
|
import { fetchTemplate } from "./lib/fetch-template.js";
|
|
27
33
|
import { assertLicenseKeyShape, writeLicenseKeyFiles, } from "./lib/license-key-files.js";
|
|
28
|
-
import { resolvePaidModules } from "./lib/module-license-gate.js";
|
|
34
|
+
import { assertDesignAllowed, deriveLevel, resolveModuleSet, resolvePaidModules, } from "./lib/module-license-gate.js";
|
|
29
35
|
import { classifyModules } from "./lib/module-tier.js";
|
|
30
36
|
import { normalizeModules } from "./module-architecture.js";
|
|
37
|
+
import { RECOGNIZED_SHAPES, RESERVED_SHAPES, } from "./parse-args.js";
|
|
31
38
|
import { SITE_CHROME_PATH, renderSiteChrome, resolveSiteChrome, } from "./site-chrome.js";
|
|
39
|
+
import { planSiteManifest } from "./site-manifest.js";
|
|
40
|
+
import { SITE_ROSTER_PATH, planSiteManifestDesign, readSiteRoster, withMeetPlaceholderSeeds, } from "./site-roster.js";
|
|
32
41
|
import { GATEABLE_SURFACES, MODULE_SURFACES, NAV_CONFIG_PATH, excludedRoutePaths, renderNavConfig, resolveSurfaces, } from "./surfaces.js";
|
|
33
42
|
import { syncRoutes } from "./sync-routes.js";
|
|
34
|
-
import { BASE_LAYER_DIR,
|
|
43
|
+
import { BASE_LAYER_DIR, SHAPE_STARTERS, TEMPLATE_MANIFESTS, TemplateError, mergeRoster, resolveTemplate, resolveTemplateLineage, rosterDeclares, templateDelivery, templateTreeDir, } from "./template-manifest.js";
|
|
35
44
|
import { validateTarget } from "./validate.js";
|
|
36
45
|
const PKG_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
37
46
|
const TEMPLATES_DIR = resolve(PKG_ROOT, "templates");
|
|
@@ -44,6 +53,8 @@ const TEMPLATES_DIR = resolve(PKG_ROOT, "templates");
|
|
|
44
53
|
* `package.json` `files` instead.
|
|
45
54
|
*/
|
|
46
55
|
const SITE_TEMPLATES_DIR = resolve(PKG_ROOT, "site-templates");
|
|
56
|
+
/** Where shipped design packages live (#6813 D2) — `resolveDesign`'s `designsDir`. */
|
|
57
|
+
const DESIGNS_DIR = join(SITE_TEMPLATES_DIR, "designs");
|
|
47
58
|
const VERSIONS_FILE = resolve(PKG_ROOT, "versions.json");
|
|
48
59
|
function readVersionMap() {
|
|
49
60
|
if (!existsSync(VERSIONS_FILE)) {
|
|
@@ -75,6 +86,55 @@ function countFiles(dir) {
|
|
|
75
86
|
}
|
|
76
87
|
return count;
|
|
77
88
|
}
|
|
89
|
+
/** Every `.mdx` file under `dir`, recursively, sorted (#6816 D6). */
|
|
90
|
+
function listMdxFiles(dir) {
|
|
91
|
+
if (!existsSync(dir))
|
|
92
|
+
return [];
|
|
93
|
+
const out = [];
|
|
94
|
+
const stack = [dir];
|
|
95
|
+
while (stack.length > 0) {
|
|
96
|
+
const current = stack.pop();
|
|
97
|
+
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
98
|
+
const abs = join(current, entry.name);
|
|
99
|
+
if (entry.isDirectory())
|
|
100
|
+
stack.push(abs);
|
|
101
|
+
else if (entry.name.endsWith(".mdx"))
|
|
102
|
+
out.push(abs);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return out.sort();
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The template a `shape` scaffolds (#6754 D5, extended #6757 D6 row 1): the
|
|
109
|
+
* one FREE-delivery manifest of that shape — a declared `--template` alias
|
|
110
|
+
* (`TEMPLATE_MANIFESTS`) or a bare-shape starter with no alias of its own
|
|
111
|
+
* (`SHAPE_STARTERS`), the union the candidate set draws from. Reads manifest
|
|
112
|
+
* fields only — no roster.
|
|
113
|
+
*
|
|
114
|
+
* @throws TemplateError on a reserved or unknown shape, a shape with no Free
|
|
115
|
+
* starter yet (D6), or more than one (never picked silently).
|
|
116
|
+
*/
|
|
117
|
+
function resolveShapeTemplate(shape) {
|
|
118
|
+
if (!RECOGNIZED_SHAPES.includes(shape)) {
|
|
119
|
+
const known = RECOGNIZED_SHAPES.join(", ");
|
|
120
|
+
throw new TemplateError(RESERVED_SHAPES.includes(shape)
|
|
121
|
+
? `shape "${shape}" is not available yet. Choose one of: ${known}.`
|
|
122
|
+
: `unknown shape "${shape}". Recognized: ${known}`);
|
|
123
|
+
}
|
|
124
|
+
const ofShape = [
|
|
125
|
+
...Object.values(TEMPLATE_MANIFESTS),
|
|
126
|
+
...Object.values(SHAPE_STARTERS),
|
|
127
|
+
].filter((manifest) => manifest.shape === shape);
|
|
128
|
+
const free = ofShape.filter((manifest) => templateDelivery(manifest) === "free");
|
|
129
|
+
if (free.length === 0) {
|
|
130
|
+
const names = ofShape.map((manifest) => manifest.name).join(", ");
|
|
131
|
+
throw new TemplateError(`shape "${shape}" has no Free starter yet — its page list lands with #6743. Templates built on this shape: ${names || "none"}.`);
|
|
132
|
+
}
|
|
133
|
+
if (free.length > 1) {
|
|
134
|
+
throw new TemplateError(`shape "${shape}" has more than one Free starter: ${free.map((manifest) => manifest.name).join(", ")}`);
|
|
135
|
+
}
|
|
136
|
+
return free[0];
|
|
137
|
+
}
|
|
78
138
|
/**
|
|
79
139
|
* Scaffold an AI Launch Kit starter project into targetDir (synchronous).
|
|
80
140
|
*
|
|
@@ -96,6 +156,9 @@ export function scaffoldSync(opts) {
|
|
|
96
156
|
excludePaths: excludedRoutePaths(enabledSurfaces),
|
|
97
157
|
markers: buildScaffoldMarkers(projectName),
|
|
98
158
|
});
|
|
159
|
+
// Stamp the copied CLAUDE.md's template regions with their hashes (#6904
|
|
160
|
+
// D2). No later layer writes a root CLAUDE.md, so this is the final file.
|
|
161
|
+
writeFileSync(join(targetAbs, "CLAUDE.md"), renderClaudeMd(readFileSync(join(TEMPLATES_DIR, "CLAUDE.md"), "utf8"), projectName));
|
|
99
162
|
// Derive + write the module-set-driven architecture.yaml (spec #3787). This
|
|
100
163
|
// is the single source of the scaffolded site's code-boundary config and
|
|
101
164
|
// closes the dangling `architecture_rules: architecture.yaml` reference in
|
|
@@ -112,13 +175,18 @@ export function scaffoldSync(opts) {
|
|
|
112
175
|
// template's `layout` block; a bare `scaffoldSync` call (every existing
|
|
113
176
|
// synchronous caller) writes DEFAULT_SITE_CHROME, byte-identical to the
|
|
114
177
|
// monorepo's own committed apps/web/lib/site-chrome.ts.
|
|
115
|
-
writeFileSync(join(targetAbs, SITE_CHROME_PATH), renderSiteChrome(resolveSiteChrome(opts.layout, opts.
|
|
178
|
+
writeFileSync(join(targetAbs, SITE_CHROME_PATH), renderSiteChrome(resolveSiteChrome(opts.layout, opts.designLayout)));
|
|
116
179
|
// Opt-in only (#4636). Every write above this line is unconditional, so a
|
|
117
180
|
// scaffold with `withCatalog` unset touches exactly the files it did before.
|
|
118
181
|
if (opts.withCatalog === true)
|
|
119
182
|
writeComponentCatalog(targetAbs);
|
|
120
183
|
const filesCreated = countFiles(targetAbs);
|
|
121
|
-
return {
|
|
184
|
+
return {
|
|
185
|
+
targetDir: targetAbs,
|
|
186
|
+
filesCreated,
|
|
187
|
+
surfaces: enabledSurfaces,
|
|
188
|
+
modules: normalizeModules(opts.modules ?? ["marketing"]),
|
|
189
|
+
};
|
|
122
190
|
}
|
|
123
191
|
/**
|
|
124
192
|
* Scaffold an AI Launch Kit starter project into targetDir (async wrapper).
|
|
@@ -138,8 +206,14 @@ export function scaffoldSync(opts) {
|
|
|
138
206
|
* its fetched bundle or — when it requires only free modules — from the
|
|
139
207
|
* `site-templates/` tree this package ships.
|
|
140
208
|
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
209
|
+
* Between 2 and 3, `assertDesignAllowed` (#6760) refuses a design the caller
|
|
210
|
+
* CHOSE (`opts.design`) for a level below Pro, HERE, also before anything is
|
|
211
|
+
* written to disk. A template's own declared design is never gated (#6778
|
|
212
|
+
* option (c)): the template's `requires` already decide its level.
|
|
213
|
+
*
|
|
214
|
+
* And, ahead of all five when `opts.template` (or `opts.shape`, #6754 D5) is
|
|
215
|
+
* set (0): resolve the template and union its `requires` with `opts.modules`,
|
|
216
|
+
* so every step above
|
|
143
217
|
* runs over the EFFECTIVE module set rather than the requested one.
|
|
144
218
|
*
|
|
145
219
|
* `scaffoldSync` itself stays licensing-oblivious on purpose: every existing
|
|
@@ -155,39 +229,150 @@ export async function scaffold(opts) {
|
|
|
155
229
|
const licenseKey = opts.license ?? opts.licenseKey;
|
|
156
230
|
// Resolved first, and by the registry rather than re-validated here: the
|
|
157
231
|
// name already passed `parse-args.ts`, and `resolveTemplate` throws a
|
|
158
|
-
// `TemplateError` for a programmatic caller that skipped the parser.
|
|
159
|
-
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if (design !== undefined && !STARTER_DESIGNS.includes(design)) {
|
|
164
|
-
throw new TemplateError(`unknown design "${design}". Declared: ${STARTER_DESIGNS.join(", ")}`);
|
|
232
|
+
// `TemplateError` for a programmatic caller that skipped the parser. A
|
|
233
|
+
// `shape` resolves to its one Free starter and from there is the template
|
|
234
|
+
// path verbatim (#6754 D5); neither leaves the bare overlay untouched.
|
|
235
|
+
if (opts.template !== undefined && opts.shape !== undefined) {
|
|
236
|
+
throw new TemplateError("pass a template or a shape, not both");
|
|
165
237
|
}
|
|
238
|
+
const template = opts.template !== undefined
|
|
239
|
+
? resolveTemplate(opts.template)
|
|
240
|
+
: opts.shape !== undefined
|
|
241
|
+
? resolveShapeTemplate(opts.shape)
|
|
242
|
+
: undefined;
|
|
243
|
+
// #6813 D8 step 3 — the design this scaffold renders, resolved from its
|
|
244
|
+
// package ONCE, before any fetch or write: the caller's override, else the
|
|
245
|
+
// template's own. Unset for a scaffold naming neither. Replaces the
|
|
246
|
+
// `STARTER_DESIGNS.includes` name check: an unrecognized name or a missing
|
|
247
|
+
// directory throws `TemplateError` here; a malformed package throws
|
|
248
|
+
// `DesignPackageError` (never caught), each naming the field.
|
|
249
|
+
const designRef = opts.design ?? template?.design;
|
|
250
|
+
const resolvedDesign = designRef !== undefined
|
|
251
|
+
? resolveDesign(designRef, { designsDir: DESIGNS_DIR })
|
|
252
|
+
: undefined;
|
|
253
|
+
// #6816 D6 step 3b — a design's section presets are validated here, before
|
|
254
|
+
// any fetch or write, against the look groups the shipped catalog carries
|
|
255
|
+
// (D2): a non-look field or unknown variant throws `DesignPackageError`
|
|
256
|
+
// naming page type, section type and field, and the target is never
|
|
257
|
+
// created. A design with no `sections` block executes no new read (D8).
|
|
258
|
+
if (resolvedDesign !== undefined &&
|
|
259
|
+
Object.keys(resolvedDesign.pkg.sections).length > 0) {
|
|
260
|
+
validateDesignPresets(resolvedDesign.pkg.sections, {
|
|
261
|
+
catalog: readSectionLookCatalog(join(PKG_ROOT, "component-catalog.json")),
|
|
262
|
+
pageTypes: shippedPageTypes(SITE_TEMPLATES_DIR),
|
|
263
|
+
file: join(resolvedDesign.dir, "DESIGN.md"),
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
// #6813 D7 — the resolved design only when the CALLER chose one (`opts.design`);
|
|
267
|
+
// a template's own declared design is never gated (#6778 option (c)).
|
|
268
|
+
const chosenDesign = opts.design !== undefined ? resolvedDesign : undefined;
|
|
166
269
|
// #5618 D2 — the `extends` chain is resolved in the client registry before
|
|
167
270
|
// any fetch: an unknown parent or a cycle is refused here, with the target
|
|
168
|
-
// still non-existent, like the four refusals below.
|
|
169
|
-
|
|
170
|
-
|
|
271
|
+
// still non-existent, like the four refusals below. The registry is the
|
|
272
|
+
// union of TEMPLATE_MANIFESTS and SHAPE_STARTERS (#6757 D6 row 1): a
|
|
273
|
+
// `--shape` run's resolved template may be a bare-shape starter, whose name
|
|
274
|
+
// (the shape itself) `TEMPLATE_MANIFESTS` alone does not carry.
|
|
275
|
+
if (template !== undefined) {
|
|
276
|
+
resolveTemplateLineage(template.name, {
|
|
277
|
+
...TEMPLATE_MANIFESTS,
|
|
278
|
+
...SHAPE_STARTERS,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
171
281
|
// The effective module set: union(template.requires, parsed --modules),
|
|
172
282
|
// normalized through `normalizeModules` — the single ordering authority
|
|
173
283
|
// (parent record D7). Order-independence falls out rather than being
|
|
174
284
|
// engineered: it returns `RECOGNIZED_MODULES.filter(...)`, declaration-
|
|
175
285
|
// ordered and deduped, so the same SET in any argument order is the same
|
|
176
286
|
// list.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
287
|
+
let modules;
|
|
288
|
+
let bundles;
|
|
289
|
+
// The run's derived level (#6759), for `assertDesignAllowed` below.
|
|
290
|
+
// Undefined on the alias branch and on a `--modules`-keep run (D2/D4 in
|
|
291
|
+
// module-license-gate.ts — the level plays no part in composing either),
|
|
292
|
+
// so a non-`standard` design on either path derives it separately, below.
|
|
293
|
+
let level;
|
|
294
|
+
if (opts.template !== undefined) {
|
|
295
|
+
// Alias run — untouched (NFR-4): the union line and its
|
|
296
|
+
// resolvePaidModules({ …, templateName }) call stay exactly as they were.
|
|
297
|
+
modules = normalizeModules([
|
|
298
|
+
...(template?.requires ?? []),
|
|
299
|
+
...(opts.modules ?? ["marketing"]),
|
|
300
|
+
]);
|
|
301
|
+
const { paid } = classifyModules(modules);
|
|
302
|
+
bundles = await resolvePaidModules({
|
|
303
|
+
paidModules: paid,
|
|
304
|
+
licenseKey,
|
|
305
|
+
...(template !== undefined ? { templateName: template.name } : {}),
|
|
306
|
+
...(opts.moduleRegistryUrl !== undefined
|
|
307
|
+
? { registryUrl: opts.moduleRegistryUrl }
|
|
308
|
+
: {}),
|
|
309
|
+
...(opts.fetchImpl !== undefined ? { fetchImpl: opts.fetchImpl } : {}),
|
|
310
|
+
});
|
|
311
|
+
// #6760 — `resolveModuleSet` never runs on this branch (NFR-4), so no
|
|
312
|
+
// level was derived above. Derive one — from the registry's per-module
|
|
313
|
+
// answers, never a key claim (D2) — only when the caller CHOSE a
|
|
314
|
+
// non-Free design alongside the template (`opts.design`, #6813 D7). An
|
|
315
|
+
// alias's own declared design is never gated (#6778 option (c)), so a
|
|
316
|
+
// plain `--template minimalism-site` run makes no extra network call and
|
|
317
|
+
// is never refused on its design.
|
|
318
|
+
if (chosenDesign !== undefined && !chosenDesign.free) {
|
|
319
|
+
({ level } = await deriveLevel({
|
|
320
|
+
licenseKey,
|
|
321
|
+
...(opts.moduleRegistryUrl !== undefined
|
|
322
|
+
? { registryUrl: opts.moduleRegistryUrl }
|
|
323
|
+
: {}),
|
|
324
|
+
...(opts.fetchImpl !== undefined ? { fetchImpl: opts.fetchImpl } : {}),
|
|
325
|
+
}));
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
// Shape and bare runs — the key's level decides (#6759 D6), never a flag,
|
|
330
|
+
// a manifest or a key claim.
|
|
331
|
+
const chosen = await resolveModuleSet({
|
|
332
|
+
named: opts.modules,
|
|
333
|
+
licenseKey,
|
|
334
|
+
...(opts.moduleRegistryUrl !== undefined
|
|
335
|
+
? { registryUrl: opts.moduleRegistryUrl }
|
|
336
|
+
: {}),
|
|
337
|
+
...(opts.fetchImpl !== undefined ? { fetchImpl: opts.fetchImpl } : {}),
|
|
338
|
+
});
|
|
339
|
+
bundles = chosen.bundles;
|
|
340
|
+
level = chosen.level;
|
|
341
|
+
modules =
|
|
342
|
+
template !== undefined
|
|
343
|
+
? normalizeModules([...template.requires, ...chosen.modules])
|
|
344
|
+
: chosen.modules;
|
|
345
|
+
// A paid member of the shape's own starter `requires` not already
|
|
346
|
+
// fetched during the level probe (D6) — every buildable shape's starter
|
|
347
|
+
// requires only `marketing` today, so this is presently a no-op branch.
|
|
348
|
+
if (template !== undefined) {
|
|
349
|
+
const templatePaid = classifyModules(template.requires).paid;
|
|
350
|
+
const stillMissing = templatePaid.filter((m) => !bundles.has(m));
|
|
351
|
+
if (stillMissing.length > 0) {
|
|
352
|
+
const extra = await resolvePaidModules({
|
|
353
|
+
paidModules: stillMissing,
|
|
354
|
+
licenseKey,
|
|
355
|
+
...(opts.moduleRegistryUrl !== undefined
|
|
356
|
+
? { registryUrl: opts.moduleRegistryUrl }
|
|
357
|
+
: {}),
|
|
358
|
+
...(opts.fetchImpl !== undefined
|
|
359
|
+
? { fetchImpl: opts.fetchImpl }
|
|
360
|
+
: {}),
|
|
361
|
+
});
|
|
362
|
+
for (const [k, v] of extra)
|
|
363
|
+
bundles.set(k, v);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
// #6760 D4 — after the level is resolved, before any fetch or write below.
|
|
368
|
+
// Only the caller's chosen design is gated (#6778 option (c)); a template's
|
|
369
|
+
// own declared design is not. `level ?? "free"` fails closed: a
|
|
370
|
+
// `--modules`-keep run under-reports its level by design (NFR-2), so a
|
|
371
|
+
// chosen non-Free design on that path is refused rather than trusted. The
|
|
372
|
+
// refusal names `chosen.display` — the name, or the absolute directory
|
|
373
|
+
// (#6813 D7) — and `chosen.free` decides Free-ness by identity (D2), never
|
|
374
|
+
// by `FREE_DESIGNS.includes(name)` alone (a path is never in that list).
|
|
375
|
+
assertDesignAllowed(chosenDesign?.display, level ?? "free", chosenDesign?.free);
|
|
191
376
|
// The template's OWN bundle, fetched here — before `scaffoldSync` — so the
|
|
192
377
|
// fourth refusal (the template's bundle unpublished, parent record D17 /
|
|
193
378
|
// spec 5481 § 3 DD5 row 4) throws with the target still non-existent, like
|
|
@@ -241,7 +426,9 @@ export async function scaffold(opts) {
|
|
|
241
426
|
// design's knobs (#6638), and a `standard` one to the same defaults
|
|
242
427
|
// `scaffoldSync` already writes for a templateless scaffold.
|
|
243
428
|
...(template?.layout !== undefined ? { layout: template.layout } : {}),
|
|
244
|
-
|
|
429
|
+
// #6813 D8 step 6 — the resolved design's OWN layout (never its name):
|
|
430
|
+
// `resolveSiteChrome` reads a layout object now, not a `StarterDesign`.
|
|
431
|
+
...(resolvedDesign !== undefined ? { designLayout: resolvedDesign.pkg.layout } : {}),
|
|
245
432
|
});
|
|
246
433
|
// Threaded to extractModuleBundle for `package.json` patch targets'
|
|
247
434
|
// `workspace:*` substitution (spec 5418 § 3 D2) — the same version map
|
|
@@ -306,6 +493,11 @@ export async function scaffold(opts) {
|
|
|
306
493
|
//
|
|
307
494
|
// One install step, two source roots (DD7): an extracted staging dir on the
|
|
308
495
|
// paid path, `site-templates/<name>/` on disk on the Free one.
|
|
496
|
+
// #6813 D6 — captured for the site-manifest record below, which needs the
|
|
497
|
+
// chrome ACTUALLY written (`installedLayout ?? template.layout`), readable
|
|
498
|
+
// only after this block (the bundle's own `layout`, when it declares one,
|
|
499
|
+
// is unreachable before install).
|
|
500
|
+
let installedLayout;
|
|
309
501
|
if (template !== undefined) {
|
|
310
502
|
// #5612 D1/D2 — the INSTALLED template's own `ailk-template.json` layout
|
|
311
503
|
// (validated by `readAilkTemplateManifest`), if it declares one. Read
|
|
@@ -326,7 +518,7 @@ export async function scaffold(opts) {
|
|
|
326
518
|
ownership,
|
|
327
519
|
})
|
|
328
520
|
: (() => {
|
|
329
|
-
const sourceRoot = join(SITE_TEMPLATES_DIR, template.name);
|
|
521
|
+
const sourceRoot = join(SITE_TEMPLATES_DIR, templateTreeDir(template.name));
|
|
330
522
|
if (!existsSync(sourceRoot)) {
|
|
331
523
|
throw new Error(`template "${template.name}": ${sourceRoot} is missing — the published package must carry site-templates/ in its "files" list`);
|
|
332
524
|
}
|
|
@@ -341,16 +533,133 @@ export async function scaffold(opts) {
|
|
|
341
533
|
ownership,
|
|
342
534
|
});
|
|
343
535
|
})();
|
|
536
|
+
installedLayout = installed.layout;
|
|
344
537
|
if (installed.layout !== undefined) {
|
|
345
|
-
writeFileSync(join(result.targetDir, SITE_CHROME_PATH), renderSiteChrome(resolveSiteChrome(installed.layout,
|
|
538
|
+
writeFileSync(join(result.targetDir, SITE_CHROME_PATH), renderSiteChrome(resolveSiteChrome(installed.layout, resolvedDesign?.pkg.layout)));
|
|
346
539
|
}
|
|
540
|
+
// #6878 D6 — re-render the nav with the SERVICE slugs the installed tree
|
|
541
|
+
// just landed (`content/en/Service/*.mdx`, absent on a product shape),
|
|
542
|
+
// now that `content/en/Service` exists to read. `scaffoldSync`'s own
|
|
543
|
+
// one-argument render above already wrote a nav for `result.surfaces`
|
|
544
|
+
// BEFORE the template tree existed — byte-identical to this one on a
|
|
545
|
+
// product shape (no Service directory to read), and this is what makes a
|
|
546
|
+
// service shape's flyout show its own Services column instead of an
|
|
547
|
+
// empty offering column. Unconditional (every templated scaffold gets
|
|
548
|
+
// this second render, not only one that resolved a `layout`), and BEFORE
|
|
549
|
+
// `pruneUnrosteredSeeds` below: the read reflects every seed the install
|
|
550
|
+
// step actually wrote, not what a later prune leaves behind.
|
|
551
|
+
const serviceContentDir = join(result.targetDir, "content", "en", "Service");
|
|
552
|
+
const serviceSlugs = existsSync(serviceContentDir)
|
|
553
|
+
? readdirSync(serviceContentDir)
|
|
554
|
+
.filter((f) => f.endsWith(".mdx"))
|
|
555
|
+
.map((f) => f.slice(0, -".mdx".length))
|
|
556
|
+
: [];
|
|
557
|
+
writeFileSync(join(result.targetDir, NAV_CONFIG_PATH), renderNavConfig(result.surfaces, { serviceSlugs }));
|
|
347
558
|
pruneUnrosteredSeeds(result.targetDir, mergeRoster(template, template.requires), result.surfaces, ownership);
|
|
559
|
+
// spec 6892 D4 — the resolved design's own sample (the minimalism design's
|
|
560
|
+
// nine-slide deck home + its SVGs today), installed AFTER the template
|
|
561
|
+
// layer so it wins over the shape's `HomePage/home.mdx`, and ONLY when
|
|
562
|
+
// the resolved design is the template's OWN declared design: a caller
|
|
563
|
+
// combination like `--shape organization-product --design minimalism`
|
|
564
|
+
// installs no sample (its roster carries neither the deck's link targets
|
|
565
|
+
// nor, in its generated record, the deck's sections).
|
|
566
|
+
if (resolvedDesign !== undefined &&
|
|
567
|
+
resolvedDesign.recorded === template.design) {
|
|
568
|
+
installDesignSample({
|
|
569
|
+
designDir: resolvedDesign.dir,
|
|
570
|
+
targetDir: result.targetDir,
|
|
571
|
+
ownership,
|
|
572
|
+
});
|
|
573
|
+
}
|
|
348
574
|
}
|
|
575
|
+
// #6813 D5/D8 steps 9-10 — whenever a design is resolved (the caller's,
|
|
576
|
+
// else the template's): the design's DESIGN.md (#6939); the compiled theme,
|
|
577
|
+
// spliced into the scaffold's `content/_site.mdx` LAST; then the site manifest records the APPLIED
|
|
578
|
+
// design, only when a template installed one (`saas-product` ships none).
|
|
579
|
+
// A scaffold naming neither a design nor a template (the bare overlay)
|
|
580
|
+
// executes no new statement.
|
|
581
|
+
if (resolvedDesign !== undefined) {
|
|
582
|
+
// #6939 — the scaffold's DESIGN.md is the design it wears, not the one
|
|
583
|
+
// its template layer shipped; the site's own header lines are kept.
|
|
584
|
+
writeFileSync(join(result.targetDir, "DESIGN.md"), planDesignMd(resolvedDesign.dir, result.targetDir));
|
|
585
|
+
const compiledTheme = compileDesignTheme(resolvedDesign.pkg);
|
|
586
|
+
const siteMdxPath = join(result.targetDir, "content", "_site.mdx");
|
|
587
|
+
if (existsSync(siteMdxPath)) {
|
|
588
|
+
const current = readFileSync(siteMdxPath, "utf8");
|
|
589
|
+
const spliced = spliceThemeBlock(current, compiledTheme, siteMdxPath);
|
|
590
|
+
if (spliced !== current)
|
|
591
|
+
writeFileSync(siteMdxPath, spliced);
|
|
592
|
+
}
|
|
593
|
+
if (template !== undefined) {
|
|
594
|
+
const rosterPath = join(result.targetDir, SITE_ROSTER_PATH);
|
|
595
|
+
if (existsSync(rosterPath)) {
|
|
596
|
+
const rosterText = readFileSync(rosterPath, "utf8");
|
|
597
|
+
const chromeWritten = resolveSiteChrome(installedLayout ?? template.layout, resolvedDesign.pkg.layout);
|
|
598
|
+
const nextManifest = planSiteManifestDesign(rosterText, { design: resolvedDesign.recorded, layout: chromeWritten, theme: compiledTheme }, rosterPath);
|
|
599
|
+
if (nextManifest !== undefined)
|
|
600
|
+
writeFileSync(rosterPath, nextManifest);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
// #6816 D6 step 10b — the design's section presets, written onto the
|
|
604
|
+
// pages of the page types the roster of record places, once every layer
|
|
605
|
+
// has landed. The one `applyDesignPresets` call site. A scaffold with no
|
|
606
|
+
// roster (the bare overlay, `saas-product`) validated its presets at
|
|
607
|
+
// step 3b and applies none: nothing "the shape placed" to resolve against.
|
|
608
|
+
if (template !== undefined &&
|
|
609
|
+
Object.keys(resolvedDesign.pkg.sections).length > 0) {
|
|
610
|
+
const roster = readSiteRoster(result.targetDir);
|
|
611
|
+
if (roster !== undefined) {
|
|
612
|
+
const pageTypes = new Set();
|
|
613
|
+
for (const row of roster.rows) {
|
|
614
|
+
const pageType = row.value["pageType"];
|
|
615
|
+
if (typeof pageType === "string")
|
|
616
|
+
pageTypes.add(pageType);
|
|
617
|
+
}
|
|
618
|
+
const contentDir = join(result.targetDir, "content");
|
|
619
|
+
const pages = listMdxFiles(contentDir)
|
|
620
|
+
.filter((path) => path !== join(contentDir, "_site.mdx"))
|
|
621
|
+
.map((path) => ({ path, text: readFileSync(path, "utf8") }));
|
|
622
|
+
const presetResult = applyDesignPresets(pages, resolvedDesign.pkg.sections, { pageTypes });
|
|
623
|
+
for (const page of presetResult.pages)
|
|
624
|
+
writeFileSync(page.path, page.text);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
// #6944 — the manifest records what this scaffold IS, once every layer has
|
|
629
|
+
// landed: the effective module set, a row for every route on disk, and the
|
|
630
|
+
// level only when the key path above derived one — `resolveModuleSet`, or
|
|
631
|
+
// `deriveLevel` for a chosen non-Free design. `level` stays undefined on a
|
|
632
|
+
// plain alias or `--modules`-keep run, and is never inferred from `modules`.
|
|
633
|
+
// A scaffold with no manifest (the bare overlay, `saas-product`) writes none.
|
|
634
|
+
if (template !== undefined) {
|
|
635
|
+
const roster = readSiteRoster(result.targetDir);
|
|
636
|
+
if (roster !== undefined) {
|
|
637
|
+
writeFileSync(join(result.targetDir, roster.path), planSiteManifest({
|
|
638
|
+
targetDir: result.targetDir,
|
|
639
|
+
roster,
|
|
640
|
+
modules: result.modules,
|
|
641
|
+
level,
|
|
642
|
+
siteTemplatesDir: SITE_TEMPLATES_DIR,
|
|
643
|
+
templatesDir: TEMPLATES_DIR,
|
|
644
|
+
markers,
|
|
645
|
+
}));
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
// #6107 D2 — after every layer landed, so nothing later overwrites it: a
|
|
649
|
+
// served scaffold needs BETTER_AUTH_SECRET (#6044), and this is its value.
|
|
650
|
+
writeAuthSecretFile(result.targetDir);
|
|
349
651
|
// #6683 D9 — persisted only after every layer landed, and only when the
|
|
350
652
|
// caller passed `license`: a scaffold without it writes nothing new.
|
|
351
653
|
if (opts.license !== undefined) {
|
|
352
654
|
writeLicenseKeyFiles(result.targetDir, opts.license);
|
|
353
655
|
}
|
|
656
|
+
// #6754 D4/D5 — the resolved starter, so the CLI names what it built.
|
|
657
|
+
if (template !== undefined && resolvedDesign !== undefined) {
|
|
658
|
+
return {
|
|
659
|
+
...result,
|
|
660
|
+
starter: { template: template.name, shape: template.shape, design: resolvedDesign.recorded },
|
|
661
|
+
};
|
|
662
|
+
}
|
|
354
663
|
return result;
|
|
355
664
|
}
|
|
356
665
|
/**
|
|
@@ -368,12 +677,17 @@ export async function scaffold(opts) {
|
|
|
368
677
|
* the content directory of a surface this scaffold ENABLES is kept too, so
|
|
369
678
|
* an explicit `--surfaces pricing` on a template that does not roster
|
|
370
679
|
* `/pricing` gets its route and its seed together.
|
|
680
|
+
*
|
|
681
|
+
* The Free `/meet` placeholder seeds count as declared wherever the roster
|
|
682
|
+
* claims their route, the `scheduling` module's rows included (#6942): an
|
|
683
|
+
* unconfigured scheduling site's /meet pages hand off to them.
|
|
371
684
|
*/
|
|
372
685
|
function pruneUnrosteredSeeds(targetDir, roster, surfaces, ownership) {
|
|
373
686
|
// `installBundleTree`'s ownership label for the base layer: it installs
|
|
374
687
|
// with `templateRequires: []`, so it records as a template. The overlay
|
|
375
688
|
// (the `templates/` snapshot) records no owner at all.
|
|
376
689
|
const shared = new Set([undefined, `template "${BASE_LAYER_DIR}"`]);
|
|
690
|
+
const declared = withMeetPlaceholderSeeds(roster);
|
|
377
691
|
const enabledDirs = GATEABLE_SURFACES.filter((surface) => surfaces.includes(surface.id)).flatMap((surface) => surface.content.map((dir) => `${dir.replace("<locale>", "en")}/`));
|
|
378
692
|
const root = join(targetDir, "content", "en");
|
|
379
693
|
if (!existsSync(root))
|
|
@@ -393,7 +707,7 @@ function pruneUnrosteredSeeds(targetDir, roster, surfaces, ownership) {
|
|
|
393
707
|
continue;
|
|
394
708
|
const path = relative(targetDir, abs).split(sep).join("/");
|
|
395
709
|
if (shared.has(ownership.get(path)) &&
|
|
396
|
-
!rosterDeclares(
|
|
710
|
+
!rosterDeclares(declared, path) &&
|
|
397
711
|
!enabledDirs.some((prefix) => path.startsWith(prefix))) {
|
|
398
712
|
rmSync(abs);
|
|
399
713
|
if (readdirSync(dir).length === 0)
|
|
@@ -526,6 +840,19 @@ export function syncCatalogSync(opts) {
|
|
|
526
840
|
catalogPath: writeComponentCatalog(targetAbs),
|
|
527
841
|
};
|
|
528
842
|
}
|
|
843
|
+
/**
|
|
844
|
+
* Rewrite the template-owned sections of an already-scaffolded repo's root
|
|
845
|
+
* `CLAUDE.md` to the template this create-ailk ships (#6904 D3). The logic
|
|
846
|
+
* lives in `claude-md.ts` (a module jest imports directly); this wrapper
|
|
847
|
+
* only supplies the package's `templates/CLAUDE.md` — the `syncRoutesSync`
|
|
848
|
+
* split. `templateText` stands in for the shipped template in tests.
|
|
849
|
+
*/
|
|
850
|
+
export function syncClaudeMdSync(opts) {
|
|
851
|
+
return syncClaudeMd({
|
|
852
|
+
targetDir: opts.targetDir,
|
|
853
|
+
templateText: opts.templateText ?? readFileSync(join(TEMPLATES_DIR, "CLAUDE.md"), "utf8"),
|
|
854
|
+
});
|
|
855
|
+
}
|
|
529
856
|
/**
|
|
530
857
|
* Compare an already-scaffolded repo's route tree with the template this
|
|
531
858
|
* create-ailk ships and, with `apply`, copy in the route dirs it is missing
|
package/dist/site-chrome.d.ts
CHANGED
|
@@ -21,11 +21,14 @@
|
|
|
21
21
|
* block, gets these defaults written, byte-identical to the monorepo's own
|
|
22
22
|
* committed `apps/web/lib/site-chrome.ts`.
|
|
23
23
|
*
|
|
24
|
-
* A
|
|
25
|
-
* `
|
|
26
|
-
*
|
|
24
|
+
* A design supplies layout knobs too (#6638, widened #6813 D4): its OWN
|
|
25
|
+
* `layout` (a `DesignPackage`'s, read by `design-scaffold.ts`'s
|
|
26
|
+
* `resolveDesign`), passed as `resolveSiteChrome`'s optional `designLayout`
|
|
27
|
+
* argument between an explicit `layout` and these defaults. This module
|
|
28
|
+
* imports nothing from `template-manifest.ts` (the cycle that import used to
|
|
29
|
+
* close): an unrecognized design NAME now fails in `resolveDesign`, before
|
|
30
|
+
* this function ever sees one.
|
|
27
31
|
*/
|
|
28
|
-
import { type StarterDesign } from "./template-manifest.js";
|
|
29
32
|
/** The `nav` knob's vocabulary. `mark-only` → the layout passes Nav an empty
|
|
30
33
|
* `navItems` (brand mark + the existing actions/theme/locale controls only). */
|
|
31
34
|
export type SiteChromeNav = "full" | "mark-only";
|
|
@@ -75,12 +78,19 @@ export declare class TemplateLayoutError extends Error {
|
|
|
75
78
|
*/
|
|
76
79
|
export declare function validateTemplateLayout(value: unknown, subject: string): TemplateLayout;
|
|
77
80
|
/**
|
|
78
|
-
* Fill every field a `TemplateLayout` leaves unset — from `
|
|
79
|
-
*
|
|
80
|
-
* `DEFAULT_SITE_CHROME`. The explicit `layout` wins field by
|
|
81
|
-
* template can wear a design and still override one knob of it.
|
|
81
|
+
* Fill every field a `TemplateLayout` leaves unset — from `designLayout`
|
|
82
|
+
* (a design package's own resolved `layout`, #6638/#6813 D4) when one is
|
|
83
|
+
* given, then from `DEFAULT_SITE_CHROME`. The explicit `layout` wins field by
|
|
84
|
+
* field, so a template can wear a design and still override one knob of it.
|
|
85
|
+
*
|
|
86
|
+
* `designLayout` is typed `TemplateLayout`, but a `.mjs` caller
|
|
87
|
+
* (`build-template-manifest.mjs`) cannot be compile-time checked the way a
|
|
88
|
+
* `.ts` caller can, so the type alone cannot stop a caller passing a design
|
|
89
|
+
* NAME (a string) rather than its resolved layout: a runtime guard throws a
|
|
90
|
+
* `TypeError` naming what was given, rather than silently resolving to the
|
|
91
|
+
* defaults.
|
|
82
92
|
*/
|
|
83
|
-
export declare function resolveSiteChrome(layout?: TemplateLayout,
|
|
93
|
+
export declare function resolveSiteChrome(layout?: TemplateLayout, designLayout?: TemplateLayout): ResolvedSiteChrome;
|
|
84
94
|
/**
|
|
85
95
|
* Render `apps/web/lib/site-chrome.ts` for a resolved layout — the same
|
|
86
96
|
* GENERATED-header idiom `renderNavConfig` uses. `layout.tsx` imports
|
package/dist/site-chrome.js
CHANGED
|
@@ -21,11 +21,14 @@
|
|
|
21
21
|
* block, gets these defaults written, byte-identical to the monorepo's own
|
|
22
22
|
* committed `apps/web/lib/site-chrome.ts`.
|
|
23
23
|
*
|
|
24
|
-
* A
|
|
25
|
-
* `
|
|
26
|
-
*
|
|
24
|
+
* A design supplies layout knobs too (#6638, widened #6813 D4): its OWN
|
|
25
|
+
* `layout` (a `DesignPackage`'s, read by `design-scaffold.ts`'s
|
|
26
|
+
* `resolveDesign`), passed as `resolveSiteChrome`'s optional `designLayout`
|
|
27
|
+
* argument between an explicit `layout` and these defaults. This module
|
|
28
|
+
* imports nothing from `template-manifest.ts` (the cycle that import used to
|
|
29
|
+
* close): an unrecognized design NAME now fails in `resolveDesign`, before
|
|
30
|
+
* this function ever sees one.
|
|
27
31
|
*/
|
|
28
|
-
import { DESIGN_LAYOUTS, TemplateError, } from "./template-manifest.js";
|
|
29
32
|
/** Defaults (#5612 D1; banner off since #6469). */
|
|
30
33
|
export const DEFAULT_SITE_CHROME = {
|
|
31
34
|
nav: "full",
|
|
@@ -92,17 +95,24 @@ export function validateTemplateLayout(value, subject) {
|
|
|
92
95
|
return result;
|
|
93
96
|
}
|
|
94
97
|
/**
|
|
95
|
-
* Fill every field a `TemplateLayout` leaves unset — from `
|
|
96
|
-
*
|
|
97
|
-
* `DEFAULT_SITE_CHROME`. The explicit `layout` wins field by
|
|
98
|
-
* template can wear a design and still override one knob of it.
|
|
98
|
+
* Fill every field a `TemplateLayout` leaves unset — from `designLayout`
|
|
99
|
+
* (a design package's own resolved `layout`, #6638/#6813 D4) when one is
|
|
100
|
+
* given, then from `DEFAULT_SITE_CHROME`. The explicit `layout` wins field by
|
|
101
|
+
* field, so a template can wear a design and still override one knob of it.
|
|
102
|
+
*
|
|
103
|
+
* `designLayout` is typed `TemplateLayout`, but a `.mjs` caller
|
|
104
|
+
* (`build-template-manifest.mjs`) cannot be compile-time checked the way a
|
|
105
|
+
* `.ts` caller can, so the type alone cannot stop a caller passing a design
|
|
106
|
+
* NAME (a string) rather than its resolved layout: a runtime guard throws a
|
|
107
|
+
* `TypeError` naming what was given, rather than silently resolving to the
|
|
108
|
+
* defaults.
|
|
99
109
|
*/
|
|
100
|
-
export function resolveSiteChrome(layout,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
throw new TemplateError(`unknown design "${design}". Declared: ${Object.keys(DESIGN_LAYOUTS).join(", ")}`);
|
|
110
|
+
export function resolveSiteChrome(layout, designLayout) {
|
|
111
|
+
if (designLayout !== undefined &&
|
|
112
|
+
(typeof designLayout !== "object" || designLayout === null || Array.isArray(designLayout))) {
|
|
113
|
+
throw new TypeError(`resolveSiteChrome: designLayout must be a layout object, got ${JSON.stringify(designLayout)}`);
|
|
105
114
|
}
|
|
115
|
+
const fromDesign = designLayout ?? {};
|
|
106
116
|
return {
|
|
107
117
|
nav: layout?.nav ?? fromDesign.nav ?? DEFAULT_SITE_CHROME.nav,
|
|
108
118
|
footer: layout?.footer ?? fromDesign.footer ?? DEFAULT_SITE_CHROME.footer,
|