ferst-core 0.8.1 → 0.8.2
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/content/cookies.ts +23 -3
- package/lib/pages.ts +13 -2
- package/package.json +1 -1
- package/routes/404.astro +15 -4
- package/routes/sitemap.xml.ts +4 -4
package/content/cookies.ts
CHANGED
|
@@ -150,7 +150,11 @@ function otherMapUse(provider: string): CookieUse {
|
|
|
150
150
|
export interface CookieScanInput {
|
|
151
151
|
/** Every block on the site's pages. Container children (`section` / `grid` / `stack`)
|
|
152
152
|
* are walked, so an embed nested in a grid counts. */
|
|
153
|
-
blocks
|
|
153
|
+
blocks?: Iterable<BlockLike>;
|
|
154
|
+
/** @deprecated Block types alone, as before 0.8.1: judged by type, not by source (any
|
|
155
|
+
* video block counts as YouTube and Vimeo). Kept so a site's own older policy page
|
|
156
|
+
* still builds; pass `blocks`. */
|
|
157
|
+
blockTypes?: Iterable<string>;
|
|
154
158
|
/** True when the site has a Google Calendar configured (dedicated calendar page). */
|
|
155
159
|
calendarConfigured?: boolean;
|
|
156
160
|
}
|
|
@@ -171,9 +175,25 @@ function allBlocks(blocks: Iterable<BlockLike>, acc: BlockLike[] = []): BlockLik
|
|
|
171
175
|
return acc;
|
|
172
176
|
}
|
|
173
177
|
|
|
178
|
+
/** How a provider was detected before 0.8.1, from block types alone. */
|
|
179
|
+
const BY_TYPE: Record<string, string[]> = {
|
|
180
|
+
'google-maps': ['locations', 'mapEmbed'],
|
|
181
|
+
'google-calendar': ['calendar'],
|
|
182
|
+
youtube: ['video'],
|
|
183
|
+
vimeo: ['video'],
|
|
184
|
+
};
|
|
185
|
+
|
|
174
186
|
/** Report the cookie uses actually active on a site, grouped by category. */
|
|
175
187
|
export function scanCookieUses(input: CookieScanInput): CookiePolicyModel {
|
|
176
|
-
const
|
|
188
|
+
const necessary = NECESSARY.map((u) => ({ ...u }));
|
|
189
|
+
if (!input.blocks && input.blockTypes) {
|
|
190
|
+
const types = new Set(input.blockTypes);
|
|
191
|
+
const additional = ADDITIONAL.filter(
|
|
192
|
+
(u) => (BY_TYPE[u.id] ?? []).some((t) => types.has(t)) || (u.calendar === true && !!input.calendarConfigured),
|
|
193
|
+
).map(({ loads, calendar, ...use }) => use);
|
|
194
|
+
return { necessary, additional };
|
|
195
|
+
}
|
|
196
|
+
const blocks = allBlocks(input.blocks ?? []);
|
|
177
197
|
const additional: CookieUse[] = ADDITIONAL.filter(
|
|
178
198
|
(u) => (u.loads ? blocks.some(u.loads) : false) || (u.calendar === true && !!input.calendarConfigured),
|
|
179
199
|
).map(({ loads, calendar, ...use }) => use); // drop detection fields from the view model
|
|
@@ -181,7 +201,7 @@ export function scanCookieUses(input: CookieScanInput): CookiePolicyModel {
|
|
|
181
201
|
const otherMaps = new Set(blocks.map(mapProviderOf).filter((p): p is string => p !== undefined && !isGoogleMaps(p)));
|
|
182
202
|
const afterMaps = additional.findIndex((u) => u.id !== 'google-maps');
|
|
183
203
|
additional.splice(afterMaps === -1 ? additional.length : afterMaps, 0, ...[...otherMaps].map(otherMapUse));
|
|
184
|
-
return { necessary
|
|
204
|
+
return { necessary, additional };
|
|
185
205
|
}
|
|
186
206
|
|
|
187
207
|
/** Every block `type` used in a list of blocks, walking container children
|
package/lib/pages.ts
CHANGED
|
@@ -29,12 +29,21 @@ export async function getPageEntries() {
|
|
|
29
29
|
return all.filter((entry) => !entry.data.draft);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/** The reserved entry a site may design as its own "page not found" page. */
|
|
33
|
+
export const NOT_FOUND_ID = '404';
|
|
34
|
+
|
|
35
|
+
/** The site's own "page not found" page, if it has designed one (a `404` entry). The
|
|
36
|
+
* injected /404 route shows it at every missing address; it is never a page of its own. */
|
|
37
|
+
export async function getNotFoundEntry() {
|
|
38
|
+
return (await getPageEntries()).find((entry) => entry.id === NOT_FOUND_ID);
|
|
39
|
+
}
|
|
40
|
+
|
|
32
41
|
/**
|
|
33
42
|
* A drop-in `getStaticPaths` for a client's `[...slug].astro`: one route per
|
|
34
43
|
* non-draft page, keyed by the file's slug, with the entry passed as a prop.
|
|
35
44
|
*/
|
|
36
45
|
export async function getPageRoutes() {
|
|
37
|
-
const entries = await getPageEntries();
|
|
46
|
+
const entries = (await getPageEntries()).filter((entry) => entry.id !== NOT_FOUND_ID);
|
|
38
47
|
return entries.map((entry) => ({
|
|
39
48
|
params: { slug: entry.id },
|
|
40
49
|
props: { entry },
|
|
@@ -58,7 +67,9 @@ export function pageRouteParams(id: string): { slug: string | undefined } {
|
|
|
58
67
|
* AND can pick its home layout in the builder like any other page.
|
|
59
68
|
*/
|
|
60
69
|
export async function getSiteRoutes() {
|
|
61
|
-
|
|
70
|
+
// The `404` entry is the site's "page not found" page, shown by the /404 route at every
|
|
71
|
+
// missing address; it must not also be a page at /404/ (or listed in the sitemap).
|
|
72
|
+
const entries = (await getPageEntries()).filter((entry) => entry.id !== NOT_FOUND_ID);
|
|
62
73
|
return entries.map((entry) => ({
|
|
63
74
|
params: pageRouteParams(entry.id),
|
|
64
75
|
props: { entry },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ferst-core",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Ferst Core — the shared, brand-agnostic client-site system: Astro components, layouts, content schemas, the layered config resolver, and a neutral styling architecture that every client overrides.",
|
|
6
6
|
"license": "BUSL-1.1",
|
package/routes/404.astro
CHANGED
|
@@ -5,11 +5,16 @@
|
|
|
5
5
|
* a `404.html` it serves the HOME PAGE with a 200 instead (a "soft 404"): search engines
|
|
6
6
|
* would see the home page at every mistyped or retired address, and a broken link would
|
|
7
7
|
* look fine to visitors and to the migration's redirect check. A site with its own
|
|
8
|
-
* `src/pages/404.astro` keeps it (the integration skips this route then).
|
|
8
|
+
* `src/pages/404.astro` keeps it (the integration skips this route then). A site that has
|
|
9
|
+
* designed its "page not found" page as content (a `404` entry in `pages`) gets that page.
|
|
9
10
|
*/
|
|
10
11
|
import Base from '../layouts/Base.astro';
|
|
11
12
|
import BlockRenderer from '../components/BlockRenderer.astro';
|
|
12
13
|
import { pageSchema } from '../content/blocks';
|
|
14
|
+
import { getNotFoundEntry } from '../lib/pages';
|
|
15
|
+
|
|
16
|
+
const own = await getNotFoundEntry();
|
|
17
|
+
const ownPage = own ? pageSchema.parse(own.data) : null;
|
|
13
18
|
|
|
14
19
|
const blog = import.meta.env.FERST_ROUTES_BLOG !== false;
|
|
15
20
|
const page = pageSchema.parse({
|
|
@@ -31,6 +36,12 @@ const page = pageSchema.parse({
|
|
|
31
36
|
});
|
|
32
37
|
---
|
|
33
38
|
|
|
34
|
-
|
|
35
|
-
<
|
|
36
|
-
|
|
39
|
+
{ownPage ? (
|
|
40
|
+
<Base title={ownPage.title} description={ownPage.description ?? "This page isn't here."} align={ownPage.align} indexable={false}>
|
|
41
|
+
<BlockRenderer blocks={ownPage.blocks} />
|
|
42
|
+
</Base>
|
|
43
|
+
) : (
|
|
44
|
+
<Base title={page.title} description="This page isn't here." indexable={false}>
|
|
45
|
+
<BlockRenderer blocks={page.blocks} />
|
|
46
|
+
</Base>
|
|
47
|
+
)}
|
package/routes/sitemap.xml.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// and each tag page in use, and the calendar when it's set up. Drafts, hidden tags, later
|
|
4
4
|
// listing pages (/posts/page/2…) and the cookie policy stay out.
|
|
5
5
|
import type { APIRoute } from 'astro';
|
|
6
|
-
import {
|
|
6
|
+
import { getSiteRoutes } from '../lib/pages';
|
|
7
7
|
import { canonicalUrl, sitemapXml } from '../lib/seo.mjs';
|
|
8
8
|
|
|
9
9
|
export const prerender = true;
|
|
@@ -12,9 +12,9 @@ export const GET: APIRoute = async () => {
|
|
|
12
12
|
const site = String(import.meta.env.FERST_SITE_URL || '');
|
|
13
13
|
const entries: Array<{ loc: string; lastmod?: Date }> = [];
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
entries.push({ loc: canonicalUrl(site, slug ? `/${slug}` : '/') });
|
|
15
|
+
// Exactly the pages the site routes (so never its "page not found" page).
|
|
16
|
+
for (const { params } of await getSiteRoutes()) {
|
|
17
|
+
entries.push({ loc: canonicalUrl(site, params.slug ? `/${params.slug}` : '/') });
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
if (import.meta.env.FERST_ROUTES_BLOG !== false) {
|