create-ailk 0.8.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 +68 -0
- package/component-catalog.json +279 -45
- 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 +92 -4
- 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/license-key-files.d.ts +9 -0
- package/dist/lib/license-key-files.js +2 -2
- package/dist/lib/module-license-gate.d.ts +19 -9
- package/dist/lib/module-license-gate.js +19 -8
- 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 +12 -2
- package/dist/parse-args.js +74 -13
- package/dist/programmatic.d.ts +30 -6
- package/dist/programmatic.js +206 -17
- 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 +141 -8
- package/dist/starter-grid.d.ts +32 -16
- package/dist/starter-grid.js +129 -33
- package/dist/surfaces.d.ts +41 -9
- package/dist/surfaces.js +200 -83
- package/dist/template-manifest.d.ts +30 -13
- package/dist/template-manifest.js +49 -24
- package/package.json +5 -4
- package/site-templates/_base/content/en/UseCase/persona-1.mdx +7 -6
- package/site-templates/_base/content/en/UseCase/persona-2.mdx +7 -6
- package/site-templates/_base/content/en/UseCase/persona-3.mdx +7 -6
- 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 +98 -6
- package/site-templates/marketing-site/DESIGN.md +98 -6
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +60 -27
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +50 -30
- package/site-templates/marketing-site-free/DESIGN.md +98 -6
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +60 -24
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +44 -27
- package/site-templates/marketing-site-free/ailk-template.json +1 -1
- package/site-templates/saas-product/DESIGN.md +98 -6
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.json +127 -18
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.md +74 -20
- package/site-templates/shapes/local-business-service/ailk-template.json +1 -1
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.json +127 -18
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.md +74 -20
- package/site-templates/shapes/organization-service/ailk-template.json +1 -1
- package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.json +127 -18
- package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.md +75 -21
- package/site-templates/shapes/person-service/ailk-template.json +1 -1
- package/site-templates/shapes/person-service/content/en/AboutPage/about.mdx +1 -1
- package/site-templates/shapes/person-service/content/en/HomePage/home.mdx +2 -2
- package/site-templates/shapes/person-service/content/en/PortfolioList/portfolio.mdx +4 -2
- package/site-templates/shapes/person-service/content/en/ServicesList/services.mdx +1 -1
- package/templates/.claude/rules/architecture.md +7 -0
- package/templates/.claude/skills/README.md +3 -2
- 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 -76
- 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-deploy/references/env-vars-template.md +28 -11
- 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 +98 -6
- package/templates/README.md +50 -38
- package/templates/apps/api/.env.example +2 -1
- 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/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/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/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 +28 -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/revalidation.ts +60 -15
- package/templates/apps/mcp/__tests__/tools/create_project.test.ts +30 -24
- 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]/{use-cases → for}/[slug]/page.tsx +4 -3
- 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 +35 -19
- package/templates/apps/web/app/[locale]/newsletter/page.tsx +1 -1
- package/templates/apps/web/app/[locale]/portfolio/[slug]/page.tsx +1 -1
- package/templates/apps/web/app/[locale]/portfolio/page.tsx +1 -1
- 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 +14 -13
- package/templates/apps/web/lib/site-brand.tsx +13 -9
- 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 +23 -9
- package/templates/apps/web/middleware.ts +28 -4
- 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 +40 -1
- package/templates/content/en/HomePage/home.mdx +2 -6
- package/templates/content/en/LandingPage/meet.mdx +59 -0
- package/templates/content/en/UseCasesList/{use-cases.mdx → for.mdx} +19 -18
- package/templates/database/CHANGELOG.md +7 -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 -52
- package/site-templates/minimalism-site/DESIGN.md +0 -162
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +0 -458
- 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 -52
- 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/PurchasePolicyPage/purchase-policy.mdx +0 -123
- 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/content/en/Service/first-engagement.mdx +0 -38
- package/site-templates/minimalism-site/content/en/Service/second-engagement.mdx +0 -38
- package/site-templates/minimalism-site/content/en/Service/third-engagement.mdx +0 -38
- package/site-templates/minimalism-site/content/en/ServicesList/services.mdx +0 -73
- package/site-templates/minimalism-site/flows/referral.flow.json +0 -71
- package/site-templates/minimalism-site/provenance.json +0 -4
- package/templates/.claude/skills/add-page/templates/route.tsx.tmpl +0 -28
- package/templates/apps/web/app/[locale]/book-a-call/page.tsx +0 -50
- package/templates/apps/web/app/[locale]/use-cases/__tests__/page-renderer-migration.test.tsx +0 -72
- /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/dist/programmatic.d.ts
CHANGED
|
@@ -16,11 +16,12 @@
|
|
|
16
16
|
* const result = scaffoldSync({ targetDir: '/tmp/my-site', projectName: 'my-site' });
|
|
17
17
|
*/
|
|
18
18
|
import { type AddModuleResult } from "./add-module.js";
|
|
19
|
+
import { type SyncClaudeMdResult } from "./claude-md.js";
|
|
19
20
|
import { CATALOG_TARGET_PATH } from "./component-catalog.js";
|
|
20
21
|
import { type ModuleName } from "./parse-args.js";
|
|
21
22
|
import { type TemplateLayout } from "./site-chrome.js";
|
|
22
23
|
import { type SyncRoutesResult } from "./sync-routes.js";
|
|
23
|
-
import { type
|
|
24
|
+
import { type StarterShape } from "./template-manifest.js";
|
|
24
25
|
export interface ScaffoldOptions {
|
|
25
26
|
/** Absolute or relative path to the target directory. Must not exist or be empty. */
|
|
26
27
|
targetDir: string;
|
|
@@ -118,15 +119,23 @@ export interface ScaffoldOptions {
|
|
|
118
119
|
*/
|
|
119
120
|
layout?: TemplateLayout;
|
|
120
121
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
122
|
+
* A design name or a directory holding a DESIGN.md (#6638; widened to a
|
|
123
|
+
* path #6813 D1/D3). `scaffold()` sets it from the resolved template's
|
|
123
124
|
* `design`; a caller may name one to OVERRIDE the template's — the
|
|
124
125
|
* shape × design pairing `verify-scaffold.mjs --design` renders (#6638
|
|
125
126
|
* DD-4), and the CLI's `--design` beside `--shape` (#6754). Unset (every
|
|
126
127
|
* existing `scaffoldSync` caller) resolves exactly as before. Gated
|
|
127
|
-
* (#6760): a design
|
|
128
|
+
* (#6760): a non-Free design needs a Pro or Pro Plus level.
|
|
128
129
|
*/
|
|
129
|
-
design?:
|
|
130
|
+
design?: string;
|
|
131
|
+
/**
|
|
132
|
+
* INTERNAL — the resolved design's own layout knobs (#6813 D2/D4/D8),
|
|
133
|
+
* populated by `scaffold()` from `resolveDesign(...).pkg.layout` before
|
|
134
|
+
* calling `scaffoldSync`. Never set by a CLI/MCP caller. Replaces the
|
|
135
|
+
* design-NAME lookup `scaffoldSync` used to do internally through the
|
|
136
|
+
* code registry that has since been deleted (#6638's per-name table).
|
|
137
|
+
*/
|
|
138
|
+
designLayout?: TemplateLayout;
|
|
130
139
|
}
|
|
131
140
|
export interface ScaffoldResult {
|
|
132
141
|
/** Resolved absolute path to the scaffolded directory. */
|
|
@@ -138,11 +147,13 @@ export interface ScaffoldResult {
|
|
|
138
147
|
/**
|
|
139
148
|
* The starter this scaffold resolved (#6754 D5) — set whenever a template
|
|
140
149
|
* was resolved, from `template` or from `shape`; absent for the bare overlay.
|
|
150
|
+
* `design` is the applied design's recorded name (#6813 D6): the shipped
|
|
151
|
+
* registry name, or the package's own `name` for an unrecognized one.
|
|
141
152
|
*/
|
|
142
153
|
starter?: {
|
|
143
154
|
template: string;
|
|
144
155
|
shape: StarterShape;
|
|
145
|
-
design:
|
|
156
|
+
design: string;
|
|
146
157
|
};
|
|
147
158
|
/**
|
|
148
159
|
* The effective module set this scaffold installed (#6759 D6) — what
|
|
@@ -258,6 +269,19 @@ export interface SyncCatalogResult {
|
|
|
258
269
|
export declare function syncCatalogSync(opts: {
|
|
259
270
|
targetDir: string;
|
|
260
271
|
}): SyncCatalogResult;
|
|
272
|
+
/**
|
|
273
|
+
* Rewrite the template-owned sections of an already-scaffolded repo's root
|
|
274
|
+
* `CLAUDE.md` to the template this create-ailk ships (#6904 D3). The logic
|
|
275
|
+
* lives in `claude-md.ts` (a module jest imports directly); this wrapper
|
|
276
|
+
* only supplies the package's `templates/CLAUDE.md` — the `syncRoutesSync`
|
|
277
|
+
* split. `templateText` stands in for the shipped template in tests.
|
|
278
|
+
*/
|
|
279
|
+
export declare function syncClaudeMdSync(opts: {
|
|
280
|
+
targetDir: string;
|
|
281
|
+
templateText?: string;
|
|
282
|
+
}): SyncClaudeMdResult;
|
|
283
|
+
/** Re-exported beside the other sync results so consumers see one surface. */
|
|
284
|
+
export type { SyncClaudeMdResult } from "./claude-md.js";
|
|
261
285
|
export interface SyncRoutesSyncOptions {
|
|
262
286
|
/** The already-scaffolded repo to sync (absolute or relative). */
|
|
263
287
|
targetDir: string;
|
package/dist/programmatic.js
CHANGED
|
@@ -19,20 +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 {
|
|
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";
|
|
31
37
|
import { RECOGNIZED_SHAPES, RESERVED_SHAPES, } from "./parse-args.js";
|
|
32
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";
|
|
33
41
|
import { GATEABLE_SURFACES, MODULE_SURFACES, NAV_CONFIG_PATH, excludedRoutePaths, renderNavConfig, resolveSurfaces, } from "./surfaces.js";
|
|
34
42
|
import { syncRoutes } from "./sync-routes.js";
|
|
35
|
-
import { BASE_LAYER_DIR, SHAPE_STARTERS,
|
|
43
|
+
import { BASE_LAYER_DIR, SHAPE_STARTERS, TEMPLATE_MANIFESTS, TemplateError, mergeRoster, resolveTemplate, resolveTemplateLineage, rosterDeclares, templateDelivery, templateTreeDir, } from "./template-manifest.js";
|
|
36
44
|
import { validateTarget } from "./validate.js";
|
|
37
45
|
const PKG_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
38
46
|
const TEMPLATES_DIR = resolve(PKG_ROOT, "templates");
|
|
@@ -45,6 +53,8 @@ const TEMPLATES_DIR = resolve(PKG_ROOT, "templates");
|
|
|
45
53
|
* `package.json` `files` instead.
|
|
46
54
|
*/
|
|
47
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");
|
|
48
58
|
const VERSIONS_FILE = resolve(PKG_ROOT, "versions.json");
|
|
49
59
|
function readVersionMap() {
|
|
50
60
|
if (!existsSync(VERSIONS_FILE)) {
|
|
@@ -76,6 +86,24 @@ function countFiles(dir) {
|
|
|
76
86
|
}
|
|
77
87
|
return count;
|
|
78
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
|
+
}
|
|
79
107
|
/**
|
|
80
108
|
* The template a `shape` scaffolds (#6754 D5, extended #6757 D6 row 1): the
|
|
81
109
|
* one FREE-delivery manifest of that shape — a declared `--template` alias
|
|
@@ -128,6 +156,9 @@ export function scaffoldSync(opts) {
|
|
|
128
156
|
excludePaths: excludedRoutePaths(enabledSurfaces),
|
|
129
157
|
markers: buildScaffoldMarkers(projectName),
|
|
130
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));
|
|
131
162
|
// Derive + write the module-set-driven architecture.yaml (spec #3787). This
|
|
132
163
|
// is the single source of the scaffolded site's code-boundary config and
|
|
133
164
|
// closes the dangling `architecture_rules: architecture.yaml` reference in
|
|
@@ -144,7 +175,7 @@ export function scaffoldSync(opts) {
|
|
|
144
175
|
// template's `layout` block; a bare `scaffoldSync` call (every existing
|
|
145
176
|
// synchronous caller) writes DEFAULT_SITE_CHROME, byte-identical to the
|
|
146
177
|
// monorepo's own committed apps/web/lib/site-chrome.ts.
|
|
147
|
-
writeFileSync(join(targetAbs, SITE_CHROME_PATH), renderSiteChrome(resolveSiteChrome(opts.layout, opts.
|
|
178
|
+
writeFileSync(join(targetAbs, SITE_CHROME_PATH), renderSiteChrome(resolveSiteChrome(opts.layout, opts.designLayout)));
|
|
148
179
|
// Opt-in only (#4636). Every write above this line is unconditional, so a
|
|
149
180
|
// scaffold with `withCatalog` unset touches exactly the files it did before.
|
|
150
181
|
if (opts.withCatalog === true)
|
|
@@ -209,12 +240,32 @@ export async function scaffold(opts) {
|
|
|
209
240
|
: opts.shape !== undefined
|
|
210
241
|
? resolveShapeTemplate(opts.shape)
|
|
211
242
|
: undefined;
|
|
212
|
-
// #
|
|
213
|
-
//
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
+
});
|
|
217
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;
|
|
218
269
|
// #5618 D2 — the `extends` chain is resolved in the client registry before
|
|
219
270
|
// any fetch: an unknown parent or a cycle is refused here, with the target
|
|
220
271
|
// still non-existent, like the four refusals below. The registry is the
|
|
@@ -260,11 +311,11 @@ export async function scaffold(opts) {
|
|
|
260
311
|
// #6760 — `resolveModuleSet` never runs on this branch (NFR-4), so no
|
|
261
312
|
// level was derived above. Derive one — from the registry's per-module
|
|
262
313
|
// answers, never a key claim (D2) — only when the caller CHOSE a
|
|
263
|
-
// non
|
|
314
|
+
// non-Free design alongside the template (`opts.design`, #6813 D7). An
|
|
264
315
|
// alias's own declared design is never gated (#6778 option (c)), so a
|
|
265
316
|
// plain `--template minimalism-site` run makes no extra network call and
|
|
266
317
|
// is never refused on its design.
|
|
267
|
-
if (
|
|
318
|
+
if (chosenDesign !== undefined && !chosenDesign.free) {
|
|
268
319
|
({ level } = await deriveLevel({
|
|
269
320
|
licenseKey,
|
|
270
321
|
...(opts.moduleRegistryUrl !== undefined
|
|
@@ -317,8 +368,11 @@ export async function scaffold(opts) {
|
|
|
317
368
|
// Only the caller's chosen design is gated (#6778 option (c)); a template's
|
|
318
369
|
// own declared design is not. `level ?? "free"` fails closed: a
|
|
319
370
|
// `--modules`-keep run under-reports its level by design (NFR-2), so a
|
|
320
|
-
// chosen non
|
|
321
|
-
|
|
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);
|
|
322
376
|
// The template's OWN bundle, fetched here — before `scaffoldSync` — so the
|
|
323
377
|
// fourth refusal (the template's bundle unpublished, parent record D17 /
|
|
324
378
|
// spec 5481 § 3 DD5 row 4) throws with the target still non-existent, like
|
|
@@ -372,7 +426,9 @@ export async function scaffold(opts) {
|
|
|
372
426
|
// design's knobs (#6638), and a `standard` one to the same defaults
|
|
373
427
|
// `scaffoldSync` already writes for a templateless scaffold.
|
|
374
428
|
...(template?.layout !== undefined ? { layout: template.layout } : {}),
|
|
375
|
-
|
|
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 } : {}),
|
|
376
432
|
});
|
|
377
433
|
// Threaded to extractModuleBundle for `package.json` patch targets'
|
|
378
434
|
// `workspace:*` substitution (spec 5418 § 3 D2) — the same version map
|
|
@@ -437,6 +493,11 @@ export async function scaffold(opts) {
|
|
|
437
493
|
//
|
|
438
494
|
// One install step, two source roots (DD7): an extracted staging dir on the
|
|
439
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;
|
|
440
501
|
if (template !== undefined) {
|
|
441
502
|
// #5612 D1/D2 — the INSTALLED template's own `ailk-template.json` layout
|
|
442
503
|
// (validated by `readAilkTemplateManifest`), if it declares one. Read
|
|
@@ -472,21 +533,131 @@ export async function scaffold(opts) {
|
|
|
472
533
|
ownership,
|
|
473
534
|
});
|
|
474
535
|
})();
|
|
536
|
+
installedLayout = installed.layout;
|
|
475
537
|
if (installed.layout !== undefined) {
|
|
476
|
-
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)));
|
|
477
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 }));
|
|
478
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
|
+
}
|
|
479
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);
|
|
480
651
|
// #6683 D9 — persisted only after every layer landed, and only when the
|
|
481
652
|
// caller passed `license`: a scaffold without it writes nothing new.
|
|
482
653
|
if (opts.license !== undefined) {
|
|
483
654
|
writeLicenseKeyFiles(result.targetDir, opts.license);
|
|
484
655
|
}
|
|
485
656
|
// #6754 D4/D5 — the resolved starter, so the CLI names what it built.
|
|
486
|
-
if (template !== undefined &&
|
|
657
|
+
if (template !== undefined && resolvedDesign !== undefined) {
|
|
487
658
|
return {
|
|
488
659
|
...result,
|
|
489
|
-
starter: { template: template.name, shape: template.shape, design },
|
|
660
|
+
starter: { template: template.name, shape: template.shape, design: resolvedDesign.recorded },
|
|
490
661
|
};
|
|
491
662
|
}
|
|
492
663
|
return result;
|
|
@@ -506,12 +677,17 @@ export async function scaffold(opts) {
|
|
|
506
677
|
* the content directory of a surface this scaffold ENABLES is kept too, so
|
|
507
678
|
* an explicit `--surfaces pricing` on a template that does not roster
|
|
508
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.
|
|
509
684
|
*/
|
|
510
685
|
function pruneUnrosteredSeeds(targetDir, roster, surfaces, ownership) {
|
|
511
686
|
// `installBundleTree`'s ownership label for the base layer: it installs
|
|
512
687
|
// with `templateRequires: []`, so it records as a template. The overlay
|
|
513
688
|
// (the `templates/` snapshot) records no owner at all.
|
|
514
689
|
const shared = new Set([undefined, `template "${BASE_LAYER_DIR}"`]);
|
|
690
|
+
const declared = withMeetPlaceholderSeeds(roster);
|
|
515
691
|
const enabledDirs = GATEABLE_SURFACES.filter((surface) => surfaces.includes(surface.id)).flatMap((surface) => surface.content.map((dir) => `${dir.replace("<locale>", "en")}/`));
|
|
516
692
|
const root = join(targetDir, "content", "en");
|
|
517
693
|
if (!existsSync(root))
|
|
@@ -531,7 +707,7 @@ function pruneUnrosteredSeeds(targetDir, roster, surfaces, ownership) {
|
|
|
531
707
|
continue;
|
|
532
708
|
const path = relative(targetDir, abs).split(sep).join("/");
|
|
533
709
|
if (shared.has(ownership.get(path)) &&
|
|
534
|
-
!rosterDeclares(
|
|
710
|
+
!rosterDeclares(declared, path) &&
|
|
535
711
|
!enabledDirs.some((prefix) => path.startsWith(prefix))) {
|
|
536
712
|
rmSync(abs);
|
|
537
713
|
if (readdirSync(dir).length === 0)
|
|
@@ -664,6 +840,19 @@ export function syncCatalogSync(opts) {
|
|
|
664
840
|
catalogPath: writeComponentCatalog(targetAbs),
|
|
665
841
|
};
|
|
666
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
|
+
}
|
|
667
856
|
/**
|
|
668
857
|
* Compare an already-scaffolded repo's route tree with the template this
|
|
669
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,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Level } from "./lib/module-tier.js";
|
|
2
|
+
import type { ModuleName } from "./parse-args.js";
|
|
3
|
+
import { type SiteRoster } from "./site-roster.js";
|
|
4
|
+
import type { MarkerMap } from "./substitute.js";
|
|
5
|
+
export interface PlanSiteManifestOptions {
|
|
6
|
+
targetDir: string;
|
|
7
|
+
/** The installed manifest, read after every layer landed. */
|
|
8
|
+
roster: SiteRoster;
|
|
9
|
+
/** The effective module set the scaffold installed. */
|
|
10
|
+
modules: readonly ModuleName[];
|
|
11
|
+
/** The level the key path derived, or undefined when it derived none. */
|
|
12
|
+
level: Level | undefined;
|
|
13
|
+
siteTemplatesDir: string;
|
|
14
|
+
templatesDir: string;
|
|
15
|
+
markers: MarkerMap;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The manifest's next text: `modules` (and `level`, when derived) after
|
|
19
|
+
* `requires`, and a row for every route present on disk.
|
|
20
|
+
*
|
|
21
|
+
* @throws AddModuleRefusedError when the file already records `modules` or
|
|
22
|
+
* `level`, or its layout is not the generator's (`rows` last, `requires`
|
|
23
|
+
* at the top level) — guessing would silently mis-record the site.
|
|
24
|
+
*/
|
|
25
|
+
export declare function planSiteManifest(opts: PlanSiteManifestOptions): string;
|