@apleasantview/eleventy-plugin-baseline 0.1.0-next.41 → 0.1.0-next.42
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 +19 -19
- package/core/content-graph/extractors.js +63 -18
- package/core/content-graph/graph.js +5 -2
- package/core/dates/git-date.js +71 -0
- package/core/dates/index.js +55 -0
- package/core/locale/derive-lang.js +19 -0
- package/core/locale/index.js +6 -0
- package/core/locale/normalize-lang.js +13 -0
- package/core/locale/normalize-locale.js +20 -0
- package/core/locale/open-graph-locale.js +14 -0
- package/core/locale/resolve-default.js +27 -0
- package/core/locale/resolve-locale.js +16 -0
- package/core/markdown/wikilinks.js +1 -1
- package/core/page-context/build.js +120 -23
- package/core/schema.js +3 -1
- package/core/seo-graph/adapter.js +246 -0
- package/core/seo-graph/build.js +87 -0
- package/core/seo-graph/index.js +1 -0
- package/core/seo-graph/open-graph.js +130 -0
- package/core/seo-graph/register.js +42 -0
- package/core/seo-graph/schema.js +18 -0
- package/core/state.js +3 -1
- package/core/surface/index.js +1 -1
- package/core/types.js +3 -0
- package/core/utils/{normalize-languages.js → normalize-language-map.js} +14 -5
- package/core/utils/title-case-slug.js +15 -0
- package/index.js +15 -9
- package/modules/head/drivers/posthtml-head-elements.js +92 -10
- package/modules/head/index.js +16 -9
- package/modules/head/schema.js +7 -3
- package/modules/multilang/filters/i18n-default-translation.js +2 -4
- package/modules/multilang/filters/i18n-translation-in.js +2 -2
- package/modules/multilang/filters/i18n-translations-for.js +2 -2
- package/modules/multilang/index.js +78 -39
- package/modules/navigator/index.js +6 -5
- package/modules/sitemap/index.js +4 -4
- package/modules/sitemap/templates/sitemap-core.html +1 -1
- package/package.json +2 -1
- /package/core/{surface/global-date-function.js → dates/date-global.js} +0 -0
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { I18nPlugin } from '@11ty/eleventy';
|
|
2
2
|
import { DeepCopy } from '@11ty/eleventy-utils';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
normalizeLang,
|
|
5
|
+
normalizeLocale,
|
|
6
|
+
deriveLang,
|
|
7
|
+
resolveDefault
|
|
8
|
+
} from '../../core/locale/index.js';
|
|
9
|
+
import { normalizeLanguageMap } from '../../core/utils/normalize-language-map.js';
|
|
4
10
|
import i18nTranslationsFor from './filters/i18n-translations-for.js';
|
|
5
11
|
import i18nTranslationIn from './filters/i18n-translation-in.js';
|
|
6
12
|
import i18nDefaultTranslation from './filters/i18n-default-translation.js';
|
|
@@ -9,10 +15,10 @@ import i18nDefaultTranslation from './filters/i18n-default-translation.js';
|
|
|
9
15
|
* Multilang (module)
|
|
10
16
|
*
|
|
11
17
|
* Language infrastructure. Normalises language config, builds translation
|
|
12
|
-
* relationships, attaches per-page locale
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* module exits early.
|
|
18
|
+
* relationships, attaches per-page lang / locale / translationKey /
|
|
19
|
+
* isDefaultLang fields, and exposes cross-language lookup filters. Active
|
|
20
|
+
* only when options.multilingual is true and both defaultLanguage and at
|
|
21
|
+
* least one languages entry are set; otherwise the module exits early.
|
|
16
22
|
*
|
|
17
23
|
* Architecture layer:
|
|
18
24
|
* module
|
|
@@ -24,7 +30,8 @@ import i18nDefaultTranslation from './filters/i18n-default-translation.js';
|
|
|
24
30
|
*
|
|
25
31
|
* Lifecycle:
|
|
26
32
|
* build-time → normalise languages, attach I18nPlugin, register filters
|
|
27
|
-
* and computed page.locale
|
|
33
|
+
* and computed page.lang / page.locale / page.translationKey
|
|
34
|
+
* / page.isDefaultLang
|
|
28
35
|
* cascade-time → translationsMap and translations collections build the
|
|
29
36
|
* per-translationKey map and write it to the store
|
|
30
37
|
*
|
|
@@ -35,15 +42,16 @@ import i18nDefaultTranslation from './filters/i18n-default-translation.js';
|
|
|
35
42
|
* lifecycle boundary.
|
|
36
43
|
*
|
|
37
44
|
* Scope:
|
|
38
|
-
* Owns language normalisation, page
|
|
39
|
-
*
|
|
40
|
-
* (i18nTranslationsFor,
|
|
41
|
-
* Does not own URL routing
|
|
45
|
+
* Owns language normalisation, per-page flat locale fields (lang, locale,
|
|
46
|
+
* translationKey, isDefaultLang), the translations and translationsMap
|
|
47
|
+
* collections, and the i18n filters (i18nTranslationsFor,
|
|
48
|
+
* i18nTranslationIn, i18nDefaultTranslation). Does not own URL routing
|
|
49
|
+
* (I18nPlugin) or hreflang rendering (head).
|
|
42
50
|
*
|
|
43
51
|
* Data flow:
|
|
44
|
-
* settings.languages + page.lang/translationKey → normalisation
|
|
45
|
-
* I18nPlugin → collections + computed page
|
|
46
|
-
* store → head, sitemap
|
|
52
|
+
* settings.languages + page.lang/locale/translationKey → normalisation
|
|
53
|
+
* + I18nPlugin → collections + flat computed page fields +
|
|
54
|
+
* translation-map store → head, sitemap
|
|
47
55
|
*
|
|
48
56
|
* @param {import("@11ty/eleventy/src/UserConfig.js").default} eleventyConfig
|
|
49
57
|
* @param {Object} moduleContext
|
|
@@ -52,18 +60,18 @@ export function multilangCore(eleventyConfig, moduleContext) {
|
|
|
52
60
|
const { state, runtime, log } = moduleContext;
|
|
53
61
|
const { settings, options } = state;
|
|
54
62
|
|
|
55
|
-
// ---
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
const defaultLanguage =
|
|
60
|
-
const languages =
|
|
63
|
+
// --- Default resolution ---
|
|
64
|
+
// resolveDefault returns { lang, locale } from settings.defaultLocale (preferred)
|
|
65
|
+
// or settings.defaultLanguage (cosmetic alias; locale derived via Intl.Locale,
|
|
66
|
+
// returning the bare language subtag when no region is given).
|
|
67
|
+
const { lang: defaultLanguage, locale: defaultLocale } = resolveDefault(settings);
|
|
68
|
+
const languages = normalizeLanguageMap(settings, log);
|
|
61
69
|
const hasLanguages = languages && Object.keys(languages).length > 0;
|
|
62
70
|
|
|
63
71
|
const isMultilingual = options.multilang === true && defaultLanguage && hasLanguages;
|
|
64
72
|
|
|
65
73
|
if (!isMultilingual) {
|
|
66
|
-
log.info('Multilang inactive, needs options.multilang, settings.defaultLanguage, and languages');
|
|
74
|
+
log.info('Multilang inactive, needs options.multilang, settings.defaultLanguage or defaultLocale, and languages');
|
|
67
75
|
return;
|
|
68
76
|
}
|
|
69
77
|
|
|
@@ -75,24 +83,50 @@ export function multilangCore(eleventyConfig, moduleContext) {
|
|
|
75
83
|
errorMode: 'allow-fallback'
|
|
76
84
|
});
|
|
77
85
|
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
// --- Per-page resolvers ---
|
|
87
|
+
// Shared between the four flat eleventyComputed registrations below and
|
|
88
|
+
// the buildTranslations collection iterator. Closes over defaults and
|
|
89
|
+
// the languages map.
|
|
90
|
+
//
|
|
91
|
+
// Accept `language` as a writer-side alias for `lang`. Cheap, forgiving,
|
|
92
|
+
// and means existing front matter using either spelling keeps working.
|
|
93
|
+
// Also derives lang from data.locale when neither is set.
|
|
94
|
+
function resolvePageLang(data) {
|
|
95
|
+
return (
|
|
96
|
+
normalizeLang(data.lang || data.language || deriveLang(data.locale)) || defaultLanguage
|
|
97
|
+
);
|
|
98
|
+
}
|
|
85
99
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
100
|
+
function resolvePageLocale(data) {
|
|
101
|
+
if (data.locale) return normalizeLocale(data.locale);
|
|
102
|
+
const lang = resolvePageLang(data);
|
|
103
|
+
return normalizeLocale(languages?.[lang]?.locale) ?? defaultLocale;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// --- Computed per-page fields ---
|
|
107
|
+
// Four independent registrations merge cleanly at the leaves (validated
|
|
108
|
+
// 2026-05-25 via temp/workbench/multilang-glow-up/eleventy-probe/).
|
|
109
|
+
// Replaces the historical single-bag page.locale object with flat
|
|
110
|
+
// siblings on page.
|
|
111
|
+
eleventyConfig.addGlobalData(
|
|
112
|
+
'eleventyComputed.page.lang',
|
|
113
|
+
() => (data) => resolvePageLang(data)
|
|
114
|
+
);
|
|
115
|
+
eleventyConfig.addGlobalData(
|
|
116
|
+
'eleventyComputed.page.locale',
|
|
117
|
+
() => (data) => resolvePageLocale(data)
|
|
118
|
+
);
|
|
119
|
+
eleventyConfig.addGlobalData(
|
|
120
|
+
'eleventyComputed.page.translationKey',
|
|
121
|
+
() => (data) => data.translationKey
|
|
122
|
+
);
|
|
123
|
+
eleventyConfig.addGlobalData(
|
|
124
|
+
'eleventyComputed.page.isDefaultLang',
|
|
125
|
+
() => (data) => resolvePageLang(data) === defaultLanguage
|
|
126
|
+
);
|
|
93
127
|
|
|
94
128
|
// Build a set of allowed language codes for validation during collection building.
|
|
95
|
-
const allowedLanguages = new Set(Object.keys(languages).map(
|
|
129
|
+
const allowedLanguages = new Set(Object.keys(languages).map(normalizeLang));
|
|
96
130
|
|
|
97
131
|
// Build both the map (keyed by translationKey → lang) and the flat list.
|
|
98
132
|
// Shared logic for both collections — called once per collection registration.
|
|
@@ -104,7 +138,7 @@ export function multilangCore(eleventyConfig, moduleContext) {
|
|
|
104
138
|
const translationKey = page.data.translationKey;
|
|
105
139
|
if (!translationKey) continue;
|
|
106
140
|
|
|
107
|
-
const lang = page.data
|
|
141
|
+
const lang = resolvePageLang(page.data);
|
|
108
142
|
if (!lang) continue;
|
|
109
143
|
|
|
110
144
|
if (allowedLanguages.size && !allowedLanguages.has(lang)) {
|
|
@@ -112,8 +146,13 @@ export function multilangCore(eleventyConfig, moduleContext) {
|
|
|
112
146
|
continue;
|
|
113
147
|
}
|
|
114
148
|
|
|
115
|
-
const
|
|
116
|
-
const
|
|
149
|
+
const isDefaultLang = lang === defaultLanguage;
|
|
150
|
+
const locale = resolvePageLocale(page.data);
|
|
151
|
+
|
|
152
|
+
// Attach flat per-page fields. Mirrors the eleventyComputed shape
|
|
153
|
+
// so collection consumers read item.lang / item.locale /
|
|
154
|
+
// item.translationKey / item.isDefaultLang directly.
|
|
155
|
+
const safeCopy = DeepCopy(page, { lang, locale, translationKey, isDefaultLang });
|
|
117
156
|
list.push(safeCopy);
|
|
118
157
|
|
|
119
158
|
if (!map[translationKey]) map[translationKey] = {};
|
|
@@ -121,7 +160,7 @@ export function multilangCore(eleventyConfig, moduleContext) {
|
|
|
121
160
|
title: page.data.title,
|
|
122
161
|
url: page.url,
|
|
123
162
|
lang,
|
|
124
|
-
isDefaultLang
|
|
163
|
+
isDefaultLang,
|
|
125
164
|
data: page.data
|
|
126
165
|
};
|
|
127
166
|
}
|
|
@@ -26,8 +26,8 @@ const __dirname = path.dirname(__filename);
|
|
|
26
26
|
* Lifecycle:
|
|
27
27
|
* build-time → register `_navigator` ({ nodes, edges, backlinks }),
|
|
28
28
|
* debug globals, filters, and the optional virtual debug page
|
|
29
|
-
* cascade-time → eleventyComputed `_snapshot` resolves contentMap
|
|
30
|
-
* pageContext on each page
|
|
29
|
+
* cascade-time → eleventyComputed `_snapshot` resolves contentMap,
|
|
30
|
+
* pageContext, and seoGraph on each page
|
|
31
31
|
*
|
|
32
32
|
* Why this exists:
|
|
33
33
|
* Templates need an addressable cross-page surface for graph reads, and
|
|
@@ -55,11 +55,11 @@ const __dirname = path.dirname(__filename);
|
|
|
55
55
|
* @param {Object} moduleContext
|
|
56
56
|
* @param {Object} moduleContext.state - Resolved plugin state.
|
|
57
57
|
* @param {Object} moduleContext.runtime - Lazy access layer; reads contentGraph.
|
|
58
|
-
* @param {Object} moduleContext.snapshots - Thunks: { contentMap, pageContext }.
|
|
58
|
+
* @param {Object} moduleContext.snapshots - Thunks: { contentMap, pageContext, seoGraph }.
|
|
59
59
|
*/
|
|
60
60
|
export function navigatorCore(eleventyConfig, moduleContext) {
|
|
61
61
|
const { state, runtime, snapshots, log, env } = moduleContext;
|
|
62
|
-
const {
|
|
62
|
+
const { options } = state;
|
|
63
63
|
|
|
64
64
|
// Structural-only options check: log on mismatch, do not throw.
|
|
65
65
|
const parsed = optionsSchema.safeParse(options.navigator);
|
|
@@ -78,7 +78,8 @@ export function navigatorCore(eleventyConfig, moduleContext) {
|
|
|
78
78
|
eleventyConfig.addGlobalData('eleventyComputed._snapshot', () => {
|
|
79
79
|
return () => ({
|
|
80
80
|
contentMap: snapshots.contentMap(),
|
|
81
|
-
pageContext: snapshots.pageContext()
|
|
81
|
+
pageContext: snapshots.pageContext(),
|
|
82
|
+
seoGraph: snapshots.seoGraph()
|
|
82
83
|
});
|
|
83
84
|
});
|
|
84
85
|
|
package/modules/sitemap/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import {
|
|
4
|
+
import { normalizeLanguageMap } from '../../core/utils/normalize-language-map.js';
|
|
5
5
|
|
|
6
6
|
const __filename = fileURLToPath(import.meta.url);
|
|
7
7
|
const __dirname = path.dirname(__filename);
|
|
@@ -19,7 +19,7 @@ const __dirname = path.dirname(__filename);
|
|
|
19
19
|
*
|
|
20
20
|
* System role:
|
|
21
21
|
* Reads the same normalised language map as multilang (via
|
|
22
|
-
* core/utils/normalize-
|
|
22
|
+
* core/utils/normalize-language-map.js) and emits virtual templates that Eleventy
|
|
23
23
|
* renders to XML. Pages opt out via `noindex` in the cascade.
|
|
24
24
|
*
|
|
25
25
|
* Lifecycle:
|
|
@@ -37,7 +37,7 @@ const __dirname = path.dirname(__filename);
|
|
|
37
37
|
* Owns computed page.sitemap and the virtual sitemap templates
|
|
38
38
|
* (single-language /sitemap.xml, or per-lang /{lang}/sitemap.xml plus a
|
|
39
39
|
* /sitemap.xml index).
|
|
40
|
-
* Does not own language normalisation (core/utils/normalize-
|
|
40
|
+
* Does not own language normalisation (core/utils/normalize-language-map.js) or noindex
|
|
41
41
|
* propagation through the cascade.
|
|
42
42
|
*
|
|
43
43
|
* Data flow:
|
|
@@ -56,7 +56,7 @@ export function sitemapCore(eleventyConfig, moduleContext) {
|
|
|
56
56
|
// Drives collection building, locale data, and sitemap-core language config.
|
|
57
57
|
const normalizeLanguageCode = (lang) => (lang || '').toLowerCase().trim();
|
|
58
58
|
const defaultLanguage = normalizeLanguageCode(settings.defaultLanguage);
|
|
59
|
-
const languages =
|
|
59
|
+
const languages = normalizeLanguageMap(settings, log);
|
|
60
60
|
const hasLanguages = languages && Object.keys(languages).length > 0;
|
|
61
61
|
const isMultilingual = options.multilang === true && defaultLanguage && hasLanguages;
|
|
62
62
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
|
3
3
|
{%- if not settings.noindex %}
|
|
4
4
|
{%- for item in collections.all %}
|
|
5
|
-
{%- if not item.data.eleventyExcludeFromCollections and (not item.data.sitemap or item.data.sitemap.ignore != true) and item.data.noindex != true %}
|
|
5
|
+
{%- if item.url and not item.data.eleventyExcludeFromCollections and (not item.data.sitemap or item.data.sitemap.ignore != true) and item.data.noindex != true %}
|
|
6
6
|
{%- set pageLang = item.data.lang or settings.defaultLanguage %}
|
|
7
7
|
{%- if (not isMultilingual) or (not sitemapLang) or (pageLang == sitemapLang) %}
|
|
8
8
|
{%- set absoluteUrl = item.url | htmlBaseUrl %}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apleasantview/eleventy-plugin-baseline",
|
|
3
|
-
"version": "0.1.0-next.
|
|
3
|
+
"version": "0.1.0-next.42",
|
|
4
4
|
"description": "An experimental Swiss army knife toolkit for Eleventy",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@11ty/eleventy-utils": "^2.0.7",
|
|
44
|
+
"@jdevalk/seo-graph-core": "^0.6.2",
|
|
44
45
|
"@rviscomi/capo.js": "^2.1.0",
|
|
45
46
|
"cssnano": "^7.1.2",
|
|
46
47
|
"dotenv": "^17.2.3",
|
|
File without changes
|