@apleasantview/eleventy-plugin-baseline 0.1.0-next.39 → 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/index.js +80 -0
- 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} +6 -6
- 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 +52 -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/types.js +1 -1
- 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 +35 -2
- package/modules/assets/processors/postcss-process.js +36 -2
- package/modules/head/drivers/capo-adapter.js +26 -4
- package/modules/head/drivers/posthtml-head-elements.js +2 -4
- 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/logging.js +0 -32
- 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/schema.js
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
import * as z from 'zod';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Schemas (runtime substrate)
|
|
5
|
+
*
|
|
6
|
+
* Zod schemas for the two user-facing inputs Baseline validates: the
|
|
7
|
+
* directory `config` export and the `settings` argument. Structural only.
|
|
8
|
+
* Value-level preferences stay permissive.
|
|
9
|
+
*
|
|
10
|
+
* Architecture layer:
|
|
11
|
+
* runtime substrate
|
|
12
|
+
*
|
|
13
|
+
* System role:
|
|
14
|
+
* Validation seam at the public boundary. The composition root parses
|
|
15
|
+
* `settings` non-fatally at init; the directory `config` is checked in
|
|
16
|
+
* the test suite, not at runtime.
|
|
17
|
+
*
|
|
18
|
+
* Lifecycle:
|
|
19
|
+
* build-time → composition root calls `settingsSchema.safeParse(settings)`
|
|
20
|
+
* and logs structural mismatches under `info`
|
|
21
|
+
*
|
|
22
|
+
* Why this exists:
|
|
23
|
+
* Eleventy accepts almost anything users pass through `addPlugin`. A
|
|
24
|
+
* structural gate catches typos and shape drift early without forcing
|
|
25
|
+
* a hard failure on imperfect input.
|
|
26
|
+
*
|
|
27
|
+
* Scope:
|
|
28
|
+
* Owns the structural shape of `settings` and `config`. Does not own
|
|
29
|
+
* defaults, value semantics, or required-field policy; those live in
|
|
30
|
+
* the composition root and individual modules.
|
|
31
|
+
*
|
|
32
|
+
* Data flow:
|
|
33
|
+
* user input → safeParse → issues logged or accepted
|
|
34
|
+
*/
|
|
35
|
+
|
|
3
36
|
export const configSchema = z.object({
|
|
4
37
|
dir: z.object({
|
|
5
38
|
input: z.string().min(1),
|
|
@@ -25,7 +58,25 @@ export const settingsSchema = z.object({
|
|
|
25
58
|
url: z.string().optional(),
|
|
26
59
|
noindex: z.boolean().optional(),
|
|
27
60
|
defaultLanguage: z.string().optional(),
|
|
28
|
-
languages: z
|
|
61
|
+
languages: z
|
|
62
|
+
.unknown()
|
|
63
|
+
.optional()
|
|
64
|
+
.superRefine((value, ctx) => {
|
|
65
|
+
if (value === undefined) return;
|
|
66
|
+
|
|
67
|
+
if (Array.isArray(value)) {
|
|
68
|
+
const arrayResult = z.array(z.string().min(1)).safeParse(value);
|
|
69
|
+
if (!arrayResult.success) {
|
|
70
|
+
for (const issue of arrayResult.error.issues) ctx.addIssue(issue);
|
|
71
|
+
}
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const recordResult = z.record(z.string(), z.looseObject({})).safeParse(value);
|
|
76
|
+
if (!recordResult.success) {
|
|
77
|
+
for (const issue of recordResult.error.issues) ctx.addIssue(issue);
|
|
78
|
+
}
|
|
79
|
+
}),
|
|
29
80
|
head: z
|
|
30
81
|
.object({
|
|
31
82
|
link: z.array(z.looseObject({})).optional(),
|
package/core/slug-index.js
CHANGED
|
@@ -35,7 +35,7 @@ const SCOPE_NAME = 'core:slug-index';
|
|
|
35
35
|
* page-context.buildPageContext → set() → registry scope → wikilinks getBySlug()
|
|
36
36
|
*
|
|
37
37
|
* @param {import('@11ty/eleventy').UserConfig} eleventyConfig
|
|
38
|
-
* @returns {{set: (slug: string, url: string, inputPath?: string) => void, getBySlug: (slug: string) => string |
|
|
38
|
+
* @returns {{set: (slug: string, url: string, inputPath?: string) => void, getBySlug: (slug: string) => string | undefined, snapshot: () => Record<string, {url: string, inputPath?: string}>}}
|
|
39
39
|
*/
|
|
40
40
|
export function createSlugIndex(eleventyConfig) {
|
|
41
41
|
const scope = getScope(eleventyConfig, SCOPE_NAME);
|
|
@@ -52,7 +52,7 @@ export function createSlugIndex(eleventyConfig) {
|
|
|
52
52
|
setEntry(scope, slug, { url, inputPath });
|
|
53
53
|
},
|
|
54
54
|
getBySlug(slug) {
|
|
55
|
-
return getEntry(scope, slug)?.url
|
|
55
|
+
return getEntry(scope, slug)?.url;
|
|
56
56
|
},
|
|
57
57
|
snapshot() {
|
|
58
58
|
return Object.fromEntries(scope.values);
|
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
|
+
}
|
package/core/types.js
CHANGED
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
* Head module options.
|
|
44
44
|
*
|
|
45
45
|
* @property {{ esbuild?: { minify?: boolean, target?: string } }} [assets]
|
|
46
|
-
* Assets module options. The esbuild slice is permissive
|
|
46
|
+
* Assets module options. The esbuild slice is permissive: any esbuild
|
|
47
47
|
* option is accepted; only `minify` and `target` are typed.
|
|
48
48
|
*/
|
|
49
49
|
|
|
@@ -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).
|