bsmnt 0.4.3 → 0.5.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/dist/application/add-integration/index.d.ts +24 -6
- package/dist/application/add-integration/index.d.ts.map +1 -1
- package/dist/application/add-integration/index.js +130 -82
- package/dist/application/add-integration/index.js.map +1 -1
- package/dist/application/create-project/map-selection-to-registry.js +1 -1
- package/dist/application/create-project/map-selection-to-registry.js.map +1 -1
- package/dist/application/shared/project-detection.d.ts +2 -2
- package/dist/application/shared/project-detection.d.ts.map +1 -1
- package/dist/application/shared/project-detection.js +2 -2
- package/dist/application/shared/project-detection.js.map +1 -1
- package/dist/core/create/internal-ids.d.ts +4 -1
- package/dist/core/create/internal-ids.d.ts.map +1 -1
- package/dist/core/create/internal-ids.js +6 -1
- package/dist/core/create/internal-ids.js.map +1 -1
- package/dist/domain/cms.d.ts +0 -7
- package/dist/domain/cms.d.ts.map +1 -1
- package/dist/domain/cms.js.map +1 -1
- package/dist/domain/forms.d.ts +17 -0
- package/dist/domain/forms.d.ts.map +1 -0
- package/dist/domain/forms.js +13 -0
- package/dist/domain/forms.js.map +1 -0
- package/dist/domain/integration.d.ts +30 -0
- package/dist/domain/integration.d.ts.map +1 -0
- package/dist/domain/integration.js +12 -0
- package/dist/domain/integration.js.map +1 -0
- package/dist/domain/skills.d.ts +32 -0
- package/dist/domain/skills.d.ts.map +1 -1
- package/dist/domain/skills.js +39 -0
- package/dist/domain/skills.js.map +1 -1
- package/dist/index.js +95 -37
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/agents/register-sanity-mcp.js +9 -6
- package/dist/infrastructure/agents/register-sanity-mcp.js.map +1 -1
- package/dist/infrastructure/create/apply-package-json.d.ts.map +1 -1
- package/dist/infrastructure/create/apply-package-json.js +19 -0
- package/dist/infrastructure/create/apply-package-json.js.map +1 -1
- package/dist/infrastructure/create/install-dependencies.d.ts.map +1 -1
- package/dist/infrastructure/create/install-dependencies.js +4 -0
- package/dist/infrastructure/create/install-dependencies.js.map +1 -1
- package/dist/infrastructure/create/merge-registry.d.ts.map +1 -1
- package/dist/infrastructure/create/merge-registry.js +8 -0
- package/dist/infrastructure/create/merge-registry.js.map +1 -1
- package/dist/infrastructure/create/setup-agent.d.ts.map +1 -1
- package/dist/infrastructure/create/setup-agent.js +29 -6
- package/dist/infrastructure/create/setup-agent.js.map +1 -1
- package/dist/infrastructure/create/setup-sanity.d.ts.map +1 -1
- package/dist/infrastructure/create/setup-sanity.js +3 -0
- package/dist/infrastructure/create/setup-sanity.js.map +1 -1
- package/dist/infrastructure/integrations/inject-integration.d.ts +3 -3
- package/dist/infrastructure/integrations/inject-integration.d.ts.map +1 -1
- package/dist/infrastructure/integrations/inject-integration.js +81 -37
- package/dist/infrastructure/integrations/inject-integration.js.map +1 -1
- package/dist/modules/features/cms/sanity/config.d.ts.map +1 -1
- package/dist/modules/features/cms/sanity/config.js +11 -5
- package/dist/modules/features/cms/sanity/config.js.map +1 -1
- package/dist/modules/features/cms/sanity/index.js +8 -2
- package/dist/modules/features/cms/sanity/index.js.map +1 -1
- package/dist/modules/features/cms/sanity/mergers/ci-workflow.d.ts +13 -0
- package/dist/modules/features/cms/sanity/mergers/ci-workflow.d.ts.map +1 -0
- package/dist/modules/features/cms/sanity/mergers/ci-workflow.js +38 -0
- package/dist/modules/features/cms/sanity/mergers/ci-workflow.js.map +1 -0
- package/dist/modules/features/cms/sanity/mergers/knip.d.ts +12 -0
- package/dist/modules/features/cms/sanity/mergers/knip.d.ts.map +1 -0
- package/dist/modules/features/cms/sanity/mergers/knip.js +38 -0
- package/dist/modules/features/cms/sanity/mergers/knip.js.map +1 -0
- package/dist/modules/features/cms/sanity/mergers/layout.d.ts.map +1 -1
- package/dist/modules/features/cms/sanity/mergers/layout.js +7 -3
- package/dist/modules/features/cms/sanity/mergers/layout.js.map +1 -1
- package/dist/modules/features/cms/sanity/mergers/sitemap.d.ts +3 -3
- package/dist/modules/features/cms/sanity/mergers/sitemap.d.ts.map +1 -1
- package/dist/modules/features/cms/sanity/mergers/sitemap.js +48 -62
- package/dist/modules/features/cms/sanity/mergers/sitemap.js.map +1 -1
- package/dist/modules/features/cms/sanity-pagebuilder/config.d.ts.map +1 -1
- package/dist/modules/features/cms/sanity-pagebuilder/config.js +8 -5
- package/dist/modules/features/cms/sanity-pagebuilder/config.js.map +1 -1
- package/dist/modules/features/cms/sanity-pagebuilder/index.js +2 -2
- package/dist/modules/features/cms/sanity-pagebuilder/index.js.map +1 -1
- package/dist/modules/features/forms/hubspot/config.d.ts +31 -0
- package/dist/modules/features/forms/hubspot/config.d.ts.map +1 -0
- package/dist/modules/features/forms/hubspot/config.js +25 -0
- package/dist/modules/features/forms/hubspot/config.js.map +1 -0
- package/dist/paths.d.ts +8 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +8 -0
- package/dist/paths.js.map +1 -1
- package/package.json +17 -9
- package/src/assets/check-node-version.mjs +22 -0
- package/src/modules/features/cms/sanity/files/app/blog/page.tsx +54 -0
- package/src/modules/features/cms/sanity/files/app/feed.xml/route.ts +36 -0
- package/src/modules/features/cms/sanity/files/lib/integrations/sanity/components/disable-draft-mode.tsx +3 -3
- package/src/modules/features/cms/sanity/files/lib/integrations/sanity/schemas/article.ts +7 -7
- package/src/modules/features/cms/sanity/files/lib/scripts/setup-ci-secrets.ts +117 -0
- package/src/modules/features/cms/sanity/files/lib/utils/rss.ts +96 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/.knip.json +8 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/app/api/[[...slug]]/route.ts +100 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/app/api/draft-mode/disable/route.ts +7 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/app/api/draft-mode/enable/route.ts +20 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/app/api/revalidate/route.ts +126 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/app/feed.xml/route.ts +40 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/app/sitemap.md/route.ts +124 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/app/studio/[[...tool]]/page.tsx +8 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/draft-mode-toggle.tsx +27 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/rich-text.tsx +87 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/visual-editing.tsx +28 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/components/ui/sanity-image/index.tsx +41 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/check-integration.ts +5 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/client.ts +27 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/disable-draft-mode.tsx +23 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/page-builder-input.tsx +39 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/page-category-input.tsx +50 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/slug-field.tsx +18 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/slug-input.tsx +14 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/confirm-publish-action.ts +40 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/env.ts +34 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/fetchers/layout.ts +48 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/fetchers/redirects.ts +17 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/icons.ts +62 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/live/index.tsx +88 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/markdown-proxy.config.ts +50 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-builder-config.ts +130 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-category.ts +28 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-tree-pane.tsx +420 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/pinned-pages.ts +54 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/presentation.ts +119 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/queries.ts +385 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/sanity.cli.ts +29 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/sanity.config.ts +113 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/index.ts +4 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/blog-content.ts +89 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/description.ts +29 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/hero.ts +28 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/singleton/content-collection.ts +45 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/content/author.ts +70 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/content/blog-category.ts +55 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/index.ts +96 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/company-data.ts +62 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/footer.ts +79 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/navbar.ts +75 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/redirect.ts +120 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/link.ts +125 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/logo-field.ts +9 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/metadata.ts +68 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/nav-objects.ts +192 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/page-builder.ts +39 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/page-folder.ts +124 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/page.ts +242 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/richText.ts +63 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/singletons.ts +39 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/structure.ts +330 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/utils/image.ts +8 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/utils/link.ts +140 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/utils/page-builder-markdown.ts +81 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/scripts/sanity-typegen.ts +45 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/check-redirect.ts +30 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/json-ld.tsx +212 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/metadata.ts +167 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/rss.ts +96 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/sitemap.ts +40 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/proxy.ts +81 -0
- package/src/templates/next-default/.github/workflows/ci.yml +62 -0
- package/src/templates/next-default/README.md +22 -0
- package/src/templates/next-default/knip.json +14 -0
- package/src/templates/next-default/lefthook.yml +5 -0
- package/src/templates/next-default/package.json +8 -4
- package/src/templates/next-experiments/.github/workflows/ci.yml +62 -0
- package/src/templates/next-experiments/README.md +22 -0
- package/src/templates/next-experiments/knip.json +14 -0
- package/src/templates/next-experiments/lefthook.yml +30 -0
- package/src/templates/next-experiments/package.json +10 -2
- package/src/templates/next-pagebuilder/.github/workflows/ci.yml +70 -0
- package/src/templates/next-pagebuilder/README.md +34 -0
- package/src/templates/next-pagebuilder/app/(content)/[[...slug]]/page.tsx +6 -1
- package/src/templates/next-pagebuilder/app/(content)/layout.tsx +3 -12
- package/src/templates/next-pagebuilder/components/layout/footer/index.tsx +11 -10
- package/src/templates/next-pagebuilder/components/layout/header/index.tsx +5 -3
- package/src/templates/next-pagebuilder/components/layout/json-ld/index.tsx +9 -10
- package/src/templates/next-pagebuilder/components/layout/wrapper/index.tsx +3 -11
- package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/index.tsx +1 -1
- package/src/templates/next-pagebuilder/knip.json +27 -0
- package/src/templates/next-pagebuilder/lefthook.yml +5 -0
- package/src/templates/next-pagebuilder/lib/scripts/setup-ci-secrets.ts +117 -0
- package/src/templates/next-pagebuilder/package.json +9 -3
- package/src/templates/next-webgl/.github/workflows/ci.yml +62 -0
- package/src/templates/next-webgl/README.md +22 -0
- package/src/templates/next-webgl/knip.json +13 -0
- package/src/templates/next-webgl/lefthook.yml +5 -0
- package/src/templates/next-webgl/package.json +8 -4
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export interface RssFeedItem {
|
|
2
|
+
title: string;
|
|
3
|
+
link: string;
|
|
4
|
+
description?: string | undefined;
|
|
5
|
+
pubDate?: string | undefined;
|
|
6
|
+
authors?: string[] | undefined;
|
|
7
|
+
category?: string | undefined;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface RssFeedConfig {
|
|
11
|
+
title: string;
|
|
12
|
+
link: string;
|
|
13
|
+
description: string;
|
|
14
|
+
feedUrl: string;
|
|
15
|
+
language?: string;
|
|
16
|
+
items: RssFeedItem[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function escapeXml(str: string): string {
|
|
20
|
+
return str
|
|
21
|
+
.replace(/&/g, "&")
|
|
22
|
+
.replace(/</g, "<")
|
|
23
|
+
.replace(/>/g, ">")
|
|
24
|
+
.replace(/"/g, """)
|
|
25
|
+
.replace(/'/g, "'");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function generateRssFeed(config: RssFeedConfig): Response {
|
|
29
|
+
const {
|
|
30
|
+
title,
|
|
31
|
+
link,
|
|
32
|
+
description,
|
|
33
|
+
feedUrl,
|
|
34
|
+
language = "en-us",
|
|
35
|
+
items,
|
|
36
|
+
} = config;
|
|
37
|
+
|
|
38
|
+
const itemsXml = items
|
|
39
|
+
.map((item) => {
|
|
40
|
+
const lines = [
|
|
41
|
+
` <title>${escapeXml(item.title)}</title>`,
|
|
42
|
+
` <link>${escapeXml(item.link)}</link>`,
|
|
43
|
+
` <guid isPermaLink="true">${escapeXml(item.link)}</guid>`,
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
if (item.description) {
|
|
47
|
+
lines.push(
|
|
48
|
+
` <description>${escapeXml(item.description)}</description>`,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const pubDate = item.pubDate ? new Date(item.pubDate) : null;
|
|
53
|
+
if (pubDate && !Number.isNaN(pubDate.getTime())) {
|
|
54
|
+
lines.push(` <pubDate>${pubDate.toUTCString()}</pubDate>`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
for (const author of item.authors ?? []) {
|
|
58
|
+
lines.push(` <dc:creator>${escapeXml(author)}</dc:creator>`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (item.category) {
|
|
62
|
+
lines.push(` <category>${escapeXml(item.category)}</category>`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return ` <item>\n${lines.join("\n")}\n </item>`;
|
|
66
|
+
})
|
|
67
|
+
.join("\n");
|
|
68
|
+
|
|
69
|
+
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
70
|
+
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom">
|
|
71
|
+
<channel>
|
|
72
|
+
<title>${escapeXml(title)}</title>
|
|
73
|
+
<link>${escapeXml(link)}</link>
|
|
74
|
+
<description>${escapeXml(description)}</description>
|
|
75
|
+
<language>${language}</language>
|
|
76
|
+
<atom:link href="${escapeXml(feedUrl)}" rel="self" type="application/rss+xml" />
|
|
77
|
+
${itemsXml}
|
|
78
|
+
</channel>
|
|
79
|
+
</rss>`;
|
|
80
|
+
|
|
81
|
+
return new Response(xml, {
|
|
82
|
+
headers: {
|
|
83
|
+
"Content-Type": "application/rss+xml",
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function rssErrorResponse(): Response {
|
|
89
|
+
return new Response(
|
|
90
|
+
'<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>Error</title></channel></rss>',
|
|
91
|
+
{
|
|
92
|
+
status: 500,
|
|
93
|
+
headers: { "Content-Type": "application/xml" },
|
|
94
|
+
},
|
|
95
|
+
);
|
|
96
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { PINNED_PAGE_ENTRIES } from "@/lib/integrations/sanity/pinned-pages";
|
|
2
|
+
import { PAGE_SITEMAP_QUERY } from "@/lib/integrations/sanity/queries";
|
|
3
|
+
import type { PAGE_SITEMAP_QUERY_RESULT } from "@/lib/integrations/sanity/sanity.types";
|
|
4
|
+
import { getBaseUrl } from "./base-url";
|
|
5
|
+
|
|
6
|
+
export const getSitemapBaseUrl = getBaseUrl;
|
|
7
|
+
|
|
8
|
+
const getNormalizedPageSlug = (slug: string | null) => {
|
|
9
|
+
if (!slug || slug === "/") return "";
|
|
10
|
+
return slug.replace(/^\/+|\/+$/g, "");
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const getPagePath = (slug: string | null) => {
|
|
14
|
+
const normalizedSlug = getNormalizedPageSlug(slug);
|
|
15
|
+
return normalizedSlug ? `/${normalizedSlug}` : "/";
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const getPageUrl = (baseUrl: string, slug: string | null) =>
|
|
19
|
+
new URL(getPagePath(slug), baseUrl).toString();
|
|
20
|
+
|
|
21
|
+
export const getMarkdownPath = (baseUrl: string, slug: string | null) => {
|
|
22
|
+
const normalizedSlug = getNormalizedPageSlug(slug);
|
|
23
|
+
|
|
24
|
+
return normalizedSlug
|
|
25
|
+
? `${baseUrl}/${normalizedSlug}.md`
|
|
26
|
+
: `${baseUrl}/index.md`;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const getSitemap = async () => {
|
|
30
|
+
const sanityModule = await import("@/lib/integrations/sanity/client");
|
|
31
|
+
const client = sanityModule?.client;
|
|
32
|
+
|
|
33
|
+
if (!client) return null;
|
|
34
|
+
|
|
35
|
+
const p = await client.fetch<PAGE_SITEMAP_QUERY_RESULT>(PAGE_SITEMAP_QUERY, {
|
|
36
|
+
pinnedIds: PINNED_PAGE_ENTRIES.map((entry) => entry.id),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return p;
|
|
40
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { NextRequest } from "next/server";
|
|
2
|
+
import { NextResponse } from "next/server";
|
|
3
|
+
import {
|
|
4
|
+
acceptHeaderRoutes,
|
|
5
|
+
ignoredPaths,
|
|
6
|
+
mdExtensionRoutes,
|
|
7
|
+
} from "@/lib/integrations/sanity/markdown-proxy.config";
|
|
8
|
+
|
|
9
|
+
export function proxy(request: NextRequest) {
|
|
10
|
+
const { pathname } = request.nextUrl;
|
|
11
|
+
const acceptHeader = request.headers.get("accept") || "";
|
|
12
|
+
|
|
13
|
+
if (ignoredPaths.some((p) => pathname.startsWith(p))) {
|
|
14
|
+
return NextResponse.next();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (pathname === "/" && acceptHeader.includes("text/markdown")) {
|
|
18
|
+
const url = request.nextUrl.clone();
|
|
19
|
+
url.pathname = "/api/index.md";
|
|
20
|
+
return NextResponse.rewrite(url);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 1. Rewrite .md extension URLs to API routes
|
|
24
|
+
// e.g. /about.md → /api/about.md
|
|
25
|
+
for (const route of mdExtensionRoutes) {
|
|
26
|
+
const match = route.regex.exec(pathname);
|
|
27
|
+
const slug = match?.[1];
|
|
28
|
+
if (slug) {
|
|
29
|
+
const url = request.nextUrl.clone();
|
|
30
|
+
url.pathname = route.apiPath.replace("[slug]", slug);
|
|
31
|
+
return NextResponse.rewrite(url);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 2. Accept: text/markdown — rewrite to markdown API route
|
|
36
|
+
if (acceptHeader.includes("text/markdown")) {
|
|
37
|
+
for (const route of acceptHeaderRoutes) {
|
|
38
|
+
const match = route.regex.exec(pathname);
|
|
39
|
+
const slug = match?.[1];
|
|
40
|
+
if (slug) {
|
|
41
|
+
const url = request.nextUrl.clone();
|
|
42
|
+
url.pathname = route.apiPath.replace("[slug]", slug);
|
|
43
|
+
return NextResponse.rewrite(url);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 3. Add Link header for markdown discoverability on HTML page requests
|
|
49
|
+
const response = NextResponse.next();
|
|
50
|
+
|
|
51
|
+
if (pathname === "/") {
|
|
52
|
+
response.headers.set(
|
|
53
|
+
"Link",
|
|
54
|
+
'</index.md>; rel="alternate"; type="text/markdown"',
|
|
55
|
+
);
|
|
56
|
+
response.headers.set("Vary", "Accept");
|
|
57
|
+
return response;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
for (const route of acceptHeaderRoutes) {
|
|
61
|
+
const match = route.regex.exec(pathname);
|
|
62
|
+
const slug = match?.[1];
|
|
63
|
+
if (slug) {
|
|
64
|
+
const mdUrl = route.publicPath.replace("[slug]", slug);
|
|
65
|
+
response.headers.set(
|
|
66
|
+
"Link",
|
|
67
|
+
`<${mdUrl}>; rel="alternate"; type="text/markdown"`,
|
|
68
|
+
);
|
|
69
|
+
response.headers.set("Vary", "Accept");
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return response;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const config = {
|
|
78
|
+
matcher: [
|
|
79
|
+
"/((?!_next/static|_next/image|favicon.ico|sitemap.xml|sitemap.md|robots.txt).*)",
|
|
80
|
+
],
|
|
81
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
push:
|
|
8
|
+
branches:
|
|
9
|
+
- main
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
ci:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
# Node version is read from .nvmrc — the single source of truth.
|
|
20
|
+
- name: Setup Node
|
|
21
|
+
uses: actions/setup-node@v4
|
|
22
|
+
with:
|
|
23
|
+
node-version-file: ".nvmrc"
|
|
24
|
+
|
|
25
|
+
# Bun version is read from package.json's "packageManager" field — the
|
|
26
|
+
# single source of truth. Do NOT pin a version here, or the two can drift.
|
|
27
|
+
- name: Setup Bun
|
|
28
|
+
uses: oven-sh/setup-bun@v2
|
|
29
|
+
|
|
30
|
+
# Cache Bun's global install cache so dependencies aren't re-downloaded
|
|
31
|
+
# on every run. Keyed on the lockfile so it busts when deps change.
|
|
32
|
+
- name: Cache Bun dependencies
|
|
33
|
+
uses: actions/cache@v4
|
|
34
|
+
with:
|
|
35
|
+
path: ~/.bun/install/cache
|
|
36
|
+
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
|
|
37
|
+
restore-keys: |
|
|
38
|
+
${{ runner.os }}-bun-
|
|
39
|
+
|
|
40
|
+
- name: Install dependencies
|
|
41
|
+
run: bun install --frozen-lockfile
|
|
42
|
+
|
|
43
|
+
- name: Type check
|
|
44
|
+
run: bun run typecheck:compare
|
|
45
|
+
|
|
46
|
+
- name: Lint
|
|
47
|
+
run: bun run lint
|
|
48
|
+
|
|
49
|
+
- name: Build
|
|
50
|
+
run: bun run build
|
|
51
|
+
|
|
52
|
+
# Runs your tests if any exist; a fresh project with none passes cleanly.
|
|
53
|
+
- name: Test
|
|
54
|
+
run: |
|
|
55
|
+
if git ls-files '*.test.*' '*.spec.*' '*_test.*' '*_spec.*' | grep -q .; then
|
|
56
|
+
bun test
|
|
57
|
+
else
|
|
58
|
+
echo "No test files found — skipping bun test."
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
- name: Knip
|
|
62
|
+
run: bun run knip
|
|
@@ -48,3 +48,25 @@ lib/
|
|
|
48
48
|
- Edit `lib/styles/tokens.css` for theme variables, breakpoints, and custom Tailwind utilities.
|
|
49
49
|
- Edit `lib/styles/global.css` for reset rules and app-wide global styles.
|
|
50
50
|
- `lib/styles/fonts.ts` handles font variables.
|
|
51
|
+
|
|
52
|
+
## Continuous Integration
|
|
53
|
+
|
|
54
|
+
A GitHub Actions workflow (`.github/workflows/ci.yml`) runs on every pull request to `main` and on pushes to `main`. It runs type checking, linting, the production build, tests, and `knip`.
|
|
55
|
+
|
|
56
|
+
The workflow takes its runtime versions from your manifests, so there's a single source of truth and nothing to keep in sync by hand:
|
|
57
|
+
|
|
58
|
+
- **Node** comes from `.nvmrc`.
|
|
59
|
+
- **Bun** comes from the `packageManager` field in `package.json`.
|
|
60
|
+
|
|
61
|
+
It also caches Bun's install cache between runs, and the test step is a no-op until you add real test files.
|
|
62
|
+
|
|
63
|
+
### Branch protection
|
|
64
|
+
|
|
65
|
+
To block merges when CI fails, enable branch protection on `main`:
|
|
66
|
+
|
|
67
|
+
1. Go to **Settings → Branches → Add branch ruleset** (or **Add rule**).
|
|
68
|
+
2. Target the `main` branch.
|
|
69
|
+
3. Enable **Require status checks to pass before merging** and select the **CI** check.
|
|
70
|
+
4. (Recommended) Enable **Require a pull request before merging**.
|
|
71
|
+
|
|
72
|
+
Pull requests then cannot be merged until the CI workflow passes.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://unpkg.com/knip@6/schema.json",
|
|
3
|
+
"entry": [
|
|
4
|
+
"lib/scripts/**/*.{ts,tsx}",
|
|
5
|
+
"lib/hooks/**/*.{ts,tsx}",
|
|
6
|
+
"lib/utils/**/*.{ts,tsx}",
|
|
7
|
+
"lib/renderer.ts",
|
|
8
|
+
"components/**/*.{ts,tsx}"
|
|
9
|
+
],
|
|
10
|
+
"ignoreDependencies": [
|
|
11
|
+
"zod",
|
|
12
|
+
"@svgr/webpack"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
"description": "Basement Next.js starter template",
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"private": true,
|
|
6
|
-
"packageManager": "bun@1.3.
|
|
6
|
+
"packageManager": "bun@1.3.14",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=24"
|
|
9
|
+
},
|
|
7
10
|
"scripts": {
|
|
8
11
|
"analyze": "cross-env ANALYZE=true bun run build",
|
|
9
12
|
"analyze:experimental": "next experimental-analyze",
|
|
@@ -12,8 +15,8 @@
|
|
|
12
15
|
"dev:https": "bun ./lib/scripts/dev.ts --https",
|
|
13
16
|
"dev:inspect": "bun ./lib/scripts/dev.ts --inspect",
|
|
14
17
|
"format": "biome format --write .",
|
|
15
|
-
"knip": "
|
|
16
|
-
"knip:fix": "
|
|
18
|
+
"knip": "knip",
|
|
19
|
+
"knip:fix": "knip --fix",
|
|
17
20
|
"lighthouse": "bunx @unlighthouse/cli --site http://localhost:3000",
|
|
18
21
|
"lint": "biome lint --max-diagnostics=200",
|
|
19
22
|
"lint:fix": "biome check --write --unsafe --max-diagnostics=200",
|
|
@@ -39,12 +42,13 @@
|
|
|
39
42
|
"@svgr/webpack": "^8.1.0",
|
|
40
43
|
"@tailwindcss/postcss": "^4",
|
|
41
44
|
"@types/bun": "^1.3.6",
|
|
42
|
-
"@types/node": "^
|
|
45
|
+
"@types/node": "^24.13.1",
|
|
43
46
|
"@types/react": "^19.2.9",
|
|
44
47
|
"@types/react-dom": "^19.2.3",
|
|
45
48
|
"@typescript/native-preview": "^7.0.0-dev.20260122.3",
|
|
46
49
|
"babel-plugin-react-compiler": "^1.0.0",
|
|
47
50
|
"cross-env": "^10.1.0",
|
|
51
|
+
"knip": "^6.16.1",
|
|
48
52
|
"lefthook": "^1.13.0",
|
|
49
53
|
"postcss-preset-env": "^10.6.1",
|
|
50
54
|
"schema-dts": "^2.0.0",
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
push:
|
|
8
|
+
branches:
|
|
9
|
+
- main
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
ci:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
# Node version is read from .nvmrc — the single source of truth.
|
|
20
|
+
- name: Setup Node
|
|
21
|
+
uses: actions/setup-node@v4
|
|
22
|
+
with:
|
|
23
|
+
node-version-file: ".nvmrc"
|
|
24
|
+
|
|
25
|
+
# Bun version is read from package.json's "packageManager" field — the
|
|
26
|
+
# single source of truth. Do NOT pin a version here, or the two can drift.
|
|
27
|
+
- name: Setup Bun
|
|
28
|
+
uses: oven-sh/setup-bun@v2
|
|
29
|
+
|
|
30
|
+
# Cache Bun's global install cache so dependencies aren't re-downloaded
|
|
31
|
+
# on every run. Keyed on the lockfile so it busts when deps change.
|
|
32
|
+
- name: Cache Bun dependencies
|
|
33
|
+
uses: actions/cache@v4
|
|
34
|
+
with:
|
|
35
|
+
path: ~/.bun/install/cache
|
|
36
|
+
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
|
|
37
|
+
restore-keys: |
|
|
38
|
+
${{ runner.os }}-bun-
|
|
39
|
+
|
|
40
|
+
- name: Install dependencies
|
|
41
|
+
run: bun install --frozen-lockfile
|
|
42
|
+
|
|
43
|
+
- name: Type check
|
|
44
|
+
run: bun run typecheck:compare
|
|
45
|
+
|
|
46
|
+
- name: Lint
|
|
47
|
+
run: bun run lint
|
|
48
|
+
|
|
49
|
+
- name: Build
|
|
50
|
+
run: bun run build
|
|
51
|
+
|
|
52
|
+
# Runs your tests if any exist; a fresh project with none passes cleanly.
|
|
53
|
+
- name: Test
|
|
54
|
+
run: |
|
|
55
|
+
if git ls-files '*.test.*' '*.spec.*' '*_test.*' '*_spec.*' | grep -q .; then
|
|
56
|
+
bun test
|
|
57
|
+
else
|
|
58
|
+
echo "No test files found — skipping bun test."
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
- name: Knip
|
|
62
|
+
run: bun run knip
|
|
@@ -48,3 +48,25 @@ lib/
|
|
|
48
48
|
- Edit `lib/styles/tokens.css` for theme variables, breakpoints, and custom Tailwind utilities.
|
|
49
49
|
- Edit `lib/styles/global.css` for reset rules and app-wide global styles.
|
|
50
50
|
- `lib/styles/fonts.ts` handles font variables.
|
|
51
|
+
|
|
52
|
+
## Continuous Integration
|
|
53
|
+
|
|
54
|
+
A GitHub Actions workflow (`.github/workflows/ci.yml`) runs on every pull request to `main` and on pushes to `main`. It runs type checking, linting, the production build, tests, and `knip`.
|
|
55
|
+
|
|
56
|
+
The workflow takes its runtime versions from your manifests, so there's a single source of truth and nothing to keep in sync by hand:
|
|
57
|
+
|
|
58
|
+
- **Node** comes from `.nvmrc`.
|
|
59
|
+
- **Bun** comes from the `packageManager` field in `package.json`.
|
|
60
|
+
|
|
61
|
+
It also caches Bun's install cache between runs, and the test step is a no-op until you add real test files.
|
|
62
|
+
|
|
63
|
+
### Branch protection
|
|
64
|
+
|
|
65
|
+
To block merges when CI fails, enable branch protection on `main`:
|
|
66
|
+
|
|
67
|
+
1. Go to **Settings → Branches → Add branch ruleset** (or **Add rule**).
|
|
68
|
+
2. Target the `main` branch.
|
|
69
|
+
3. Enable **Require status checks to pass before merging** and select the **CI** check.
|
|
70
|
+
4. (Recommended) Enable **Require a pull request before merging**.
|
|
71
|
+
|
|
72
|
+
Pull requests then cannot be merged until the CI workflow passes.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://unpkg.com/knip@6/schema.json",
|
|
3
|
+
"entry": [
|
|
4
|
+
"lib/scripts/**/*.{ts,tsx}",
|
|
5
|
+
"lib/hooks/**/*.{ts,tsx}",
|
|
6
|
+
"lib/utils/**/*.{ts,tsx}",
|
|
7
|
+
"components/**/*.{ts,tsx}"
|
|
8
|
+
],
|
|
9
|
+
"ignoreDependencies": [
|
|
10
|
+
"zod",
|
|
11
|
+
"three",
|
|
12
|
+
"@svgr/webpack"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Lefthook configuration for git hooks
|
|
2
|
+
# Cross-platform compatible (Windows, macOS, Linux)
|
|
3
|
+
# https://github.com/evilmartians/lefthook
|
|
4
|
+
|
|
5
|
+
pre-commit:
|
|
6
|
+
parallel: true
|
|
7
|
+
commands:
|
|
8
|
+
biome:
|
|
9
|
+
glob: '*.{js,mjs,ts,jsx,tsx,css,scss}'
|
|
10
|
+
run: bun biome check --max-diagnostics=200 --write --unsafe {staged_files}
|
|
11
|
+
stage_fixed: true
|
|
12
|
+
typecheck:
|
|
13
|
+
glob: '*.{ts,tsx}'
|
|
14
|
+
run: bunx tsgo --noEmit --incremental false
|
|
15
|
+
stage_fixed: false
|
|
16
|
+
|
|
17
|
+
pre-push:
|
|
18
|
+
commands:
|
|
19
|
+
knip:
|
|
20
|
+
run: bun run knip
|
|
21
|
+
|
|
22
|
+
post-merge:
|
|
23
|
+
commands:
|
|
24
|
+
install-deps:
|
|
25
|
+
run: bun i
|
|
26
|
+
|
|
27
|
+
post-checkout:
|
|
28
|
+
commands:
|
|
29
|
+
install-deps:
|
|
30
|
+
run: '[ "{3}" = "1" ] && bun i || true'
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
"description": "Basement Next.js starter template",
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"private": true,
|
|
6
|
-
"packageManager": "bun@1.3.
|
|
6
|
+
"packageManager": "bun@1.3.14",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=24"
|
|
9
|
+
},
|
|
7
10
|
"scripts": {
|
|
8
11
|
"analyze": "cross-env ANALYZE=true bun run build",
|
|
9
12
|
"analyze:experimental": "next experimental-analyze",
|
|
@@ -12,9 +15,12 @@
|
|
|
12
15
|
"dev:https": "bun ./lib/scripts/dev.ts --https",
|
|
13
16
|
"dev:inspect": "bun ./lib/scripts/dev.ts --inspect",
|
|
14
17
|
"format": "biome format --write .",
|
|
18
|
+
"knip": "knip",
|
|
19
|
+
"knip:fix": "knip --fix",
|
|
15
20
|
"lighthouse": "bunx @unlighthouse/cli --site http://localhost:3000",
|
|
16
21
|
"lint": "biome lint --max-diagnostics=200",
|
|
17
22
|
"lint:fix": "biome check --write --unsafe --max-diagnostics=200",
|
|
23
|
+
"prepare": "lefthook install",
|
|
18
24
|
"start": "next start",
|
|
19
25
|
"typecheck": "tsgo --noEmit",
|
|
20
26
|
"typecheck:compare": "bun run typecheck:tsc && bun run typecheck",
|
|
@@ -38,12 +44,14 @@
|
|
|
38
44
|
"@svgr/webpack": "^8.1.0",
|
|
39
45
|
"@tailwindcss/postcss": "^4",
|
|
40
46
|
"@types/bun": "^1.3.6",
|
|
41
|
-
"@types/node": "^
|
|
47
|
+
"@types/node": "^24.13.1",
|
|
42
48
|
"@types/react": "^19.2.9",
|
|
43
49
|
"@types/react-dom": "^19.2.3",
|
|
44
50
|
"@typescript/native-preview": "^7.0.0-dev.20260122.3",
|
|
45
51
|
"babel-plugin-react-compiler": "^1.0.0",
|
|
46
52
|
"cross-env": "^10.1.0",
|
|
53
|
+
"knip": "^6.16.1",
|
|
54
|
+
"lefthook": "^1.13.0",
|
|
47
55
|
"postcss-preset-env": "^10.6.1",
|
|
48
56
|
"schema-dts": "^2.0.0",
|
|
49
57
|
"tailwindcss": "^4",
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
push:
|
|
8
|
+
branches:
|
|
9
|
+
- main
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
ci:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
# Node version is read from .nvmrc — the single source of truth.
|
|
20
|
+
- name: Setup Node
|
|
21
|
+
uses: actions/setup-node@v4
|
|
22
|
+
with:
|
|
23
|
+
node-version-file: ".nvmrc"
|
|
24
|
+
|
|
25
|
+
# Bun version is read from package.json's "packageManager" field — the
|
|
26
|
+
# single source of truth. Do NOT pin a version here, or the two can drift.
|
|
27
|
+
- name: Setup Bun
|
|
28
|
+
uses: oven-sh/setup-bun@v2
|
|
29
|
+
|
|
30
|
+
# Cache Bun's global install cache so dependencies aren't re-downloaded
|
|
31
|
+
# on every run. Keyed on the lockfile so it busts when deps change.
|
|
32
|
+
- name: Cache Bun dependencies
|
|
33
|
+
uses: actions/cache@v4
|
|
34
|
+
with:
|
|
35
|
+
path: ~/.bun/install/cache
|
|
36
|
+
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
|
|
37
|
+
restore-keys: |
|
|
38
|
+
${{ runner.os }}-bun-
|
|
39
|
+
|
|
40
|
+
- name: Install dependencies
|
|
41
|
+
run: bun install --frozen-lockfile
|
|
42
|
+
|
|
43
|
+
- name: Type check
|
|
44
|
+
run: bun run typecheck:compare
|
|
45
|
+
|
|
46
|
+
- name: Lint
|
|
47
|
+
run: bun run lint
|
|
48
|
+
|
|
49
|
+
- name: Build
|
|
50
|
+
run: bun run build
|
|
51
|
+
env:
|
|
52
|
+
# Sanity project ID and dataset are public (they ship in the client
|
|
53
|
+
# bundle), so they are stored as repository variables. The Sanity read
|
|
54
|
+
# token is secret — add it under Settings → Secrets and variables →
|
|
55
|
+
# Actions. All three are empty (and harmless) until Sanity is set up.
|
|
56
|
+
NEXT_PUBLIC_SANITY_PROJECT_ID: ${{ vars.NEXT_PUBLIC_SANITY_PROJECT_ID }}
|
|
57
|
+
NEXT_PUBLIC_SANITY_DATASET: ${{ vars.NEXT_PUBLIC_SANITY_DATASET }}
|
|
58
|
+
SANITY_API_READ_TOKEN: ${{ secrets.SANITY_API_READ_TOKEN }}
|
|
59
|
+
|
|
60
|
+
# Runs your tests if any exist; a fresh project with none passes cleanly.
|
|
61
|
+
- name: Test
|
|
62
|
+
run: |
|
|
63
|
+
if git ls-files '*.test.*' '*.spec.*' '*_test.*' '*_spec.*' | grep -q .; then
|
|
64
|
+
bun test
|
|
65
|
+
else
|
|
66
|
+
echo "No test files found — skipping bun test."
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
- name: Knip
|
|
70
|
+
run: bun run knip
|
|
@@ -21,3 +21,37 @@ bun dev
|
|
|
21
21
|
- `bun build` - Production build
|
|
22
22
|
- `bun lint` - Run linter
|
|
23
23
|
- `bun sanity:typegen` - Generate Sanity types
|
|
24
|
+
|
|
25
|
+
## Continuous Integration
|
|
26
|
+
|
|
27
|
+
A GitHub Actions workflow (`.github/workflows/ci.yml`) runs on every pull request to `main` and on pushes to `main`. It runs type checking, linting, the production build, tests, and `knip`.
|
|
28
|
+
|
|
29
|
+
The workflow takes its runtime versions from your manifests, so there's a single source of truth and nothing to keep in sync by hand:
|
|
30
|
+
|
|
31
|
+
- **Node** comes from `.nvmrc`.
|
|
32
|
+
- **Bun** comes from the `packageManager` field in `package.json`.
|
|
33
|
+
|
|
34
|
+
It also caches Bun's install cache between runs, and the test step is a no-op until you add real test files.
|
|
35
|
+
|
|
36
|
+
### Sanity environment
|
|
37
|
+
|
|
38
|
+
The build reads Sanity config from GitHub Actions variables/secrets (empty and harmless until you configure Sanity). Once your repo is on GitHub, the quickest way to set them is:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
bun run ci:secrets
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
This reads the values from `.env.local` and pushes them to this repo with the GitHub CLI — `NEXT_PUBLIC_SANITY_PROJECT_ID` / `NEXT_PUBLIC_SANITY_DATASET` as **Variables** (they are public) and `SANITY_API_READ_TOKEN` as a **Secret**. Requires the [GitHub CLI](https://cli.github.com), authenticated (`gh auth login`).
|
|
45
|
+
|
|
46
|
+
To set them by hand instead, go to **Settings → Secrets and variables → Actions** and add the two `NEXT_PUBLIC_*` values under **Variables** and the token under **Secrets**.
|
|
47
|
+
|
|
48
|
+
### Branch protection
|
|
49
|
+
|
|
50
|
+
To block merges when CI fails, enable branch protection on `main`:
|
|
51
|
+
|
|
52
|
+
1. Go to **Settings → Branches → Add branch ruleset** (or **Add rule**).
|
|
53
|
+
2. Target the `main` branch.
|
|
54
|
+
3. Enable **Require status checks to pass before merging** and select the **CI** check.
|
|
55
|
+
4. (Recommended) Enable **Require a pull request before merging**.
|
|
56
|
+
|
|
57
|
+
Pull requests then cannot be merged until the CI workflow passes.
|
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
ALL_PAGE_SLUGS_QUERY_RESULT,
|
|
11
11
|
PAGE_QUERY_RESULT,
|
|
12
12
|
} from "@/lib/integrations/sanity/sanity.types"
|
|
13
|
+
import { checkRedirect } from "@/lib/utils/check-redirect"
|
|
13
14
|
import { generateSanityMetadata } from "@/lib/utils/metadata"
|
|
14
15
|
import { getSlugTag } from "@/lib/utils/slug-tag"
|
|
15
16
|
|
|
@@ -71,7 +72,11 @@ export default async function CatchAllPage({
|
|
|
71
72
|
const path = toPath(slug)
|
|
72
73
|
const page = await getResolvedPage(path)
|
|
73
74
|
|
|
74
|
-
if (!page)
|
|
75
|
+
if (!page) {
|
|
76
|
+
// Fall back to a CMS redirect before 404ing.
|
|
77
|
+
await checkRedirect(path)
|
|
78
|
+
notFound()
|
|
79
|
+
}
|
|
75
80
|
|
|
76
81
|
return <PageDocument page={page} path={path} searchParams={searchParams} />
|
|
77
82
|
}
|