bsmnt 0.14.0 → 0.15.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/core/create/internal-ids.d.ts +1 -0
- package/dist/core/create/internal-ids.d.ts.map +1 -1
- package/dist/core/create/internal-ids.js +1 -0
- package/dist/core/create/internal-ids.js.map +1 -1
- package/dist/domain/skills.d.ts +5 -0
- package/dist/domain/skills.d.ts.map +1 -1
- package/dist/domain/skills.js +9 -1
- package/dist/domain/skills.js.map +1 -1
- package/dist/infrastructure/create/format-project.d.ts +9 -0
- package/dist/infrastructure/create/format-project.d.ts.map +1 -0
- package/dist/infrastructure/create/format-project.js +21 -0
- package/dist/infrastructure/create/format-project.js.map +1 -0
- package/dist/infrastructure/create/lifecycle-registry.d.ts.map +1 -1
- package/dist/infrastructure/create/lifecycle-registry.js +5 -0
- package/dist/infrastructure/create/lifecycle-registry.js.map +1 -1
- package/dist/modules/features/cms/sanity-pagebuilder/index.d.ts.map +1 -1
- package/dist/modules/features/cms/sanity-pagebuilder/index.js +1 -0
- package/dist/modules/features/cms/sanity-pagebuilder/index.js.map +1 -1
- package/dist/modules/features/cms/sanity-pagebuilder-cache/index.d.ts.map +1 -1
- package/dist/modules/features/cms/sanity-pagebuilder-cache/index.js +1 -0
- package/dist/modules/features/cms/sanity-pagebuilder-cache/index.js.map +1 -1
- package/package.json +1 -1
- package/src/agent-skills/launch-ready/SKILL.md +114 -0
- package/src/agent-skills/launch-ready/references/a11y.md +74 -0
- package/src/agent-skills/launch-ready/references/content.md +65 -0
- package/src/agent-skills/launch-ready/references/crawlers.md +89 -0
- package/src/agent-skills/launch-ready/references/launch.md +44 -0
- package/src/agent-skills/launch-ready/references/metadata.md +83 -0
- package/src/agent-skills/launch-ready/references/performance.md +36 -0
- package/src/agent-skills/launch-ready/references/routing.md +47 -0
- package/src/agent-skills/launch-ready/references/security.md +39 -0
- package/src/modules/features/cms/sanity/files/app/layout.tsx +2 -2
- package/src/modules/features/cms/sanity/files/lib/utils/metadata.ts +2 -2
- package/src/modules/features/cms/sanity/files/lib/utils/url.ts +6 -3
- package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/rich-text.tsx +3 -1
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/redirect-import-dialog.tsx +538 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/csv.ts +88 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/fetchers/breadcrumbs.ts +22 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-builder-config.ts +15 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/queries.ts +20 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/redirect-import.ts +651 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/sanity.config.ts +2 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/redirect.ts +9 -6
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/structured-data.ts +8 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/metadata.ts +60 -6
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/page.ts +22 -1
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/richText.ts +0 -1
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/structure.ts +16 -1
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/tags.ts +5 -2
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/scripts/sanity-typegen.ts +8 -11
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/metadata.ts +109 -136
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/sanity/rich-text.tsx +3 -1
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/redirect-import-dialog.tsx +538 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/csv.ts +88 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/fetchers/breadcrumbs.ts +25 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/page-builder-config.ts +15 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/queries.ts +20 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/redirect-import.ts +651 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/sanity.config.ts +2 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/redirect.ts +9 -6
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/structured-data.ts +8 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/metadata.ts +60 -6
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/page.ts +22 -1
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/richText.ts +0 -1
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/structure.ts +16 -1
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/tags.ts +6 -2
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/scripts/sanity-typegen.ts +8 -11
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/metadata.ts +109 -136
- package/src/templates/next-default/.env.example +3 -1
- package/src/templates/next-default/app/layout.tsx +2 -1
- package/src/templates/next-default/app/robots.ts +9 -3
- package/src/templates/next-default/lib/utils/metadata.ts +2 -1
- package/src/templates/next-default/lib/utils/url.ts +20 -3
- package/src/templates/next-default/next.config.ts +16 -0
- package/src/templates/next-experiments/.env.example +3 -1
- package/src/templates/next-experiments/app/layout.tsx +2 -1
- package/src/templates/next-experiments/app/robots.ts +9 -3
- package/src/templates/next-experiments/lib/utils/metadata.ts +2 -1
- package/src/templates/next-experiments/lib/utils/url.ts +20 -3
- package/src/templates/next-experiments/next.config.ts +16 -0
- package/src/templates/next-pagebuilder/.env.example +3 -1
- package/src/templates/next-pagebuilder/.github/workflows/ci.yml +32 -8
- package/src/templates/next-pagebuilder/AGENTS.md +3 -0
- package/src/templates/next-pagebuilder/app/(content)/[[...slug]]/page.tsx +45 -7
- package/src/templates/next-pagebuilder/app/layout.tsx +3 -41
- package/src/templates/next-pagebuilder/app/robots.ts +9 -3
- package/src/templates/next-pagebuilder/biome.json +2 -1
- package/src/templates/next-pagebuilder/components/layout/footer/index.tsx +1 -1
- package/src/templates/next-pagebuilder/components/page-document/index.tsx +27 -35
- package/src/templates/next-pagebuilder/lefthook.yml +1 -1
- package/src/templates/next-pagebuilder/lib/integrations/sanity/sanity.types.ts +32 -29
- package/src/templates/next-pagebuilder/lib/utils/base-url.ts +18 -3
- package/src/templates/next-pagebuilder/lib/utils/breadcrumbs.ts +70 -0
- package/src/templates/next-pagebuilder/next.config.ts +16 -0
- package/src/templates/next-pagebuilder/package.json +1 -1
- package/src/templates/next-pagebuilder-cache/.env.example +3 -1
- package/src/templates/next-pagebuilder-cache/.github/workflows/ci.yml +32 -8
- package/src/templates/next-pagebuilder-cache/AGENTS.md +3 -0
- package/src/templates/next-pagebuilder-cache/app/(content)/[[...slug]]/page.tsx +46 -7
- package/src/templates/next-pagebuilder-cache/app/layout.tsx +5 -46
- package/src/templates/next-pagebuilder-cache/app/robots.ts +9 -3
- package/src/templates/next-pagebuilder-cache/biome.json +2 -1
- package/src/templates/next-pagebuilder-cache/components/layout/footer/index.tsx +1 -1
- package/src/templates/next-pagebuilder-cache/components/page-document/index.tsx +27 -35
- package/src/templates/next-pagebuilder-cache/lefthook.yml +1 -1
- package/src/templates/next-pagebuilder-cache/lib/integrations/sanity/sanity.types.ts +32 -29
- package/src/templates/next-pagebuilder-cache/lib/utils/base-url.ts +18 -3
- package/src/templates/next-pagebuilder-cache/lib/utils/breadcrumbs.ts +70 -0
- package/src/templates/next-pagebuilder-cache/next.config.ts +16 -0
- package/src/templates/next-pagebuilder-cache/package.json +1 -1
- package/src/templates/next-webgl/.env.example +3 -1
- package/src/templates/next-webgl/app/layout.tsx +2 -1
- package/src/templates/next-webgl/app/robots.ts +9 -3
- package/src/templates/next-webgl/lib/utils/metadata.ts +2 -1
- package/src/templates/next-webgl/lib/utils/url.ts +20 -3
- package/src/templates/next-webgl/next.config.ts +16 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# On the real domain
|
|
2
|
+
|
|
3
|
+
These only fail in production, so check them against the deployed URL.
|
|
4
|
+
|
|
5
|
+
## LAUNCH-01 · Forms deliver on production
|
|
6
|
+
|
|
7
|
+
**Must not ship** · only if the site has forms
|
|
8
|
+
|
|
9
|
+
Submit each form on production and confirm the submission arrives. The usual
|
|
10
|
+
failure: the form's API key is set for Preview but not for Production, so the
|
|
11
|
+
form works on every preview and silently drops submissions after launch.
|
|
12
|
+
|
|
13
|
+
## LAUNCH-02 · CMS publishing updates production
|
|
14
|
+
|
|
15
|
+
**Must not ship** · only if the site uses a CMS
|
|
16
|
+
|
|
17
|
+
Publish a small change in the CMS and confirm production shows it. The
|
|
18
|
+
revalidation webhook points at the production URL and carries its secret.
|
|
19
|
+
|
|
20
|
+
## LAUNCH-03 · One host
|
|
21
|
+
|
|
22
|
+
**Fix before launch**
|
|
23
|
+
|
|
24
|
+
Pick the apex domain or `www`. In Vercel's domain settings, the other
|
|
25
|
+
redirects to it permanently.
|
|
26
|
+
|
|
27
|
+
**Check:**
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
curl -sI https://<other-host>/ | grep -i '^location'
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
It points at the chosen host.
|
|
34
|
+
|
|
35
|
+
## LAUNCH-04 · Old URLs redirect on a relaunch
|
|
36
|
+
|
|
37
|
+
**Fix before launch** · only if *Replaces an existing site: yes*
|
|
38
|
+
|
|
39
|
+
Every old URL with traffic or backlinks redirects to its new equivalent
|
|
40
|
+
through `redirects()` in `next.config.ts`. Ask the user for the old site's URL
|
|
41
|
+
list: its sitemap, or its top pages in analytics.
|
|
42
|
+
|
|
43
|
+
**Check:** request a sample of old URLs. Each returns `308` to a page that
|
|
44
|
+
returns `200`.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Metadata
|
|
2
|
+
|
|
3
|
+
## META-01 · Every page has a real title
|
|
4
|
+
|
|
5
|
+
**Must not ship**
|
|
6
|
+
|
|
7
|
+
Each route exports `metadata` or `generateMetadata` with its own title. If
|
|
8
|
+
`app/layout.tsx` still has the `"Basement Starter"` default and its
|
|
9
|
+
`"%s - Basement Starter"` template, replace both with the site's name.
|
|
10
|
+
|
|
11
|
+
**Check:**
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
curl -s localhost:3000/<route> | grep -o '<title>[^<]*'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## META-02 · Metadata is in the server HTML
|
|
18
|
+
|
|
19
|
+
**Must not ship**
|
|
20
|
+
|
|
21
|
+
Set titles, descriptions and share tags through the Metadata API, never with
|
|
22
|
+
`document.title` or a client effect. Crawlers and link previews don't run
|
|
23
|
+
JavaScript.
|
|
24
|
+
|
|
25
|
+
**Check:** the tags appear in `curl -s localhost:3000/<route>`, not only in
|
|
26
|
+
the browser's DOM.
|
|
27
|
+
|
|
28
|
+
## META-03 · The share image loads
|
|
29
|
+
|
|
30
|
+
**Must not ship**
|
|
31
|
+
|
|
32
|
+
A declared `og:image` is absolute and returns an image.
|
|
33
|
+
|
|
34
|
+
If the layout or `generatePageMetadata` defaults to `/opengraph-image.jpg`,
|
|
35
|
+
add that file: no template ships it. Until `public/opengraph-image.jpg`
|
|
36
|
+
exists, every share card is broken, and so is the Organization JSON-LD logo,
|
|
37
|
+
which uses the same path.
|
|
38
|
+
|
|
39
|
+
**Check:**
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
img=$(curl -s localhost:3000/ | grep -o 'property="og:image" content="[^"]*' | cut -d'"' -f4)
|
|
43
|
+
curl -so /dev/null -w '%{http_code} %{content_type}\n' "$img"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Expect `200 image/...`.
|
|
47
|
+
|
|
48
|
+
## META-04 · No `localhost` URLs in production
|
|
49
|
+
|
|
50
|
+
**Must not ship**
|
|
51
|
+
|
|
52
|
+
`metadataBase`, canonicals and share URLs read `NEXT_PUBLIC_BASE_URL`, then
|
|
53
|
+
the Vercel deployment URL, and fall back to `http://localhost:3000` off Vercel.
|
|
54
|
+
Set it for the Production environment.
|
|
55
|
+
|
|
56
|
+
**Check:**
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
curl -s https://<production>/ | grep -c 'localhost:3000'
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Expect `0`.
|
|
63
|
+
|
|
64
|
+
## META-05 · Titles are unique
|
|
65
|
+
|
|
66
|
+
**Fix before launch**
|
|
67
|
+
|
|
68
|
+
Distinct pages have distinct titles. Variants that canonicalize to one page
|
|
69
|
+
are exempt.
|
|
70
|
+
|
|
71
|
+
## META-06 · Public pages have a description
|
|
72
|
+
|
|
73
|
+
**Fix before launch**
|
|
74
|
+
|
|
75
|
+
Slack, LinkedIn and iMessage show the description as the preview text. If the
|
|
76
|
+
layout's default reads `package.json`'s `description`, write a real one there.
|
|
77
|
+
|
|
78
|
+
## META-07 · `<html lang>` matches the content
|
|
79
|
+
|
|
80
|
+
**Fix before launch**
|
|
81
|
+
|
|
82
|
+
The starter sets `lang="en"`. Change it when the site isn't English, and set
|
|
83
|
+
it per locale on a multilingual site.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Performance
|
|
2
|
+
|
|
3
|
+
## PERF-01 · The LCP image isn't lazy
|
|
4
|
+
|
|
5
|
+
**Fix before launch**
|
|
6
|
+
|
|
7
|
+
The largest image in the first screen (usually the hero) passes `priority` to
|
|
8
|
+
`@/components/ui/image`. Everything below the fold stays lazy, which is the
|
|
9
|
+
default.
|
|
10
|
+
|
|
11
|
+
## PERF-02 · Image `sizes` match the layout
|
|
12
|
+
|
|
13
|
+
**Fix before launch**
|
|
14
|
+
|
|
15
|
+
`sizes` describes the width the image renders at. Without it, the image
|
|
16
|
+
component falls back to `100vw` and the browser downloads the largest
|
|
17
|
+
variant, which is only right for full-width images. Build `sizes` with
|
|
18
|
+
`buildImageSizes` or `buildGridImageSizes` from `lib/utils/image-sizes.ts`.
|
|
19
|
+
|
|
20
|
+
The `no-fill-without-sizes` lint rule catches `fill` with no `sizes`. It can't
|
|
21
|
+
tell whether the value is correct.
|
|
22
|
+
|
|
23
|
+
## PERF-03 · Responses are compressed
|
|
24
|
+
|
|
25
|
+
**Fix before launch** · only when hosted outside Vercel
|
|
26
|
+
|
|
27
|
+
Vercel compresses responses by default. Another host has to serve HTML, JS
|
|
28
|
+
and CSS with gzip or brotli.
|
|
29
|
+
|
|
30
|
+
**Check:**
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
curl -sI -H 'Accept-Encoding: gzip, br' https://<production>/ | grep -i content-encoding
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Expect `gzip` or `br`.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Routing
|
|
2
|
+
|
|
3
|
+
## ROUTE-01 · Missing pages return 404
|
|
4
|
+
|
|
5
|
+
**Must not ship**
|
|
6
|
+
|
|
7
|
+
When a dynamic or CMS route has no data, call `notFound()` before any
|
|
8
|
+
`<Suspense>`. Once a response starts streaming, its status is already 200.
|
|
9
|
+
Next can't change it then, and injects a `noindex` tag instead.
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
export default async function Page({ params }: PageProps<"/blog/[slug]">) {
|
|
13
|
+
const { slug } = await params
|
|
14
|
+
const post = await getPost(slug)
|
|
15
|
+
if (!post) notFound()
|
|
16
|
+
return <Post post={post} />
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Check:**
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
curl -so /dev/null -w '%{http_code}\n' localhost:3000/does-not-exist
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Expect `404`. Repeat under each dynamic segment, such as `/blog/does-not-exist`.
|
|
27
|
+
|
|
28
|
+
**Not this:** redirecting unknown paths to `/`. That's still a missing page
|
|
29
|
+
answering 200.
|
|
30
|
+
|
|
31
|
+
## ROUTE-02 · No internal link is broken
|
|
32
|
+
|
|
33
|
+
**Must not ship**
|
|
34
|
+
|
|
35
|
+
Every internal link resolves to a page that returns 200. A control that only
|
|
36
|
+
runs JavaScript is a `<button>`, not `<a href="#">`. An `#id` that exists on the
|
|
37
|
+
page, or a "back to top" link, is fine.
|
|
38
|
+
|
|
39
|
+
**Check:** list each route's links and request them.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
curl -s localhost:3000/ | grep -o 'href="/[^"#]*"' | cut -d'"' -f2 | sort -u |
|
|
43
|
+
while read -r p; do printf '%s %s\n' "$(curl -so /dev/null -w '%{http_code}' "localhost:3000$p")" "$p"; done
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Every line starts with `200`. A broken link in the nav or footer breaks on every
|
|
47
|
+
page, so check those first.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Security and hygiene
|
|
2
|
+
|
|
3
|
+
## SEC-01 · `NEXT_PUBLIC_` holds only public keys
|
|
4
|
+
|
|
5
|
+
**Must not ship**
|
|
6
|
+
|
|
7
|
+
Anything prefixed `NEXT_PUBLIC_` ships in the browser bundle, readable by
|
|
8
|
+
anyone. Only keys designed to be public belong there: a Sanity project ID, a
|
|
9
|
+
Stripe `pk_` key, an analytics site ID. Read every other secret on the server.
|
|
10
|
+
|
|
11
|
+
**Check:**
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
grep -rhoE 'NEXT_PUBLIC_[A-Z0-9_]+' app components lib | sort -u
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Confirm each name on the list is meant to be public.
|
|
18
|
+
|
|
19
|
+
## SEC-02 · Tracking matches the consent decision
|
|
20
|
+
|
|
21
|
+
**Fix before launch** · only if consent is required
|
|
22
|
+
|
|
23
|
+
No analytics or ad script runs, and no tracking cookie is set, before the
|
|
24
|
+
visitor answers the consent banner. The `ketch-consent-check` skill covers
|
|
25
|
+
gating a script behind Ketch, when the project has it.
|
|
26
|
+
|
|
27
|
+
**Check (browser):** in a fresh private window, before you answer the banner,
|
|
28
|
+
DevTools → Application → Cookies lists no tracking cookies.
|
|
29
|
+
|
|
30
|
+
## SEC-03 · No first-party console errors
|
|
31
|
+
|
|
32
|
+
**Fix before launch**
|
|
33
|
+
|
|
34
|
+
The console shows no errors from the site's own code on load, and no
|
|
35
|
+
hydration mismatches. A mismatch means the server and the client rendered
|
|
36
|
+
different content. Errors from third-party scripts are out of scope.
|
|
37
|
+
|
|
38
|
+
**Check (browser):** load each route with DevTools open, then read the
|
|
39
|
+
console.
|
|
@@ -9,13 +9,13 @@ import { fontsVariable } from "@/lib/styles/fonts";
|
|
|
9
9
|
import AppData from "@/package.json";
|
|
10
10
|
import "@/lib/styles/css/index.css";
|
|
11
11
|
import { cn } from "@/lib/styles/cn";
|
|
12
|
+
import { getBaseUrl } from "@/lib/utils/url";
|
|
12
13
|
|
|
13
14
|
const APP_NAME = AppData.name;
|
|
14
15
|
const APP_DEFAULT_TITLE = "Basement Starter";
|
|
15
16
|
const APP_TITLE_TEMPLATE = "%s - Basement Starter";
|
|
16
17
|
const APP_DESCRIPTION = AppData.description;
|
|
17
|
-
const APP_BASE_URL =
|
|
18
|
-
process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:3000";
|
|
18
|
+
const APP_BASE_URL = getBaseUrl();
|
|
19
19
|
|
|
20
20
|
const geist = Geist({
|
|
21
21
|
subsets: ["latin"],
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Metadata } from "next";
|
|
2
|
+
import { getBaseUrl } from "@/lib/utils/url";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Metadata Generation Utilities
|
|
@@ -26,8 +27,7 @@ interface GenerateMetadataOptions {
|
|
|
26
27
|
authors?: string[];
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
const APP_BASE_URL =
|
|
30
|
-
process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:3000";
|
|
30
|
+
const APP_BASE_URL = getBaseUrl();
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Generate complete metadata object for pages
|
|
@@ -3,15 +3,18 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Priority:
|
|
5
5
|
* 1. NEXT_PUBLIC_BASE_URL - explicit override (custom domains)
|
|
6
|
-
* 2. VERCEL_PROJECT_PRODUCTION_URL - auto-set by Vercel
|
|
7
|
-
* 3. VERCEL_URL -
|
|
6
|
+
* 2. VERCEL_PROJECT_PRODUCTION_URL - auto-set by Vercel, on production deployments only
|
|
7
|
+
* 3. VERCEL_URL - the deployment's own URL, so previews never emit production URLs
|
|
8
8
|
* 4. localhost fallback
|
|
9
9
|
*/
|
|
10
10
|
export function getBaseUrl(): string {
|
|
11
11
|
if (process.env.NEXT_PUBLIC_BASE_URL) {
|
|
12
12
|
return process.env.NEXT_PUBLIC_BASE_URL;
|
|
13
13
|
}
|
|
14
|
-
if (
|
|
14
|
+
if (
|
|
15
|
+
process.env.VERCEL_ENV === "production" &&
|
|
16
|
+
process.env.VERCEL_PROJECT_PRODUCTION_URL
|
|
17
|
+
) {
|
|
15
18
|
return `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`;
|
|
16
19
|
}
|
|
17
20
|
if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`;
|
|
@@ -80,7 +80,9 @@ export const RichText = ({ content }: RichTextProps) => {
|
|
|
80
80
|
externalLink: LinkMark,
|
|
81
81
|
},
|
|
82
82
|
block: {
|
|
83
|
-
h1:
|
|
83
|
+
// The h1 style is retired: the page owns its H1, so legacy content
|
|
84
|
+
// drops a level.
|
|
85
|
+
h1: ({ children }) => <h2 className="h2">{children}</h2>,
|
|
84
86
|
h2: ({ children }) => <h2 className="h2">{children}</h2>,
|
|
85
87
|
h3: ({ children }) => <h3 className="h3">{children}</h3>,
|
|
86
88
|
h4: ({ children }) => <h4 className="h4">{children}</h4>,
|