@apleasantview/eleventy-plugin-baseline 0.1.0-next.40 → 0.1.0-next.41
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 +21 -23
- package/core/back-compat/options.js +69 -0
- package/core/content-graph/backlinks.js +65 -0
- package/core/content-graph/extractors.js +140 -0
- package/core/content-graph/graph.js +118 -0
- package/core/content-graph/index.js +2 -0
- package/core/content-graph/prepass.js +121 -0
- package/core/logging/banner.js +49 -0
- package/core/{logging.js → logging/index.js} +19 -2
- package/core/logging/quips.js +30 -0
- package/core/markdown/auto-heading-ids.js +86 -0
- package/core/markdown/index.js +5 -0
- package/core/markdown/safe-use.js +42 -0
- package/core/{wikilinks.js → markdown/wikilinks.js} +3 -3
- package/core/page-context/build.js +239 -0
- package/core/page-context/index.js +1 -0
- package/core/page-context/register.js +73 -0
- package/core/page-context/seo-helpers.js +56 -0
- package/core/schema.js +19 -1
- package/core/slug-index.js +2 -2
- package/core/state.js +73 -0
- package/core/{shortcodes/image.js → surface/image-shortcode.js} +4 -4
- package/core/surface/index.js +22 -0
- package/core/utils/add-trailing-slash.js +11 -0
- package/core/utils/ensure-dot-slash-dir.js +13 -0
- package/core/utils/normalize-languages.js +28 -0
- package/core/utils/resolve-field.js +9 -0
- package/core/utils/resolve-subdir.js +20 -0
- package/core/utils/slugify.js +15 -0
- package/core/utils/unique-by.js +25 -0
- package/core/virtual-dir.js +11 -10
- package/index.js +152 -115
- package/modules/assets/index.js +4 -2
- package/modules/assets/processors/esbuild-process.js +2 -2
- package/modules/assets/processors/postcss-process.js +2 -2
- package/modules/head/drivers/posthtml-head-elements.js +1 -3
- package/modules/head/index.js +7 -10
- package/modules/multilang/index.js +4 -2
- package/modules/navigator/index.js +33 -20
- package/modules/navigator/templates/navigator-core.html +1 -1
- package/modules/sitemap/index.js +7 -3
- package/package.json +4 -2
- package/core/filters/index.js +0 -4
- package/core/global-functions/index.js +0 -6
- package/core/page-context.js +0 -310
- package/core/shortcodes/index.js +0 -2
- package/core/utils/helpers.js +0 -75
- /package/core/{filters/markdown.js → markdown/markdownify.js} +0 -0
- /package/core/{filters → surface/filters}/isString.js +0 -0
- /package/core/{filters → surface/filters}/related-posts.js +0 -0
- /package/core/{global-functions/date.js → surface/global-date-function.js} +0 -0
package/core/state.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State derivation (composition root helper)
|
|
3
|
+
*
|
|
4
|
+
* Pure normalisation of user-supplied `settings` and `options` into the
|
|
5
|
+
* resolved `state` shape modules read from. No eleventyConfig, no
|
|
6
|
+
* environment reads beyond the `mode` argument, no side effects.
|
|
7
|
+
*
|
|
8
|
+
* Architecture layer:
|
|
9
|
+
* composition root (pure helper)
|
|
10
|
+
*
|
|
11
|
+
* System role:
|
|
12
|
+
* The single place that applies defaults, fallbacks, and feature
|
|
13
|
+
* inference. Extracted from the entry point so it can be reasoned
|
|
14
|
+
* about — and tested — without booting Eleventy.
|
|
15
|
+
*
|
|
16
|
+
* Why this exists:
|
|
17
|
+
* Keeping defaults and feature derivation tangled with eleventyConfig
|
|
18
|
+
* wiring made the entry point hard to scan. Pulling the pure half out
|
|
19
|
+
* leaves the composition root as a list of registration steps.
|
|
20
|
+
*
|
|
21
|
+
* Scope:
|
|
22
|
+
* Owns settings/options normalisation and the derived `features` map.
|
|
23
|
+
* Does not own validation (see core/schema.js) or any runtime wiring.
|
|
24
|
+
*
|
|
25
|
+
* Data flow:
|
|
26
|
+
* settings + options + { mode } → { settings, options, features }
|
|
27
|
+
*
|
|
28
|
+
* @param {import('./types.js').BaselineSettings} settings
|
|
29
|
+
* @param {import('./types.js').BaselineOptions} options
|
|
30
|
+
* @param {{ mode?: string }} [env]
|
|
31
|
+
* @returns {import('./types.js').BaselineState & { features: Readonly<Record<string, boolean>> }}
|
|
32
|
+
*/
|
|
33
|
+
export function deriveBaselineState(settings, options, { mode } = {}) {
|
|
34
|
+
const isDev = mode === 'development';
|
|
35
|
+
|
|
36
|
+
const resolvedSettings = {
|
|
37
|
+
title: settings.title,
|
|
38
|
+
tagline: settings.tagline,
|
|
39
|
+
url: settings.url,
|
|
40
|
+
noindex: settings.noindex ?? false,
|
|
41
|
+
defaultLanguage: settings.defaultLanguage,
|
|
42
|
+
languages: settings.languages,
|
|
43
|
+
head: settings.head
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const resolvedOptions = {
|
|
47
|
+
verbose: options.verbose ?? true,
|
|
48
|
+
multilang: options.multilingual ?? false,
|
|
49
|
+
sitemap: options.sitemap ?? options.enableSitemapTemplate ?? true,
|
|
50
|
+
navigator: options.navigator ?? options.enableNavigatorTemplate ?? isDev,
|
|
51
|
+
head: {
|
|
52
|
+
titleSeparator: options.head?.titleSeparator,
|
|
53
|
+
showGenerator: options.head?.showGenerator
|
|
54
|
+
},
|
|
55
|
+
assets: {
|
|
56
|
+
esbuild: options.assets?.esbuild ?? options.assetsESBuild ?? {}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const features = Object.freeze({
|
|
61
|
+
multilang: Boolean(resolvedOptions.multilang),
|
|
62
|
+
sitemap: Boolean(resolvedOptions.sitemap),
|
|
63
|
+
navigator: Boolean(resolvedOptions.navigator),
|
|
64
|
+
head: true,
|
|
65
|
+
assets: true
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return Object.freeze({
|
|
69
|
+
settings: Object.freeze(resolvedSettings),
|
|
70
|
+
options: Object.freeze(resolvedOptions),
|
|
71
|
+
features
|
|
72
|
+
});
|
|
73
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import Image from '@11ty/eleventy-img';
|
|
3
|
-
import { createLogger } from '../logging.js';
|
|
3
|
+
import { createLogger } from '../logging/index.js';
|
|
4
4
|
|
|
5
5
|
// Module-level logger. Image shortcode only uses `.warn`, which emits regardless
|
|
6
6
|
// of verbose, so we don't thread verbose through the shortcode signature.
|
|
7
|
-
const log = createLogger('image');
|
|
7
|
+
const log = createLogger('image-shortcode');
|
|
8
8
|
|
|
9
9
|
const DEFAULT_WIDTHS = [320, 640, 960, 1280, 1920, 'auto'];
|
|
10
10
|
const DEFAULT_FORMATS = ['avif', 'webp'];
|
|
@@ -68,7 +68,7 @@ export async function imageShortcode(options = {}) {
|
|
|
68
68
|
|
|
69
69
|
// --- Validation and normalization ---
|
|
70
70
|
|
|
71
|
-
if (!src) throw new Error(`
|
|
71
|
+
if (!src) throw new Error(`[baseline/image-shortcode] src is required (received ${JSON.stringify(src)})`);
|
|
72
72
|
if (alt == null) {
|
|
73
73
|
log.warn('alt is required (use empty string for decorative images)');
|
|
74
74
|
}
|
|
@@ -106,7 +106,7 @@ export async function imageShortcode(options = {}) {
|
|
|
106
106
|
});
|
|
107
107
|
} catch (error) {
|
|
108
108
|
if (process.env.ELEVENTY_RUN_MODE === 'serve') {
|
|
109
|
-
log.warn(`transformOnRequest failed for ${src}, retrying
|
|
109
|
+
log.warn(`transformOnRequest failed for ${src}, retrying. ${error?.message || error}`);
|
|
110
110
|
metadata = await Image(resolvedSrc, imageOptions);
|
|
111
111
|
} else {
|
|
112
112
|
throw error;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Surface barrel
|
|
3
|
+
*
|
|
4
|
+
* Single entry point for everything Baseline registers against Eleventy that
|
|
5
|
+
* user templates can reach: filters, global functions, shortcodes.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { registerDateGlobal } from './global-date-function.js';
|
|
9
|
+
|
|
10
|
+
// --- Filters ---
|
|
11
|
+
export { markdownFilter } from '../markdown/markdownify.js';
|
|
12
|
+
export { relatedPostsFilter } from './filters/related-posts.js';
|
|
13
|
+
export { isStringFilter } from './filters/isString.js';
|
|
14
|
+
|
|
15
|
+
// --- Shortcodes ---
|
|
16
|
+
export { imageShortcode } from './image-shortcode.js';
|
|
17
|
+
|
|
18
|
+
// --- Global functions (aggregator) ---
|
|
19
|
+
/** @param {import("@11ty/eleventy").UserConfig} eleventyConfig */
|
|
20
|
+
export function registerGlobals(eleventyConfig) {
|
|
21
|
+
registerDateGlobal(eleventyConfig);
|
|
22
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { TemplatePath } from '@11ty/eleventy-utils';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Normalise a directory path to a `./`-prefixed form, defaulting empty/missing
|
|
5
|
+
* input to the current directory. Thin wrapper over
|
|
6
|
+
* `TemplatePath.addLeadingDotSlash` that bakes in the empty-string fallback.
|
|
7
|
+
*
|
|
8
|
+
* @param {string | undefined} dir
|
|
9
|
+
* @returns {string}
|
|
10
|
+
*/
|
|
11
|
+
export function ensureDotSlashDir(dir) {
|
|
12
|
+
return TemplatePath.addLeadingDotSlash(dir || './');
|
|
13
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize language input to an object map.
|
|
3
|
+
* Accepts an array of language codes or an object keyed by language code.
|
|
4
|
+
* Returns undefined if input is invalid or empty.
|
|
5
|
+
*
|
|
6
|
+
* @param {Object} settings - Options object containing languages.
|
|
7
|
+
* @param {import('../logging/index.js').BaselineLogger} [logger] - Logger for dropped-entry notice.
|
|
8
|
+
* @returns {Record<string, Object>|undefined} Normalized language map, or undefined.
|
|
9
|
+
*/
|
|
10
|
+
export function normalizeLanguages(settings, logger) {
|
|
11
|
+
const normalizedLanguages = Array.isArray(settings.languages)
|
|
12
|
+
? Object.fromEntries(
|
|
13
|
+
settings.languages
|
|
14
|
+
.filter((lang) => typeof lang === 'string' && lang.trim())
|
|
15
|
+
.map((lang) => [lang.toLowerCase().trim(), {}])
|
|
16
|
+
)
|
|
17
|
+
: settings.languages && typeof settings.languages === 'object'
|
|
18
|
+
? settings.languages
|
|
19
|
+
: undefined;
|
|
20
|
+
|
|
21
|
+
if (logger && Array.isArray(settings.languages)) {
|
|
22
|
+
const normalizedCount = normalizedLanguages ? Object.keys(normalizedLanguages).length : 0;
|
|
23
|
+
if (normalizedCount !== settings.languages.length) {
|
|
24
|
+
logger.info('Some languages entries were invalid and were dropped.');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return normalizedLanguages;
|
|
28
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a field with a page → site → fallback precedence chain.
|
|
3
|
+
*
|
|
4
|
+
* @param {{ pageValue?: any, siteValue?: any, fallbackValue?: any, isHome?: boolean }} args
|
|
5
|
+
* @returns {any}
|
|
6
|
+
*/
|
|
7
|
+
export function resolveField({ pageValue, siteValue, fallbackValue }) {
|
|
8
|
+
return pageValue ?? siteValue ?? fallbackValue;
|
|
9
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { TemplatePath } from '@11ty/eleventy-utils';
|
|
2
|
+
import { addTrailingSlash } from './add-trailing-slash.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolve a subdirectory under input and output.
|
|
6
|
+
* Joins inputDir/outputDir with rawDir, normalises, and adds trailing slashes.
|
|
7
|
+
* @param {string} inputDir - The input directory (e.g., "./src/").
|
|
8
|
+
* @param {string} outputDir - The output directory (e.g., "./dist/").
|
|
9
|
+
* @param {string} rawDir - Raw subdirectory value (e.g., "assets", "static").
|
|
10
|
+
* @returns {{input: string, output: string}}
|
|
11
|
+
*/
|
|
12
|
+
export function resolveSubdir(inputDir, outputDir, rawDir) {
|
|
13
|
+
const joinedInput = TemplatePath.join(inputDir, rawDir || '');
|
|
14
|
+
const joinedOutput = TemplatePath.join(outputDir, rawDir || '');
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
input: addTrailingSlash(TemplatePath.standardizeFilePath(joinedInput)),
|
|
18
|
+
output: addTrailingSlash(TemplatePath.standardizeFilePath(joinedOutput))
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import slugifyLib from 'slugify';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Slugify a string into a wikilink-friendly key.
|
|
5
|
+
* Lowercases, strips diacritics, replaces non-alphanumerics with hyphens,
|
|
6
|
+
* trims leading/trailing hyphens. Returns undefined for empty input.
|
|
7
|
+
*
|
|
8
|
+
* @param {string|null|undefined} input
|
|
9
|
+
* @returns {string|undefined}
|
|
10
|
+
*/
|
|
11
|
+
export function slugify(input) {
|
|
12
|
+
if (input == null) return;
|
|
13
|
+
const slug = slugifyLib(String(input), { lower: true, strict: true, trim: true });
|
|
14
|
+
return slug || undefined;
|
|
15
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deduplicate an array by a key (string property name or selector function).
|
|
3
|
+
* Items without a derivable key are kept via their JSON-stringified shape.
|
|
4
|
+
*
|
|
5
|
+
* @template T
|
|
6
|
+
* @param {T[]} arr
|
|
7
|
+
* @param {string | ((item: T) => string | undefined)} keyFn
|
|
8
|
+
* @returns {T[]}
|
|
9
|
+
*/
|
|
10
|
+
export const uniqueBy = (arr, keyFn) =>
|
|
11
|
+
Object.values(
|
|
12
|
+
(arr ?? []).reduce((acc, item) => {
|
|
13
|
+
if (!item) return acc;
|
|
14
|
+
|
|
15
|
+
const id = typeof keyFn === 'function' ? keyFn(item) : item?.[keyFn];
|
|
16
|
+
|
|
17
|
+
if (!id) {
|
|
18
|
+
acc[JSON.stringify(item)] = item;
|
|
19
|
+
return acc;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
acc[id] = item;
|
|
23
|
+
return acc;
|
|
24
|
+
}, {})
|
|
25
|
+
);
|
package/core/virtual-dir.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { resolveSubdir } from './utils/
|
|
3
|
-
import { createLogger } from './logging.js';
|
|
1
|
+
import { ensureDotSlashDir } from './utils/ensure-dot-slash-dir.js';
|
|
2
|
+
import { resolveSubdir } from './utils/resolve-subdir.js';
|
|
3
|
+
import { createLogger } from './logging/index.js';
|
|
4
4
|
import { getScope, addScopeListener, setEntry } from './registry.js';
|
|
5
5
|
|
|
6
|
+
const SCOPE_NAME = 'core:virtual-dir';
|
|
7
|
+
const LOG_NAME = 'virtual-dir';
|
|
8
|
+
|
|
6
9
|
/**
|
|
7
10
|
* Virtual directories (runtime substrate)
|
|
8
11
|
*
|
|
@@ -40,8 +43,6 @@ import { getScope, addScopeListener, setEntry } from './registry.js';
|
|
|
40
43
|
* live { input, output } cache → consumers
|
|
41
44
|
*/
|
|
42
45
|
|
|
43
|
-
const SCOPE_NAME = 'core:virtual-dir';
|
|
44
|
-
|
|
45
46
|
/**
|
|
46
47
|
* Register a virtual directory on eleventyConfig.directories.
|
|
47
48
|
*
|
|
@@ -56,10 +57,10 @@ const SCOPE_NAME = 'core:virtual-dir';
|
|
|
56
57
|
*/
|
|
57
58
|
export function registerVirtualDir(eleventyConfig, { key, outputDir } = {}) {
|
|
58
59
|
if (!key) {
|
|
59
|
-
throw new Error('
|
|
60
|
+
throw new Error('[baseline/virtual-dir] `name` is required');
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
const log = createLogger(
|
|
63
|
+
const log = createLogger(LOG_NAME);
|
|
63
64
|
const scope = getScope(eleventyConfig, SCOPE_NAME);
|
|
64
65
|
const rawDir = eleventyConfig.dir?.[key] || key;
|
|
65
66
|
const rawOutputDir = outputDir ?? rawDir;
|
|
@@ -75,7 +76,7 @@ export function registerVirtualDir(eleventyConfig, { key, outputDir } = {}) {
|
|
|
75
76
|
// shared listener below refreshes when Eleventy emits its final directories.
|
|
76
77
|
const existing = Object.getOwnPropertyDescriptor(eleventyConfig.directories, key);
|
|
77
78
|
if (existing && existing.configurable === false) {
|
|
78
|
-
log.info(`directories
|
|
79
|
+
log.info(`directories.${key} already defined, skipping`);
|
|
79
80
|
} else {
|
|
80
81
|
Object.defineProperty(eleventyConfig.directories, key, {
|
|
81
82
|
get() {
|
|
@@ -101,8 +102,8 @@ export function registerVirtualDir(eleventyConfig, { key, outputDir } = {}) {
|
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
function syncCache(cache, dirs, rawDir, rawOutputDir) {
|
|
104
|
-
const inputDir =
|
|
105
|
-
const outputDir =
|
|
105
|
+
const inputDir = ensureDotSlashDir(dirs.input);
|
|
106
|
+
const outputDir = ensureDotSlashDir(dirs.output);
|
|
106
107
|
|
|
107
108
|
// resolveSubdir symmetrically resolves against input and output; call twice
|
|
108
109
|
// so input and output subdirs can differ (e.g. `public` copies to root).
|