create-ailk 0.31.0 → 0.32.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 +41 -5
- package/dist/body-heading.d.ts +13 -0
- package/dist/body-heading.js +19 -5
- package/dist/cutover.d.ts +6 -1
- package/dist/cutover.js +23 -6
- package/dist/language-import.d.ts +16 -3
- package/dist/language-import.js +27 -11
- package/dist/lib/import-mdx.js +57 -24
- package/dist/lib/render-comparison.d.ts +51 -0
- package/dist/lib/render-comparison.js +205 -6
- package/dist/mdx-braces.d.ts +73 -0
- package/dist/mdx-braces.js +285 -0
- package/dist/migrate-content.d.ts +1 -1
- package/dist/migrate-content.js +4 -2
- package/dist/programmatic.js +2 -8
- package/dist/site-manifest.js +6 -38
- package/dist/site-roster.d.ts +46 -3
- package/dist/site-roster.js +124 -4
- package/dist/surfaces.d.ts +8 -0
- package/dist/surfaces.js +16 -0
- package/dist/update.d.ts +21 -1
- package/dist/update.js +142 -11
- package/package.json +3 -3
- package/templates/.github/workflows/ci.yml +2 -0
- package/templates/apps/mcp/.env.example +7 -1
- package/templates/apps/web/__tests__/setup.ts +9 -0
- package/templates/apps/web/app/[locale]/[slug]/[detail]/__tests__/page.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/about/__tests__/about-routes.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/pricing/[slug]/__tests__/page.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/pricing/__tests__/offerings.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/pricing/__tests__/page.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/pricing/__tests__/unconnected.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/team/__tests__/team-routes.test.tsx +3 -0
- package/templates/apps/web/lib/__tests__/page-chrome.test.ts +3 -0
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +134 -10
- package/templates/apps/web/src/lib/routes.ts +5 -3
- package/templates/package.json +2 -1
- package/templates/tools/body-heading.ts +440 -0
- package/templates/tools/check-mdx-body.ts +123 -0
- package/templates/tools/mdx-braces.ts +303 -0
- package/versions.json +47 -47
|
@@ -45,6 +45,9 @@ import { createSitemapHandler } from "../../../sitemap.xml/route";
|
|
|
45
45
|
import { createLlmsHandler } from "../../../llms.txt/route";
|
|
46
46
|
import type { ContentAdapter } from "@working-theory/content-adapters";
|
|
47
47
|
|
|
48
|
+
// #10704 — a fixture suite: its content is shaped for the mdx source, whatever the site serves from.
|
|
49
|
+
process.env.CONTENT_ADAPTER = "mdx";
|
|
50
|
+
|
|
48
51
|
type JsonLd = Record<string, unknown>;
|
|
49
52
|
|
|
50
53
|
function jsonLdScripts(container: HTMLElement): JsonLd[] {
|
|
@@ -58,6 +58,9 @@ import CareersPage from "../page";
|
|
|
58
58
|
import CareersSlugPage from "../[slug]/page";
|
|
59
59
|
import * as pageRenderer from "@working-theory/page-renderer";
|
|
60
60
|
|
|
61
|
+
// #10704 — a fixture suite: its content is shaped for the mdx source, whatever the site serves from.
|
|
62
|
+
process.env.CONTENT_ADAPTER = "mdx";
|
|
63
|
+
|
|
61
64
|
// ── Shared stubs ───────────────────────────────────────────────────────────────
|
|
62
65
|
const getMdxSourceEmpty = jest.fn().mockResolvedValue(null);
|
|
63
66
|
|
|
@@ -32,6 +32,9 @@ jest.mock("next/navigation", () => ({
|
|
|
32
32
|
|
|
33
33
|
import OfferingPricingPage, { dynamic, generateMetadata } from "../page";
|
|
34
34
|
|
|
35
|
+
// #10704 — a fixture suite: its content is shaped for the mdx source, whatever the site serves from.
|
|
36
|
+
process.env.CONTENT_ADAPTER = "mdx";
|
|
37
|
+
|
|
35
38
|
const SLUG = "website-rebuild";
|
|
36
39
|
const SITE_EMAIL = "hello@example.com";
|
|
37
40
|
|
|
@@ -27,6 +27,9 @@ import {
|
|
|
27
27
|
|
|
28
28
|
import PricingPage, { generateMetadata } from "../page";
|
|
29
29
|
|
|
30
|
+
// #10704 — a fixture suite: its content is shaped for the mdx source, whatever the site serves from.
|
|
31
|
+
process.env.CONTENT_ADAPTER = "mdx";
|
|
32
|
+
|
|
30
33
|
const SITE_EMAIL = "hello@example.com";
|
|
31
34
|
const HOSTILE = "</script><script>alert(1)</script>";
|
|
32
35
|
|
|
@@ -15,6 +15,9 @@ import { act, render, screen, within } from "@testing-library/react";
|
|
|
15
15
|
|
|
16
16
|
import PricingPage from "../page";
|
|
17
17
|
|
|
18
|
+
// #10704 — a fixture suite: its content is shaped for the mdx source, whatever the site serves from.
|
|
19
|
+
process.env.CONTENT_ADAPTER = "mdx";
|
|
20
|
+
|
|
18
21
|
const PLANS = [
|
|
19
22
|
{
|
|
20
23
|
key: "basic",
|
|
@@ -27,6 +27,9 @@ import { TOPOLOGY } from "../../../../lib/topology.generated";
|
|
|
27
27
|
import * as pricingPage from "../page";
|
|
28
28
|
import PricingPage, { generateMetadata } from "../page";
|
|
29
29
|
|
|
30
|
+
// #10704 — a fixture suite: its content is shaped for the mdx source, whatever the site serves from.
|
|
31
|
+
process.env.CONTENT_ADAPTER = "mdx";
|
|
32
|
+
|
|
30
33
|
const PLANS = [
|
|
31
34
|
{
|
|
32
35
|
key: "basic",
|
|
@@ -51,6 +51,9 @@ import { notFound } from "next/navigation";
|
|
|
51
51
|
import ProjectsPage from "../page";
|
|
52
52
|
import ProjectSlugPage from "../[slug]/page";
|
|
53
53
|
|
|
54
|
+
// #10704 — a fixture suite: its content is shaped for the mdx source, whatever the site serves from.
|
|
55
|
+
process.env.CONTENT_ADAPTER = "mdx";
|
|
56
|
+
|
|
54
57
|
const mockNotFound = notFound as jest.MockedFunction<typeof notFound>;
|
|
55
58
|
|
|
56
59
|
// ── Shared stub helpers ────────────────────────────────────────────────────────
|
|
@@ -31,6 +31,9 @@ import ServicesPage from "../page";
|
|
|
31
31
|
import ServiceSlugPage from "../[slug]/page";
|
|
32
32
|
import * as pageRenderer from "@working-theory/page-renderer";
|
|
33
33
|
|
|
34
|
+
// #10704 — a fixture suite: its content is shaped for the mdx source, whatever the site serves from.
|
|
35
|
+
process.env.CONTENT_ADAPTER = "mdx";
|
|
36
|
+
|
|
34
37
|
const mockNotFound = notFound as jest.MockedFunction<typeof notFound>;
|
|
35
38
|
|
|
36
39
|
// ── Shared stub helpers ────────────────────────────────────────────────────────
|
|
@@ -31,6 +31,9 @@ import TeamPage from "../page";
|
|
|
31
31
|
import TeamMemberPage from "../[slug]/page";
|
|
32
32
|
import * as pageRenderer from "@working-theory/page-renderer";
|
|
33
33
|
|
|
34
|
+
// #10704 — a fixture suite: its content is shaped for the mdx source, whatever the site serves from.
|
|
35
|
+
process.env.CONTENT_ADAPTER = "mdx";
|
|
36
|
+
|
|
34
37
|
type JsonLd = Record<string, unknown>;
|
|
35
38
|
|
|
36
39
|
function jsonLdScripts(container: HTMLElement): JsonLd[] {
|
|
@@ -29,6 +29,9 @@ import {
|
|
|
29
29
|
} from "../page-chrome";
|
|
30
30
|
import type { SiteChrome } from "../site-chrome";
|
|
31
31
|
|
|
32
|
+
// #10704 — a fixture suite: its content is shaped for the mdx source, whatever the site serves from.
|
|
33
|
+
process.env.CONTENT_ADAPTER = "mdx";
|
|
34
|
+
|
|
32
35
|
// ── AC-2 — the route families ────────────────────────────────────────────
|
|
33
36
|
|
|
34
37
|
function camel(prefix: string): string {
|
|
@@ -29,6 +29,16 @@
|
|
|
29
29
|
* `tools/content-compile`, which the scaffold does not ship, and the adapter
|
|
30
30
|
* reads the same files with the same frontmatter schema the manifest is
|
|
31
31
|
* compiled from. See docs/internal/decisions/scaffold-structured-data-check.md.
|
|
32
|
+
*
|
|
33
|
+
* The pages come from the site's active content source (#10704):
|
|
34
|
+
* `resolvedAdapterKind()`, the resolver every page reads through. Under `mdx`
|
|
35
|
+
* (the default) they are the files under `content/`. Under `database` — a
|
|
36
|
+
* spoke that serves every page from its hub and may hold no page file — they
|
|
37
|
+
* are the hub's published pages as the build's last-good snapshot holds them
|
|
38
|
+
* (`.ailk/content-snapshot.json`, written by `pnpm --filter web build` under
|
|
39
|
+
* `CONTENT_ADAPTER=database`), read through the site's own content source
|
|
40
|
+
* with the API origin pinned to a refused loopback, so a test run reads no
|
|
41
|
+
* hub and needs no key.
|
|
32
42
|
*/
|
|
33
43
|
import {
|
|
34
44
|
copyFileSync,
|
|
@@ -51,8 +61,11 @@ import type {
|
|
|
51
61
|
import {
|
|
52
62
|
SCHEMA_ORG_TYPES,
|
|
53
63
|
createMdxAdapter,
|
|
64
|
+
resolvedAdapterKind,
|
|
54
65
|
} from "@working-theory/content-adapters";
|
|
55
66
|
import {
|
|
67
|
+
getContentAdapter,
|
|
68
|
+
getMdxSource,
|
|
56
69
|
getPageSections,
|
|
57
70
|
pageToJsonLd,
|
|
58
71
|
siteOrigin,
|
|
@@ -158,6 +171,94 @@ const adapter = createMdxAdapter({
|
|
|
158
171
|
defaultLocale: DEFAULT_LOCALE,
|
|
159
172
|
});
|
|
160
173
|
|
|
174
|
+
// ── #10704 D2 — a site that serves its pages from its hub ──────────────────
|
|
175
|
+
|
|
176
|
+
const SOURCE = resolvedAdapterKind();
|
|
177
|
+
// page-renderer's `CONTENT_SNAPSHOT_PATH`, which this jest config maps no
|
|
178
|
+
// subpath to read; create-ailk's structured-data-check suite pins the two
|
|
179
|
+
// equal. Relative to cwd, as the content source reads it.
|
|
180
|
+
const SNAPSHOT_REL = ".ailk/content-snapshot.json";
|
|
181
|
+
|
|
182
|
+
// No read may reach a hub: every page and list read fails as unavailable and
|
|
183
|
+
// the content source answers from the snapshot, the path a deployed spoke
|
|
184
|
+
// takes while its hub is down.
|
|
185
|
+
if (SOURCE === "database") process.env.AILK_API_URL = "http://127.0.0.1:9";
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Every page the snapshot holds, keyed by its cache tag
|
|
189
|
+
* (`content:<workspace>:<type>:<slug>:<locale>`, `cache-tags.ts`), or why it
|
|
190
|
+
* holds none. Read only under `database`.
|
|
191
|
+
*/
|
|
192
|
+
function readHubPages(): {
|
|
193
|
+
pages: Array<{ locale: string; type: string; slug: string }>;
|
|
194
|
+
problem: string | null;
|
|
195
|
+
} {
|
|
196
|
+
const path = join(process.cwd(), SNAPSHOT_REL);
|
|
197
|
+
const build = "run `pnpm --filter web build` under CONTENT_ADAPTER=database to write it";
|
|
198
|
+
if (!existsSync(path)) {
|
|
199
|
+
return { pages: [], problem: `${path} does not exist — ${build}` };
|
|
200
|
+
}
|
|
201
|
+
let parsed: { version?: unknown; pages?: unknown; lists?: unknown } | null;
|
|
202
|
+
try {
|
|
203
|
+
parsed = JSON.parse(readFileSync(path, "utf8")) as typeof parsed;
|
|
204
|
+
} catch {
|
|
205
|
+
parsed = null;
|
|
206
|
+
}
|
|
207
|
+
if (
|
|
208
|
+
parsed === null ||
|
|
209
|
+
typeof parsed !== "object" ||
|
|
210
|
+
parsed.version !== 1 ||
|
|
211
|
+
typeof parsed.pages !== "object" ||
|
|
212
|
+
parsed.pages === null ||
|
|
213
|
+
typeof parsed.lists !== "object" ||
|
|
214
|
+
parsed.lists === null
|
|
215
|
+
) {
|
|
216
|
+
return { pages: [], problem: `${path} is not a version-1 content snapshot — ${build}` };
|
|
217
|
+
}
|
|
218
|
+
const pages = Object.keys(parsed.pages).map((tag) => {
|
|
219
|
+
const parts = tag.split(":");
|
|
220
|
+
return {
|
|
221
|
+
type: parts[2] ?? "",
|
|
222
|
+
slug: parts.slice(3, -1).join(":"),
|
|
223
|
+
locale: parts[parts.length - 1] ?? "",
|
|
224
|
+
};
|
|
225
|
+
});
|
|
226
|
+
if (pages.length === 0) {
|
|
227
|
+
return { pages, problem: `${path} holds no page — ${build}` };
|
|
228
|
+
}
|
|
229
|
+
return { pages, problem: null };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const HUB = SOURCE === "database" ? readHubPages() : { pages: [], problem: null };
|
|
233
|
+
|
|
234
|
+
/** The snapshot's (locale, type) pairs, in the shape the content walk takes. */
|
|
235
|
+
function hubTypeDirs(): Array<{ locale: string; typeDir: string }> {
|
|
236
|
+
const seen = new Set<string>();
|
|
237
|
+
const out: Array<{ locale: string; typeDir: string }> = [];
|
|
238
|
+
for (const { locale, type } of HUB.pages) {
|
|
239
|
+
if (seen.has(`${locale}/${type}`)) continue;
|
|
240
|
+
seen.add(`${locale}/${type}`);
|
|
241
|
+
out.push({ locale, typeDir: type });
|
|
242
|
+
}
|
|
243
|
+
return out.sort((a, b) =>
|
|
244
|
+
`${a.locale}/${a.typeDir}`.localeCompare(`${b.locale}/${b.typeDir}`),
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** A type's pages in a locale, from the active source. */
|
|
249
|
+
function listDetailFor(type: PageType, locale: string): Promise<ContentPage[]> {
|
|
250
|
+
return SOURCE === "database"
|
|
251
|
+
? getContentAdapter().listDetail(type, locale)
|
|
252
|
+
: adapter.listDetail(type, locale);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** One page's source, from the active source (`ContentAdapter` has no `getPageSource`). */
|
|
256
|
+
function pageSourceFor(type: PageType, slug: string, locale: string) {
|
|
257
|
+
return SOURCE === "database"
|
|
258
|
+
? getMdxSource(type, slug, locale)
|
|
259
|
+
: adapter.getPageSource(type, slug, locale);
|
|
260
|
+
}
|
|
261
|
+
|
|
161
262
|
// ── JSON-LD flatten + validate (the ten lines from tools/scrape-jsonld.ts) ──
|
|
162
263
|
|
|
163
264
|
function flattenBlocks(parsed: unknown): JsonLdObject[] {
|
|
@@ -244,9 +345,21 @@ function identityFailure(blocks: JsonLdObject[], label: string): string | null {
|
|
|
244
345
|
// ── 1. Content-driven: every content page builds valid JSON-LD ──────────────
|
|
245
346
|
|
|
246
347
|
describe("structured data — every content page builds valid JSON-LD", () => {
|
|
247
|
-
const typeDirs = contentTypeDirs();
|
|
348
|
+
const typeDirs = SOURCE === "database" ? hubTypeDirs() : contentTypeDirs();
|
|
349
|
+
let hubPagesChecked = 0;
|
|
350
|
+
|
|
351
|
+
afterAll(() => {
|
|
352
|
+
if (SOURCE === "database") {
|
|
353
|
+
console.info(`structured-data: ${hubPagesChecked} hub page(s) checked from ${SNAPSHOT_REL}`);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
248
356
|
|
|
249
357
|
it("finds content to check", () => {
|
|
358
|
+
if (SOURCE === "database") {
|
|
359
|
+
// A hub-served site with no snapshot has nothing to check: misconfigured.
|
|
360
|
+
expect(HUB.problem).toBeNull();
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
250
363
|
// A site with no content directory at all is misconfigured, not clean.
|
|
251
364
|
expect(existsSync(CONTENT_ROOT)).toBe(true);
|
|
252
365
|
expect(typeDirs.length).toBeGreaterThan(0);
|
|
@@ -274,14 +387,17 @@ describe("structured data — every content page builds valid JSON-LD", () => {
|
|
|
274
387
|
}
|
|
275
388
|
const type = typeDir as PageType;
|
|
276
389
|
|
|
277
|
-
const pages: ContentPage[] = await
|
|
278
|
-
const dirPath = join(CONTENT_ROOT, locale, typeDir);
|
|
279
|
-
const mdxCount = readdirSync(dirPath).filter(
|
|
280
|
-
(n) => n.endsWith(".mdx") && !n.startsWith("_"),
|
|
281
|
-
).length;
|
|
390
|
+
const pages: ContentPage[] = await listDetailFor(type, locale);
|
|
282
391
|
// The adapter drops a page whose frontmatter fails the schema; that
|
|
283
392
|
// page would render nothing, so count it here rather than lose it.
|
|
284
|
-
|
|
393
|
+
// Under `database` the count is the pages the hub published (#10704).
|
|
394
|
+
const expected =
|
|
395
|
+
SOURCE === "database"
|
|
396
|
+
? HUB.pages.filter((p) => p.locale === locale && p.type === type).length
|
|
397
|
+
: readdirSync(join(CONTENT_ROOT, locale, typeDir)).filter(
|
|
398
|
+
(n) => n.endsWith(".mdx") && !n.startsWith("_"),
|
|
399
|
+
).length;
|
|
400
|
+
expect(pages.length).toBe(expected);
|
|
285
401
|
|
|
286
402
|
const failures: string[] = [];
|
|
287
403
|
for (const page of pages) {
|
|
@@ -295,6 +411,7 @@ describe("structured data — every content page builds valid JSON-LD", () => {
|
|
|
295
411
|
if (failure !== null) failures.push(failure);
|
|
296
412
|
}
|
|
297
413
|
expect(failures).toEqual([]);
|
|
414
|
+
if (SOURCE === "database") hubPagesChecked += pages.length;
|
|
298
415
|
},
|
|
299
416
|
);
|
|
300
417
|
});
|
|
@@ -674,14 +791,16 @@ describe("structured data — every route emits a valid JSON-LD script", () => {
|
|
|
674
791
|
// #10043 D7 — a Service's category, for the nested route's params.
|
|
675
792
|
const parents = new Map<string, string>();
|
|
676
793
|
if (identity.slug !== null) {
|
|
677
|
-
const page = await
|
|
794
|
+
const page = await pageSourceFor(
|
|
678
795
|
type,
|
|
679
796
|
identity.slug,
|
|
680
797
|
DEFAULT_LOCALE,
|
|
681
798
|
);
|
|
682
799
|
if (page === null) {
|
|
683
800
|
throw new Error(
|
|
684
|
-
|
|
801
|
+
SOURCE === "database"
|
|
802
|
+
? `${label}: renders ${type}/${identity.slug}, but the hub serves no such page (${SNAPSHOT_REL}) — the route serves 404 in production.`
|
|
803
|
+
: `${label}: renders ${type}/${identity.slug}, but content/${DEFAULT_LOCALE}/${type}/${identity.slug}.mdx does not exist — the route serves 404 in production.`,
|
|
685
804
|
);
|
|
686
805
|
}
|
|
687
806
|
slugs = [identity.slug];
|
|
@@ -689,7 +808,7 @@ describe("structured data — every route emits a valid JSON-LD script", () => {
|
|
|
689
808
|
// The hub's own page shares this route's type but is served at the hub
|
|
690
809
|
// (#10044 D13), so it is never rendered here.
|
|
691
810
|
const hubSlug = hubOwnSlug(file, type);
|
|
692
|
-
const pages = (await
|
|
811
|
+
const pages = (await listDetailFor(type, DEFAULT_LOCALE)).filter(
|
|
693
812
|
(p: ContentPage) => p.slug !== hubSlug,
|
|
694
813
|
);
|
|
695
814
|
for (const p of pages) if (p.parent !== undefined) parents.set(p.slug, p.parent);
|
|
@@ -772,6 +891,8 @@ describe("structured data — a hub's own page is not rendered at its detail rou
|
|
|
772
891
|
async () => {
|
|
773
892
|
const root = mkdtempSync(join(tmpdir(), "ailk-10044-sd-"));
|
|
774
893
|
const prior = process.env.AILK_CONTENT_ROOT;
|
|
894
|
+
// #10704 D2 — the fixture is files, whatever source the site serves from.
|
|
895
|
+
const priorSource = process.env.CONTENT_ADAPTER;
|
|
775
896
|
try {
|
|
776
897
|
mkdirSync(join(root, DEFAULT_LOCALE, "Pricing"), { recursive: true });
|
|
777
898
|
copyFileSync(join(CONTENT_ROOT, "_site.mdx"), join(root, "_site.mdx"));
|
|
@@ -784,6 +905,7 @@ describe("structured data — a hub's own page is not rendered at its detail rou
|
|
|
784
905
|
'---\ntype: "Pricing"\nslug: "website-rebuild"\ntitle: "Website Rebuild"\ndescription: "What a rebuild costs."\nplans: ["ss-website-rebuild"]\n---\n',
|
|
785
906
|
);
|
|
786
907
|
process.env.AILK_CONTENT_ROOT = root;
|
|
908
|
+
process.env.CONTENT_ADAPTER = "mdx";
|
|
787
909
|
const fixtureAdapter = createMdxAdapter({
|
|
788
910
|
contentRoot: root,
|
|
789
911
|
defaultLocale: DEFAULT_LOCALE,
|
|
@@ -807,6 +929,8 @@ describe("structured data — a hub's own page is not rendered at its detail rou
|
|
|
807
929
|
} finally {
|
|
808
930
|
if (prior === undefined) delete process.env.AILK_CONTENT_ROOT;
|
|
809
931
|
else process.env.AILK_CONTENT_ROOT = prior;
|
|
932
|
+
if (priorSource === undefined) delete process.env.CONTENT_ADAPTER;
|
|
933
|
+
else process.env.CONTENT_ADAPTER = priorSource;
|
|
810
934
|
rmSync(root, { recursive: true, force: true });
|
|
811
935
|
}
|
|
812
936
|
},
|
|
@@ -39,9 +39,11 @@ export function resolveBaseUrl(): string {
|
|
|
39
39
|
* `packages/templates/src/__tests__/routes-map.test.ts`'s table test and
|
|
40
40
|
* `apps/web/__tests__/routes/routes-map-identity.test.ts`'s differential
|
|
41
41
|
* test over the live content tree). The signature is annotated explicitly so
|
|
42
|
-
* this stays the exact `(type: string, slug: string) => string` contract
|
|
43
|
-
*
|
|
44
|
-
*
|
|
42
|
+
* this stays the exact `(type: string, slug: string) => string` contract its
|
|
43
|
+
* callers depend on, even though `routeFor`'s own `slug` parameter is
|
|
44
|
+
* optional. `sitemap.xml`, `llms.txt` and, since #10775, the docs-blog
|
|
45
|
+
* module's `docs-blog/llms-route.ts` call `pagePathFor` instead; the export
|
|
46
|
+
* stays for a docs-blog site that installed the older copy of that route.
|
|
45
47
|
*/
|
|
46
48
|
export const slugToPath: (type: string, slug: string) => string = routeFor;
|
|
47
49
|
|
package/templates/package.json
CHANGED
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"architecture": "tsx tools/lint-architecture.ts",
|
|
22
22
|
"claude-md:check": "tsx tools/check-claude-md.ts",
|
|
23
23
|
"content-source:check": "tsx tools/check-content-source.ts",
|
|
24
|
-
"
|
|
24
|
+
"mdx-body:check": "tsx tools/check-mdx-body.ts",
|
|
25
|
+
"preflight": "pnpm architecture && pnpm claude-md:check && pnpm content-source:check && pnpm mdx-body:check && pnpm type-check && pnpm lint && pnpm build"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
27
28
|
"@arethetypeswrong/cli": "^0.18.5",
|