create-ailk 0.13.0 → 0.14.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 +12 -4
- package/component-catalog.json +247 -33
- package/component-catalog.md +10 -6
- package/dist/apply-design.d.ts +21 -2
- package/dist/apply-design.js +120 -19
- package/dist/cli.js +1 -0
- package/dist/design-compile.d.ts +70 -2
- package/dist/design-compile.js +97 -1
- package/dist/design-home.d.ts +74 -0
- package/dist/design-home.js +256 -0
- package/dist/design-package.d.ts +39 -2
- package/dist/design-package.js +223 -9
- package/dist/design-presets.d.ts +17 -1
- package/dist/design-presets.js +17 -5
- package/dist/design-scaffold.d.ts +6 -25
- package/dist/design-scaffold.js +26 -74
- package/dist/index.d.ts +1 -0
- package/dist/lib/design-checks.d.ts +12 -1
- package/dist/lib/design-checks.js +38 -2
- package/dist/lib/module-license-gate.d.ts +9 -0
- package/dist/lib/module-license-gate.js +18 -0
- package/dist/programmatic.js +70 -26
- package/dist/site-chrome.d.ts +38 -0
- package/dist/site-chrome.js +109 -2
- package/dist/site-fonts.d.ts +21 -0
- package/dist/site-fonts.js +54 -0
- package/dist/site-roster.d.ts +16 -0
- package/dist/site-roster.js +81 -0
- package/dist/starter-grid.js +15 -0
- package/dist/surfaces.js +12 -0
- package/dist/team-profiles.d.ts +29 -0
- package/dist/team-profiles.js +43 -0
- package/package.json +3 -3
- package/release-scope.json +1 -1
- package/site-templates/_base/content/en/TeamList/team.mdx +68 -0
- package/site-templates/_base/content/en/TeamMember/first-member.mdx +22 -0
- package/site-templates/_base/content/en/TeamMember/second-member.mdx +22 -0
- package/site-templates/_base/content/en/TeamMember/third-member.mdx +22 -0
- package/site-templates/designs/minimalism/DESIGN.md +164 -34
- package/site-templates/designs/minimalism/home/local-business-service.mdx +119 -0
- package/site-templates/designs/minimalism/home/organization-product.mdx +132 -0
- package/site-templates/designs/minimalism/home/organization-service.mdx +131 -0
- package/site-templates/designs/minimalism/home/person-service.mdx +101 -0
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +34 -1
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +20 -0
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +34 -1
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +22 -1
- package/site-templates/marketing-site-free/ailk-template.json +1 -1
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.json +34 -1
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.md +26 -7
- package/site-templates/shapes/local-business-service/ailk-template.json +1 -1
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.json +34 -1
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.md +26 -7
- package/site-templates/shapes/organization-service/ailk-template.json +1 -1
- package/templates/README.md +2 -0
- package/templates/apps/api/CLAUDE.md +4 -1
- package/templates/apps/mcp/package.json +1 -1
- package/templates/apps/web/CLAUDE.md +11 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/settings.test.tsx +10 -0
- package/templates/apps/web/app/[locale]/(authed)/account/settings/__tests__/theme-modes.test.ts +42 -0
- package/templates/apps/web/app/[locale]/(authed)/account/settings/theme-modes.ts +8 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +14 -1
- package/templates/apps/web/app/[locale]/contact/__tests__/site-contact-lines.test.ts +69 -0
- package/templates/apps/web/app/[locale]/contact/site-contact-lines.ts +64 -0
- package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +4 -4
- package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +15 -3
- package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +17 -6
- package/templates/apps/web/app/[locale]/for/__tests__/page-renderer-migration.test.tsx +14 -1
- package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +4 -6
- package/templates/apps/web/app/[locale]/layout.tsx +156 -53
- package/templates/apps/web/app/[locale]/page.tsx +37 -0
- package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +4 -6
- package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +15 -2
- package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +43 -6
- package/templates/apps/web/app/[locale]/services/page.tsx +14 -2
- package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/team/[slug]/page.tsx +45 -0
- package/templates/apps/web/app/[locale]/team/__tests__/team-routes.test.tsx +155 -0
- package/templates/apps/web/app/[locale]/team/page.tsx +49 -0
- package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +14 -1
- package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/not-found.tsx +47 -0
- package/templates/apps/web/lib/__tests__/profile-back-link.test.ts +95 -0
- package/templates/apps/web/lib/__tests__/site-theme-head.test.ts +70 -0
- package/templates/apps/web/lib/__tests__/site-theme.test.ts +64 -1
- package/templates/apps/web/lib/profile-back-link.ts +51 -0
- package/templates/apps/web/lib/site-chrome.ts +12 -0
- package/templates/apps/web/lib/site-fonts.ts +15 -0
- package/templates/apps/web/lib/site-logo.tsx +39 -1
- package/templates/apps/web/lib/site-theme-head.ts +33 -0
- package/templates/apps/web/lib/site-theme.ts +13 -1
- package/templates/apps/web/messages/en.json +7 -1
- package/templates/database/CHANGELOG.md +7 -0
- package/templates/database/package.json +1 -1
- package/versions.json +34 -34
- package/site-templates/designs/minimalism/sample/README.md +0 -48
- package/site-templates/designs/minimalism/sample/content/en/HomePage/home.mdx +0 -244
- package/site-templates/designs/minimalism/sample/public/avatar-placeholder-1.svg +0 -7
- package/site-templates/designs/minimalism/sample/public/avatar-placeholder-2.svg +0 -7
- package/site-templates/designs/minimalism/sample/public/avatar-placeholder-3.svg +0 -7
- package/site-templates/designs/minimalism/sample/public/avatar-placeholder-4.svg +0 -7
- package/site-templates/designs/minimalism/sample/public/avatar-placeholder-5.svg +0 -7
- package/site-templates/designs/minimalism/sample/public/avatar-placeholder-6.svg +0 -7
- package/site-templates/designs/minimalism/sample/public/hero-backdrop.svg +0 -17
- package/site-templates/designs/minimalism/sample/public/logo-placeholder-1.svg +0 -6
- package/site-templates/designs/minimalism/sample/public/logo-placeholder-2.svg +0 -6
- package/site-templates/designs/minimalism/sample/public/logo-placeholder-3.svg +0 -6
- package/site-templates/designs/minimalism/sample/public/wordmark-placeholder.svg +0 -28
package/dist/design-presets.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { type DesignSections } from "./design-package.js";
|
|
|
2
2
|
export interface SectionLookCatalogEntry {
|
|
3
3
|
/** The look group, in registry order (D2). */
|
|
4
4
|
readonly look: readonly string[];
|
|
5
|
+
/** The type's rendering component (#7743 D6 — what the site manifest records), when the catalog names one. */
|
|
6
|
+
readonly component: string | undefined;
|
|
5
7
|
/** The type's `data` JSON Schema `properties` (the catalog's `schema.properties`). */
|
|
6
8
|
readonly properties: Readonly<Record<string, unknown>>;
|
|
7
9
|
}
|
|
@@ -37,6 +39,9 @@ export interface LocatedDataEntry {
|
|
|
37
39
|
export interface LocatedSection {
|
|
38
40
|
readonly type: string;
|
|
39
41
|
readonly form: "flow" | "block";
|
|
42
|
+
/** #7743 D3 — the element's `[start, end)` span: flow, its `{ … }`; block, from its `- ` to its last line's end. */
|
|
43
|
+
readonly start: number;
|
|
44
|
+
readonly end: number;
|
|
40
45
|
/** Flow: the index of `data`'s `{`. Block: the end of the `data:` line (before its newline). */
|
|
41
46
|
readonly dataOpen: number;
|
|
42
47
|
/** Flow: the index of `data`'s `}`. Block: `dataOpen`. */
|
|
@@ -45,6 +50,15 @@ export interface LocatedSection {
|
|
|
45
50
|
readonly entryColumn: number;
|
|
46
51
|
readonly entries: readonly LocatedDataEntry[];
|
|
47
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* A page's located sections, with the `[valueStart, valueEnd)` span of the
|
|
55
|
+
* whole `sections:` value (#7743 D3): from just after `sections:` (and the
|
|
56
|
+
* spaces after it) to the flow form's `]`, or the block form's last line end.
|
|
57
|
+
*/
|
|
58
|
+
export type LocatedSections = LocatedSection[] & {
|
|
59
|
+
readonly valueStart: number;
|
|
60
|
+
readonly valueEnd: number;
|
|
61
|
+
};
|
|
48
62
|
/**
|
|
49
63
|
* Locate a page's `sections[]` in its frontmatter — the prettier-formatted
|
|
50
64
|
* flow form (`sections:\n [ { "id": …, "type": …, "data": { … } }, … ]`)
|
|
@@ -52,7 +66,7 @@ export interface LocatedSection {
|
|
|
52
66
|
* Undefined when the page has no frontmatter or no column-0 `sections:` key.
|
|
53
67
|
* Any other shape throws `TemplateError` naming the element (D5).
|
|
54
68
|
*/
|
|
55
|
-
export declare function locatePageSections(text: string, file: string):
|
|
69
|
+
export declare function locatePageSections(text: string, file: string): LocatedSections | undefined;
|
|
56
70
|
export interface PresetPage {
|
|
57
71
|
readonly path: string;
|
|
58
72
|
readonly text: string;
|
|
@@ -64,6 +78,8 @@ export interface PresetApplication {
|
|
|
64
78
|
readonly sectionType: string;
|
|
65
79
|
readonly field: string;
|
|
66
80
|
}
|
|
81
|
+
/** The frontmatter's column-0 `type:` value, or undefined when it has none. */
|
|
82
|
+
export declare function pageTypeOf(text: string, file: string): string | undefined;
|
|
67
83
|
/**
|
|
68
84
|
* Set one look field on section `index` of a page, splicing only its value
|
|
69
85
|
* span (D5): an existing value is replaced; an absent field is inserted as
|
package/dist/design-presets.js
CHANGED
|
@@ -44,6 +44,7 @@ export function readSectionLookCatalog(catalogPath) {
|
|
|
44
44
|
const properties = entry.schema["properties"];
|
|
45
45
|
out[type] = {
|
|
46
46
|
look: entry.look.map(String),
|
|
47
|
+
component: typeof entry.component === "string" ? entry.component : undefined,
|
|
47
48
|
properties: isPlainObject(properties) ? properties : {},
|
|
48
49
|
};
|
|
49
50
|
}
|
|
@@ -397,6 +398,8 @@ function locateFlow(text, open, end, file) {
|
|
|
397
398
|
out.push({
|
|
398
399
|
type,
|
|
399
400
|
form: "flow",
|
|
401
|
+
start: element.open,
|
|
402
|
+
end: element.close + 1,
|
|
400
403
|
dataOpen: data.open,
|
|
401
404
|
dataClose: data.close,
|
|
402
405
|
entryColumn: -1,
|
|
@@ -410,7 +413,7 @@ function locateFlow(text, open, end, file) {
|
|
|
410
413
|
}
|
|
411
414
|
if (text[i] !== "]")
|
|
412
415
|
cannotLocate(file, undefined, "the closing ]");
|
|
413
|
-
return out;
|
|
416
|
+
return { sections: out, close: i };
|
|
414
417
|
}
|
|
415
418
|
function splitLines(text, start, end) {
|
|
416
419
|
const lines = [];
|
|
@@ -510,9 +513,12 @@ function locateBlock(text, lines, file) {
|
|
|
510
513
|
lastDeep = l.end;
|
|
511
514
|
}
|
|
512
515
|
close();
|
|
516
|
+
const last = item.filter((l) => !l.blank).at(-1);
|
|
513
517
|
return {
|
|
514
518
|
type,
|
|
515
519
|
form: "block",
|
|
520
|
+
start: item[0].start + itemIndent,
|
|
521
|
+
end: last.end,
|
|
516
522
|
dataOpen: data.end,
|
|
517
523
|
dataClose: data.end,
|
|
518
524
|
entryColumn,
|
|
@@ -545,20 +551,26 @@ export function locatePageSections(text, file) {
|
|
|
545
551
|
const nextKey = /\n(?=[^\s#-])/.exec(text.slice(afterKey, fmEnd));
|
|
546
552
|
const valueEnd = nextKey ? afterKey + nextKey.index : fmEnd;
|
|
547
553
|
const first = skipSpace(text, afterKey, valueEnd);
|
|
548
|
-
if (text[first] === "[")
|
|
549
|
-
|
|
554
|
+
if (text[first] === "[") {
|
|
555
|
+
const { sections, close } = locateFlow(text, first, valueEnd, file);
|
|
556
|
+
return Object.assign(sections, { valueStart: afterKey, valueEnd: close + 1 });
|
|
557
|
+
}
|
|
550
558
|
const lineEnd = text.indexOf("\n", afterKey);
|
|
551
559
|
if (lineEnd >= 0 && text.slice(afterKey, lineEnd).trim() === "") {
|
|
552
560
|
const lines = splitLines(text, lineEnd + 1, valueEnd);
|
|
553
561
|
const firstLine = lines.find((l) => !l.blank);
|
|
554
562
|
if (firstLine === undefined || text.startsWith("- ", firstLine.start + firstLine.indent)) {
|
|
555
|
-
|
|
563
|
+
const lastLine = lines.filter((l) => !l.blank).at(-1);
|
|
564
|
+
return Object.assign(locateBlock(text, lines, file), {
|
|
565
|
+
valueStart: afterKey,
|
|
566
|
+
valueEnd: lastLine?.end ?? afterKey,
|
|
567
|
+
});
|
|
556
568
|
}
|
|
557
569
|
}
|
|
558
570
|
return cannotLocate(file, undefined, "a flow or block sequence");
|
|
559
571
|
}
|
|
560
572
|
/** The frontmatter's column-0 `type:` value, or undefined when it has none. */
|
|
561
|
-
function pageTypeOf(text, file) {
|
|
573
|
+
export function pageTypeOf(text, file) {
|
|
562
574
|
const fm = /^---\r?\n([\s\S]*?)\r?\n---[ \t]*\r?$/m.exec(text);
|
|
563
575
|
if (!fm || fm.index !== 0)
|
|
564
576
|
return undefined;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type CompiledDesignTheme } from "./design-compile.js";
|
|
2
2
|
import { type DesignPackage } from "./design-package.js";
|
|
3
|
-
import type { LayerOwnership } from "./lib/extract-module-bundle.js";
|
|
4
3
|
/** One resolved `--design` reference (D2). */
|
|
5
4
|
export interface ResolvedDesign {
|
|
6
5
|
/** "name" when the reference was a registry name, "path" when a directory. */
|
|
@@ -38,9 +37,12 @@ export declare function resolveDesign(ref: string, opts: {
|
|
|
38
37
|
}): ResolvedDesign;
|
|
39
38
|
/**
|
|
40
39
|
* Render a compiled theme's `theme:` frontmatter block (D5) — `theme:`, each
|
|
41
|
-
* treatment present as a bare `key: value
|
|
42
|
-
* `
|
|
43
|
-
*
|
|
40
|
+
* treatment present as a bare `key: value` (#7739's `darkInk`/`figures` after
|
|
41
|
+
* `checkmarks`), the `typography:` group when compiled — every role and field
|
|
42
|
+
* in the compile's order, role keys unquoted, sizes as plain scalars (#7720
|
|
43
|
+
* D2, #7739 D2.5) — then `colors:` with `light:`/`dark:` sub-blocks, each
|
|
44
|
+
* colour slot as `slot: <JSON.stringify(value)>` in `DESIGN_COLOR_MAP`'s
|
|
45
|
+
* order. Ends with a trailing newline.
|
|
44
46
|
*/
|
|
45
47
|
export declare function renderThemeBlock(theme: CompiledDesignTheme): string;
|
|
46
48
|
/**
|
|
@@ -53,24 +55,3 @@ export declare function renderThemeBlock(theme: CompiledDesignTheme): string;
|
|
|
53
55
|
* @throws TemplateError when `text` carries no frontmatter to write into.
|
|
54
56
|
*/
|
|
55
57
|
export declare function spliceThemeBlock(text: string, theme: CompiledDesignTheme, file: string): string;
|
|
56
|
-
/**
|
|
57
|
-
* Install a design's `sample/` directory onto a scaffold (spec 6892 D4) — a
|
|
58
|
-
* FIXED, undeclared convention, not a design-package field: `sample/content/**`
|
|
59
|
-
* lands at the scaffold's `content/**`, `sample/public/**` at
|
|
60
|
-
* `apps/web/public/**`. A design with no `sample/` directory (e.g. `standard`)
|
|
61
|
-
* installs nothing.
|
|
62
|
-
*
|
|
63
|
-
* Refuses — before its first write, so a refused sample leaves `targetDir`
|
|
64
|
-
* untouched — any file outside those two roots, or any `.ts`/`.tsx`/`.js`/
|
|
65
|
-
* `.mjs` file (content only, the template §5.2 rule this design package
|
|
66
|
-
* amendment carries over). Every installed file's destination is recorded in
|
|
67
|
-
* `ownership` as `design:<name>` (`readDesignPackage(designDir).name`), the
|
|
68
|
-
* same call-scoped registry `installBundleTree` writes into.
|
|
69
|
-
*
|
|
70
|
-
* @throws Error on a file outside `content/`/`public/`, or a forbidden extension.
|
|
71
|
-
*/
|
|
72
|
-
export declare function installDesignSample(opts: {
|
|
73
|
-
designDir: string;
|
|
74
|
-
targetDir: string;
|
|
75
|
-
ownership: LayerOwnership;
|
|
76
|
-
}): void;
|
package/dist/design-scaffold.js
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* directory it reads is supplied by the caller — so it stays importable from
|
|
11
11
|
* Jest's CJS transform, unlike `programmatic.ts`.
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
14
|
-
import { join,
|
|
13
|
+
import { statSync } from "node:fs";
|
|
14
|
+
import { join, resolve } from "node:path";
|
|
15
15
|
import { isDeepStrictEqual } from "node:util";
|
|
16
16
|
import { DESIGN_COLOR_MAP } from "./design-compile.js";
|
|
17
17
|
import { readDesignPackage } from "./design-package.js";
|
|
@@ -82,9 +82,12 @@ export function resolveDesign(ref, opts) {
|
|
|
82
82
|
const RUNTIME_COLOR_SLOT_ORDER = DESIGN_COLOR_MAP.filter((row) => row.mode === "light").map((row) => row.runtime);
|
|
83
83
|
/**
|
|
84
84
|
* Render a compiled theme's `theme:` frontmatter block (D5) — `theme:`, each
|
|
85
|
-
* treatment present as a bare `key: value
|
|
86
|
-
* `
|
|
87
|
-
*
|
|
85
|
+
* treatment present as a bare `key: value` (#7739's `darkInk`/`figures` after
|
|
86
|
+
* `checkmarks`), the `typography:` group when compiled — every role and field
|
|
87
|
+
* in the compile's order, role keys unquoted, sizes as plain scalars (#7720
|
|
88
|
+
* D2, #7739 D2.5) — then `colors:` with `light:`/`dark:` sub-blocks, each
|
|
89
|
+
* colour slot as `slot: <JSON.stringify(value)>` in `DESIGN_COLOR_MAP`'s
|
|
90
|
+
* order. Ends with a trailing newline.
|
|
88
91
|
*/
|
|
89
92
|
export function renderThemeBlock(theme) {
|
|
90
93
|
const lines = ["theme:"];
|
|
@@ -96,6 +99,24 @@ export function renderThemeBlock(theme) {
|
|
|
96
99
|
lines.push(` placeholders: ${theme.placeholders}`);
|
|
97
100
|
if (theme.checkmarks !== undefined)
|
|
98
101
|
lines.push(` checkmarks: ${theme.checkmarks}`);
|
|
102
|
+
if (theme.buttonShape !== undefined)
|
|
103
|
+
lines.push(` buttonShape: ${theme.buttonShape}`);
|
|
104
|
+
if (theme.darkInk !== undefined)
|
|
105
|
+
lines.push(` darkInk: ${theme.darkInk}`);
|
|
106
|
+
if (theme.figures !== undefined)
|
|
107
|
+
lines.push(` figures: ${theme.figures}`);
|
|
108
|
+
if (theme.typography !== undefined) {
|
|
109
|
+
lines.push(" typography:");
|
|
110
|
+
for (const [role, fields] of Object.entries(theme.typography)) {
|
|
111
|
+
lines.push(` ${role}:`);
|
|
112
|
+
for (const [field, value] of Object.entries(fields)) {
|
|
113
|
+
lines.push(` ${field}: ${String(value)}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// #7719 D6 — present only for a design that uses `solid-dark`, or (#7739 D2.3) sets `darkInk: accent`.
|
|
118
|
+
if (theme.darkBands !== undefined)
|
|
119
|
+
lines.push(` darkBands: ${theme.darkBands}`);
|
|
99
120
|
lines.push(" colors:");
|
|
100
121
|
for (const mode of ["light", "dark"]) {
|
|
101
122
|
lines.push(` ${mode}:`);
|
|
@@ -149,72 +170,3 @@ export function spliceThemeBlock(text, theme, file) {
|
|
|
149
170
|
: [...lines.slice(0, themeStart), ...rendered, ...lines.slice(themeEnd)];
|
|
150
171
|
return next.join("\n");
|
|
151
172
|
}
|
|
152
|
-
/** Forbidden extensions for a design sample's files (spec 6892 D4) — content only, never code. */
|
|
153
|
-
const SAMPLE_FORBIDDEN_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs"];
|
|
154
|
-
function hasSampleForbiddenExtension(p) {
|
|
155
|
-
return SAMPLE_FORBIDDEN_EXTENSIONS.some((ext) => p.toLowerCase().endsWith(ext));
|
|
156
|
-
}
|
|
157
|
-
/** Every file under `dir`, as `/`-separated paths relative to `dir`. */
|
|
158
|
-
function listFiles(dir) {
|
|
159
|
-
const out = [];
|
|
160
|
-
const stack = [dir];
|
|
161
|
-
for (let cur = stack.pop(); cur !== undefined; cur = stack.pop()) {
|
|
162
|
-
for (const entry of readdirSync(cur, { withFileTypes: true })) {
|
|
163
|
-
const abs = join(cur, entry.name);
|
|
164
|
-
if (entry.isDirectory())
|
|
165
|
-
stack.push(abs);
|
|
166
|
-
else if (entry.isFile())
|
|
167
|
-
out.push(relative(dir, abs).split(sep).join("/"));
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
return out;
|
|
171
|
-
}
|
|
172
|
-
/** Where one `sample/` file lands on a scaffold (spec 6892 D4's fixed convention). */
|
|
173
|
-
function sampleDestination(rel) {
|
|
174
|
-
if (rel.startsWith("content/"))
|
|
175
|
-
return rel;
|
|
176
|
-
return `apps/web/public/${rel.slice("public/".length)}`;
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Install a design's `sample/` directory onto a scaffold (spec 6892 D4) — a
|
|
180
|
-
* FIXED, undeclared convention, not a design-package field: `sample/content/**`
|
|
181
|
-
* lands at the scaffold's `content/**`, `sample/public/**` at
|
|
182
|
-
* `apps/web/public/**`. A design with no `sample/` directory (e.g. `standard`)
|
|
183
|
-
* installs nothing.
|
|
184
|
-
*
|
|
185
|
-
* Refuses — before its first write, so a refused sample leaves `targetDir`
|
|
186
|
-
* untouched — any file outside those two roots, or any `.ts`/`.tsx`/`.js`/
|
|
187
|
-
* `.mjs` file (content only, the template §5.2 rule this design package
|
|
188
|
-
* amendment carries over). Every installed file's destination is recorded in
|
|
189
|
-
* `ownership` as `design:<name>` (`readDesignPackage(designDir).name`), the
|
|
190
|
-
* same call-scoped registry `installBundleTree` writes into.
|
|
191
|
-
*
|
|
192
|
-
* @throws Error on a file outside `content/`/`public/`, or a forbidden extension.
|
|
193
|
-
*/
|
|
194
|
-
export function installDesignSample(opts) {
|
|
195
|
-
const sampleDir = join(opts.designDir, "sample");
|
|
196
|
-
if (!existsSync(sampleDir))
|
|
197
|
-
return;
|
|
198
|
-
const name = readDesignPackage(opts.designDir).name;
|
|
199
|
-
const subject = `design:${name}`;
|
|
200
|
-
// README.md at the sample root is the ONE documented exception (spec 6892
|
|
201
|
-
// D5: "the narrative sections move to designs/<name>/sample/README.md,
|
|
202
|
-
// which is not installed") — every other file is real content and is
|
|
203
|
-
// validated against the two roots below.
|
|
204
|
-
const files = listFiles(sampleDir).filter((rel) => rel !== "README.md");
|
|
205
|
-
for (const rel of files) {
|
|
206
|
-
if (!rel.startsWith("content/") && !rel.startsWith("public/")) {
|
|
207
|
-
throw new Error(`${subject}: sample file "${rel}" is outside content/ and public/ (spec 6892 D4)`);
|
|
208
|
-
}
|
|
209
|
-
if (hasSampleForbiddenExtension(rel)) {
|
|
210
|
-
throw new Error(`${subject}: sample file "${rel}" is a code file — a design sample is content only (spec 6892 D4)`);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
for (const rel of files) {
|
|
214
|
-
const destinationRel = sampleDestination(rel);
|
|
215
|
-
const destinationAbs = resolve(opts.targetDir, destinationRel);
|
|
216
|
-
mkdirSync(join(destinationAbs, ".."), { recursive: true });
|
|
217
|
-
copyFileSync(join(sampleDir, rel), destinationAbs);
|
|
218
|
-
opts.ownership.set(destinationRel, subject);
|
|
219
|
-
}
|
|
220
|
-
}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export { DEFAULT_AILK_MODULE_REGISTRY_URL, FetchModuleError, fetchModule, type F
|
|
|
10
10
|
export { extractModuleBundle, type ExistingFilePolicy, type ExtractModuleBundleOptions, type InstallReportRow, } from "./lib/extract-module-bundle.js";
|
|
11
11
|
export { ADD_MODULE_HELP, AddModuleRefusedError, applyModuleToScaffold, formatAddModuleReport, parseAddModuleArgs, type AddModuleReportRow, type AddModuleResult, type ApplyModuleOptions, } from "./add-module.js";
|
|
12
12
|
export { APPLY_DESIGN_HELP, applyDesign, formatApplyDesignReport, parseApplyDesignArgs, type ApplyDesignArgs, type ApplyDesignChange, type ApplyDesignOptions, type ApplyDesignResult, } from "./apply-design.js";
|
|
13
|
+
export type { HomeSeedOutcome } from "./design-home.js";
|
|
13
14
|
export { SITE_ROSTER_PATH, readSiteRoster, siteRosterProblems, } from "./site-roster.js";
|
|
14
15
|
export { SYNC_ROUTES_HELP, formatSyncRoutesReport, listTemplateRouteDirs, parseSyncRoutesArgs, syncRoutes, type RouteReport, type SyncRoutesOptions, type SyncRoutesResult, } from "./sync-routes.js";
|
|
15
16
|
export { deriveArchitecture } from "./derive-architecture.js";
|
|
@@ -46,7 +46,18 @@ export type JsonLdDriftVerdict = {
|
|
|
46
46
|
changed: DriftNode[];
|
|
47
47
|
reason: string;
|
|
48
48
|
};
|
|
49
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* `after` with the standard home put back when the seeded home's JSON-LD is
|
|
51
|
+
* exactly the standard home's node followed by one `Person` per
|
|
52
|
+
* `TEAM_SEED_MEMBERS` member, in order (the people slide's `team` section,
|
|
53
|
+
* #7744 D8). Any other home, or a home the people slide did not change, is
|
|
54
|
+
* returned as it is.
|
|
55
|
+
*/
|
|
56
|
+
export declare function admitSeededPeopleSlide(before: ComposedPages, after: ComposedPages): ComposedPages;
|
|
57
|
+
/**
|
|
58
|
+
* Decide whether `apply-design <design>` left every page's JSON-LD unchanged
|
|
59
|
+
* (logo excluded), the seeded people slide's Persons admitted.
|
|
60
|
+
*/
|
|
50
61
|
export declare function assessJsonLdDrift(design: string, before: ComposedPages, after: ComposedPages): JsonLdDriftVerdict;
|
|
51
62
|
export interface MeasureApplyDesignDriftOptions {
|
|
52
63
|
/** create-ailk's built `dist/cli.js`. */
|
|
@@ -16,10 +16,17 @@
|
|
|
16
16
|
* touches the filesystem and processes only through the paths it is handed,
|
|
17
17
|
* so the driver and `__tests__/verify-scaffold-checks.test.ts` run the same
|
|
18
18
|
* procedure. Nothing here imports `@working-theory/theme`.
|
|
19
|
+
*
|
|
20
|
+
* One change is content, not drift (#7744 D8): a design that seeds an
|
|
21
|
+
* organization home's people slide adds one `Person` per seeded team member to
|
|
22
|
+
* the home's graph. `admitSeededPeopleSlide` puts the standard home back only
|
|
23
|
+
* when the seeded home is exactly that, so any other change still counts.
|
|
19
24
|
*/
|
|
20
25
|
import { execFileSync } from "node:child_process";
|
|
21
26
|
import { writeFileSync } from "node:fs";
|
|
22
27
|
import { join } from "node:path";
|
|
28
|
+
import { isDeepStrictEqual } from "node:util";
|
|
29
|
+
import { TEAM_SEED_MEMBERS } from "../team-profiles.js";
|
|
23
30
|
function describePair(row) {
|
|
24
31
|
const measured = row.status === "unmeasurable" || row.ratio === null
|
|
25
32
|
? "unmeasurable"
|
|
@@ -107,15 +114,44 @@ export function jsonLdDrift(before, after) {
|
|
|
107
114
|
const cmp = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
108
115
|
return nodes.sort((x, y) => cmp(x.page, y.page) || cmp(x.path, y.path));
|
|
109
116
|
}
|
|
117
|
+
const HOME_PAGE = "content/en/HomePage/home.mdx";
|
|
118
|
+
/**
|
|
119
|
+
* `after` with the standard home put back when the seeded home's JSON-LD is
|
|
120
|
+
* exactly the standard home's node followed by one `Person` per
|
|
121
|
+
* `TEAM_SEED_MEMBERS` member, in order (the people slide's `team` section,
|
|
122
|
+
* #7744 D8). Any other home, or a home the people slide did not change, is
|
|
123
|
+
* returned as it is.
|
|
124
|
+
*/
|
|
125
|
+
export function admitSeededPeopleSlide(before, after) {
|
|
126
|
+
const standard = before[HOME_PAGE];
|
|
127
|
+
const seeded = after[HOME_PAGE];
|
|
128
|
+
if (standard === undefined || seeded === undefined || !seeded.sectionsValid)
|
|
129
|
+
return after;
|
|
130
|
+
const graph = JSON.parse(seeded.jsonld)["@graph"];
|
|
131
|
+
if (!Array.isArray(graph))
|
|
132
|
+
return after;
|
|
133
|
+
const [page, ...people] = graph;
|
|
134
|
+
const expected = TEAM_SEED_MEMBERS.map((m) => ({
|
|
135
|
+
"@context": "https://schema.org",
|
|
136
|
+
"@type": "Person",
|
|
137
|
+
name: m.name,
|
|
138
|
+
jobTitle: m.role,
|
|
139
|
+
}));
|
|
140
|
+
const isPeopleSlide = isDeepStrictEqual(page, JSON.parse(standard.jsonld)) && isDeepStrictEqual(people, expected);
|
|
141
|
+
return isPeopleSlide ? { ...after, [HOME_PAGE]: standard } : after;
|
|
142
|
+
}
|
|
110
143
|
/** How many changed nodes a drift reason lists before it stops. */
|
|
111
144
|
const REASON_NODE_LIMIT = 10;
|
|
112
|
-
/**
|
|
145
|
+
/**
|
|
146
|
+
* Decide whether `apply-design <design>` left every page's JSON-LD unchanged
|
|
147
|
+
* (logo excluded), the seeded people slide's Persons admitted.
|
|
148
|
+
*/
|
|
113
149
|
export function assessJsonLdDrift(design, before, after) {
|
|
114
150
|
const pages = Object.keys(before).length;
|
|
115
151
|
if (pages === 0) {
|
|
116
152
|
return { ok: false, pages, changed: [], reason: `design ${design}: no page was composed` };
|
|
117
153
|
}
|
|
118
|
-
const changed = jsonLdDrift(before, after);
|
|
154
|
+
const changed = jsonLdDrift(before, admitSeededPeopleSlide(before, after));
|
|
119
155
|
if (changed.length === 0)
|
|
120
156
|
return { ok: true, pages };
|
|
121
157
|
const listed = changed
|
|
@@ -139,6 +139,15 @@ export declare function deriveLevel(options: GateOptions): Promise<{
|
|
|
139
139
|
level: Level;
|
|
140
140
|
bundles: Map<ModuleName, Uint8Array>;
|
|
141
141
|
}>;
|
|
142
|
+
/**
|
|
143
|
+
* The level a `--modules`-keep run's chosen non-Free design is gated on
|
|
144
|
+
* (#7010). `resolveModuleSet`'s keep branch composes no level (NFR-2), so
|
|
145
|
+
* `scaffold()` derives one here through `deriveLevel`, as the addition branch
|
|
146
|
+
* does. A key the registry refuses for a Pro module — lapsed, revoked, or one
|
|
147
|
+
* that does not cover Pro — reads as `"free"`, so `assertDesignAllowed`
|
|
148
|
+
* refuses with its own message (fail closed); any other failure propagates.
|
|
149
|
+
*/
|
|
150
|
+
export declare function deriveDesignLevel(options: GateOptions): Promise<Level>;
|
|
142
151
|
/**
|
|
143
152
|
* The one entry point that turns `--modules` + the key into the effective
|
|
144
153
|
* module set and its resolved bundles (D5) — `programmatic.ts`'s `scaffold()`
|
|
@@ -294,6 +294,24 @@ export async function deriveLevel(options) {
|
|
|
294
294
|
throw err;
|
|
295
295
|
}
|
|
296
296
|
}
|
|
297
|
+
/**
|
|
298
|
+
* The level a `--modules`-keep run's chosen non-Free design is gated on
|
|
299
|
+
* (#7010). `resolveModuleSet`'s keep branch composes no level (NFR-2), so
|
|
300
|
+
* `scaffold()` derives one here through `deriveLevel`, as the addition branch
|
|
301
|
+
* does. A key the registry refuses for a Pro module — lapsed, revoked, or one
|
|
302
|
+
* that does not cover Pro — reads as `"free"`, so `assertDesignAllowed`
|
|
303
|
+
* refuses with its own message (fail closed); any other failure propagates.
|
|
304
|
+
*/
|
|
305
|
+
export async function deriveDesignLevel(options) {
|
|
306
|
+
try {
|
|
307
|
+
return (await deriveLevel(options)).level;
|
|
308
|
+
}
|
|
309
|
+
catch (err) {
|
|
310
|
+
if (err instanceof LicenseRequiredError)
|
|
311
|
+
return "free";
|
|
312
|
+
throw err;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
297
315
|
/**
|
|
298
316
|
* The one entry point that turns `--modules` + the key into the effective
|
|
299
317
|
* module set and its resolved bundles (D5) — `programmatic.ts`'s `scaffold()`
|
package/dist/programmatic.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* import { scaffoldSync } from './programmatic.js';
|
|
16
16
|
* const result = scaffoldSync({ targetDir: '/tmp/my-site', projectName: 'my-site' });
|
|
17
17
|
*/
|
|
18
|
-
import { existsSync, readdirSync, readFileSync, rmdirSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
18
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmdirSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
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";
|
|
@@ -24,21 +24,23 @@ import { renderClaudeMd, syncClaudeMd, } from "./claude-md.js";
|
|
|
24
24
|
import { CATALOG_TARGET_PATH, writeComponentCatalog, } from "./component-catalog.js";
|
|
25
25
|
import { buildScaffoldMarkers, copyTemplate } from "./copy.js";
|
|
26
26
|
import { deriveArchitecture } from "./derive-architecture.js";
|
|
27
|
-
import { compileDesignTheme } from "./design-compile.js";
|
|
27
|
+
import { compileDesignFonts, compileDesignTheme } from "./design-compile.js";
|
|
28
|
+
import { HOME_PAGE_PATH, designHomeSeeds, planDesignHome, shapeHomePath, shippedHomeSeeds, } from "./design-home.js";
|
|
28
29
|
import { applyDesignPresets, readSectionLookCatalog, shippedPageTypes, validateDesignPresets, } from "./design-presets.js";
|
|
29
|
-
import {
|
|
30
|
+
import { resolveDesign, spliceThemeBlock, } from "./design-scaffold.js";
|
|
30
31
|
import { writeAuthSecretFile } from "./lib/auth-secret-file.js";
|
|
31
32
|
import { extractModuleBundle, installBundleTree, } from "./lib/extract-module-bundle.js";
|
|
32
33
|
import { fetchTemplate } from "./lib/fetch-template.js";
|
|
33
34
|
import { assertLicenseKeyShape, writeLicenseKeyFiles, } from "./lib/license-key-files.js";
|
|
34
|
-
import { assertDesignAllowed, assertReleased, deriveLevel, resolveModuleSet, resolvePaidModules, } from "./lib/module-license-gate.js";
|
|
35
|
+
import { assertDesignAllowed, assertReleased, deriveDesignLevel, deriveLevel, resolveModuleSet, resolvePaidModules, } from "./lib/module-license-gate.js";
|
|
35
36
|
import { classifyModules } from "./lib/module-tier.js";
|
|
36
37
|
import { normalizeModules } from "./module-architecture.js";
|
|
37
38
|
import { writePaidSections } from "./paid-sections.js";
|
|
38
39
|
import { RECOGNIZED_SHAPES, RESERVED_SHAPES, } from "./parse-args.js";
|
|
39
40
|
import { SITE_CHROME_PATH, renderSiteChrome, resolveSiteChrome, } from "./site-chrome.js";
|
|
41
|
+
import { SITE_FONTS_PATH, renderSiteFonts } from "./site-fonts.js";
|
|
40
42
|
import { planSiteManifest } from "./site-manifest.js";
|
|
41
|
-
import { SITE_ROSTER_PATH, planSiteManifestDesign, readSiteRoster, withMeetPlaceholderSeeds, } from "./site-roster.js";
|
|
43
|
+
import { SITE_ROSTER_PATH, planHomeRecord, planSiteManifestDesign, readSiteRoster, withMeetPlaceholderSeeds, } from "./site-roster.js";
|
|
42
44
|
import { GATEABLE_SURFACES, MODULE_SURFACES, NAV_CONFIG_PATH, excludedRoutePaths, renderNavConfig, resolveSurfaces, } from "./surfaces.js";
|
|
43
45
|
import { syncRoutes } from "./sync-routes.js";
|
|
44
46
|
import { BASE_LAYER_DIR, SHAPE_STARTERS, TEMPLATE_MANIFESTS, TemplateError, mergeRoster, resolveTemplate, resolveTemplateLineage, rosterDeclares, templateDelivery, templateTreeDir, } from "./template-manifest.js";
|
|
@@ -267,14 +269,22 @@ export async function scaffold(opts) {
|
|
|
267
269
|
// (D2): a non-look field or unknown variant throws `DesignPackageError`
|
|
268
270
|
// naming page type, section type and field, and the target is never
|
|
269
271
|
// created. A design with no `sections` block executes no new read (D8).
|
|
272
|
+
let lookCatalog;
|
|
273
|
+
const catalog = () => (lookCatalog ??= readSectionLookCatalog(join(PKG_ROOT, "component-catalog.json")));
|
|
270
274
|
if (resolvedDesign !== undefined &&
|
|
271
275
|
Object.keys(resolvedDesign.pkg.sections).length > 0) {
|
|
272
276
|
validateDesignPresets(resolvedDesign.pkg.sections, {
|
|
273
|
-
catalog:
|
|
277
|
+
catalog: catalog(),
|
|
274
278
|
pageTypes: shippedPageTypes(SITE_TEMPLATES_DIR),
|
|
275
279
|
file: join(resolvedDesign.dir, "DESIGN.md"),
|
|
276
280
|
});
|
|
277
281
|
}
|
|
282
|
+
// #7743 D5 — the design's home seeds are read and validated beside its
|
|
283
|
+
// presets: a malformed seed throws `DesignPackageError`, naming the file,
|
|
284
|
+
// before the target is created.
|
|
285
|
+
const designSeeds = resolvedDesign !== undefined
|
|
286
|
+
? designHomeSeeds(resolvedDesign, { designsDir: DESIGNS_DIR, catalog })
|
|
287
|
+
: [];
|
|
278
288
|
// #6813 D7 — the resolved design only when the CALLER chose one (`opts.design`);
|
|
279
289
|
// a template's own declared design is never gated (#6778 option (c)).
|
|
280
290
|
const chosenDesign = opts.design !== undefined ? resolvedDesign : undefined;
|
|
@@ -301,7 +311,7 @@ export async function scaffold(opts) {
|
|
|
301
311
|
// The run's derived level (#6759), for `assertDesignAllowed` below.
|
|
302
312
|
// Undefined on the alias branch and on a `--modules`-keep run (D2/D4 in
|
|
303
313
|
// module-license-gate.ts — the level plays no part in composing either),
|
|
304
|
-
// so a non-`standard` design on either path derives it separately
|
|
314
|
+
// so a non-`standard` design on either path derives it separately.
|
|
305
315
|
let level;
|
|
306
316
|
if (opts.template !== undefined) {
|
|
307
317
|
// Alias run — untouched (NFR-4): the union line and its
|
|
@@ -350,6 +360,18 @@ export async function scaffold(opts) {
|
|
|
350
360
|
});
|
|
351
361
|
bundles = chosen.bundles;
|
|
352
362
|
level = chosen.level;
|
|
363
|
+
// #7010 — a `--modules`-keep run composes no level (NFR-2), so a chosen
|
|
364
|
+
// non-Free design derives one from the key here, as the addition branch
|
|
365
|
+
// does; a plain keep run makes no extra network call.
|
|
366
|
+
if (level === undefined && chosenDesign !== undefined && !chosenDesign.free) {
|
|
367
|
+
level = await deriveDesignLevel({
|
|
368
|
+
licenseKey,
|
|
369
|
+
...(opts.moduleRegistryUrl !== undefined
|
|
370
|
+
? { registryUrl: opts.moduleRegistryUrl }
|
|
371
|
+
: {}),
|
|
372
|
+
...(opts.fetchImpl !== undefined ? { fetchImpl: opts.fetchImpl } : {}),
|
|
373
|
+
});
|
|
374
|
+
}
|
|
353
375
|
modules =
|
|
354
376
|
template !== undefined
|
|
355
377
|
? normalizeModules([...template.requires, ...chosen.modules])
|
|
@@ -378,9 +400,9 @@ export async function scaffold(opts) {
|
|
|
378
400
|
}
|
|
379
401
|
// #6760 D4 — after the level is resolved, before any fetch or write below.
|
|
380
402
|
// Only the caller's chosen design is gated (#6778 option (c)); a template's
|
|
381
|
-
// own declared design is not. `level ?? "free"` fails closed: a
|
|
382
|
-
// `--modules`-keep run
|
|
383
|
-
//
|
|
403
|
+
// own declared design is not. `level ?? "free"` fails closed: a level left
|
|
404
|
+
// underived (a plain alias or `--modules`-keep run with no chosen non-Free
|
|
405
|
+
// design) is never trusted. The
|
|
384
406
|
// refusal names `chosen.display` — the name, or the absolute directory
|
|
385
407
|
// (#6813 D7) — and `chosen.free` decides Free-ness by identity (D2), never
|
|
386
408
|
// by `FREE_DESIGNS.includes(name)` alone (a path is never in that list).
|
|
@@ -568,22 +590,6 @@ export async function scaffold(opts) {
|
|
|
568
590
|
: [];
|
|
569
591
|
writeFileSync(join(result.targetDir, NAV_CONFIG_PATH), renderNavConfig(result.surfaces, { serviceSlugs }));
|
|
570
592
|
pruneUnrosteredSeeds(result.targetDir, mergeRoster(template, template.requires), result.surfaces, ownership);
|
|
571
|
-
// spec 6892 D4 — the resolved design's own sample (the minimalism design's
|
|
572
|
-
// nine-slide deck home + its SVGs; no template declares `minimalism`
|
|
573
|
-
// since #6976, so none installs it today), installed AFTER the template
|
|
574
|
-
// layer so it wins over the shape's `HomePage/home.mdx`, and ONLY when
|
|
575
|
-
// the resolved design is the template's OWN declared design: a caller
|
|
576
|
-
// combination like `--shape organization-product --design minimalism`
|
|
577
|
-
// installs no sample (its roster carries neither the deck's link targets
|
|
578
|
-
// nor, in its generated record, the deck's sections).
|
|
579
|
-
if (resolvedDesign !== undefined &&
|
|
580
|
-
resolvedDesign.recorded === template.design) {
|
|
581
|
-
installDesignSample({
|
|
582
|
-
designDir: resolvedDesign.dir,
|
|
583
|
-
targetDir: result.targetDir,
|
|
584
|
-
ownership,
|
|
585
|
-
});
|
|
586
|
-
}
|
|
587
593
|
}
|
|
588
594
|
// #6813 D5/D8 steps 9-10 — whenever a design is resolved (the caller's,
|
|
589
595
|
// else the template's): the design's DESIGN.md (#6939); the compiled theme,
|
|
@@ -595,6 +601,15 @@ export async function scaffold(opts) {
|
|
|
595
601
|
// #6939 — the scaffold's DESIGN.md is the design it wears, not the one
|
|
596
602
|
// its template layer shipped; the site's own header lines are kept.
|
|
597
603
|
writeFileSync(join(result.targetDir, "DESIGN.md"), planDesignMd(resolvedDesign.dir, result.targetDir));
|
|
604
|
+
// #7720 D4.1 — the faces the design sources, beside site-chrome.ts. A
|
|
605
|
+
// design that sources none renders the overlay's committed default, so
|
|
606
|
+
// the file is left as the template tree wrote it.
|
|
607
|
+
const siteFontsPath = join(result.targetDir, SITE_FONTS_PATH);
|
|
608
|
+
const siteFonts = renderSiteFonts(compileDesignFonts(resolvedDesign.pkg));
|
|
609
|
+
if (!existsSync(siteFontsPath) || readFileSync(siteFontsPath, "utf8") !== siteFonts) {
|
|
610
|
+
mkdirSync(dirname(siteFontsPath), { recursive: true });
|
|
611
|
+
writeFileSync(siteFontsPath, siteFonts);
|
|
612
|
+
}
|
|
598
613
|
const compiledTheme = compileDesignTheme(resolvedDesign.pkg);
|
|
599
614
|
const siteMdxPath = join(result.targetDir, "content", "_site.mdx");
|
|
600
615
|
if (existsSync(siteMdxPath)) {
|
|
@@ -613,6 +628,27 @@ export async function scaffold(opts) {
|
|
|
613
628
|
writeFileSync(rosterPath, nextManifest);
|
|
614
629
|
}
|
|
615
630
|
}
|
|
631
|
+
// #7743 D5 — the design's home arc, seeded over a placeholder home once
|
|
632
|
+
// every layer has landed, before the presets dress it. A home that is not
|
|
633
|
+
// the shape's placeholder is never written; `--design standard` (no
|
|
634
|
+
// seeds) leaves the shape's home byte-identical. The one
|
|
635
|
+
// `planDesignHome` call site in scaffold().
|
|
636
|
+
const homePath = join(result.targetDir, ...HOME_PAGE_PATH.split("/"));
|
|
637
|
+
let homeSeeded = false;
|
|
638
|
+
if (template !== undefined) {
|
|
639
|
+
const homePlan = planDesignHome({
|
|
640
|
+
current: existsSync(homePath) ? readFileSync(homePath, "utf8") : undefined,
|
|
641
|
+
shape: template.shape,
|
|
642
|
+
target: designSeeds.find((seed) => seed.shape === template.shape),
|
|
643
|
+
shapeHome: readFileSync(shapeHomePath(template.shape, { siteTemplatesDir: SITE_TEMPLATES_DIR, templatesDir: TEMPLATES_DIR }), "utf8"),
|
|
644
|
+
shippedSeeds: shippedHomeSeeds({ designsDir: DESIGNS_DIR, catalog }),
|
|
645
|
+
catalog: catalog(),
|
|
646
|
+
});
|
|
647
|
+
if (homePlan.outcome === "seeded" && homePlan.text !== undefined) {
|
|
648
|
+
writeFileSync(homePath, homePlan.text);
|
|
649
|
+
homeSeeded = true;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
616
652
|
// #6816 D6 step 10b — the design's section presets, written onto the
|
|
617
653
|
// pages of the page types the roster of record places, once every layer
|
|
618
654
|
// has landed. The one `applyDesignPresets` call site. A scaffold with no
|
|
@@ -637,6 +673,14 @@ export async function scaffold(opts) {
|
|
|
637
673
|
writeFileSync(page.path, page.text);
|
|
638
674
|
}
|
|
639
675
|
}
|
|
676
|
+
// #7743 D6 — the manifest's `/` row records the seeded home's sections,
|
|
677
|
+
// as the presets left them.
|
|
678
|
+
const rosterPath = join(result.targetDir, SITE_ROSTER_PATH);
|
|
679
|
+
if (homeSeeded && existsSync(rosterPath)) {
|
|
680
|
+
const nextManifest = planHomeRecord(readFileSync(rosterPath, "utf8"), readFileSync(homePath, "utf8"), catalog());
|
|
681
|
+
if (nextManifest !== undefined)
|
|
682
|
+
writeFileSync(rosterPath, nextManifest);
|
|
683
|
+
}
|
|
640
684
|
}
|
|
641
685
|
// #6944 — the manifest records what this scaffold IS, once every layer has
|
|
642
686
|
// landed: the effective module set, a row for every route on disk, and the
|