@apleasantview/eleventy-plugin-baseline 0.1.0-next.29 → 0.1.0-next.33
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 +33 -4
- package/core/debug.js +18 -3
- package/core/filters/isString.js +5 -0
- package/core/filters/markdown.js +6 -0
- package/core/filters/related-posts.js +6 -0
- package/core/helpers.js +6 -97
- package/core/logging.js +2 -2
- package/core/modules.js +0 -4
- package/core/shortcodes/image.js +162 -128
- package/eleventy.config.js +34 -22
- package/modules/assets-core/plugins/assets-core.js +126 -13
- package/modules/assets-esbuild/{filters/inline-esbuild.js → process.js} +10 -1
- package/modules/assets-postcss/fallback/postcss.config.js +1 -1
- package/modules/assets-postcss/process.js +49 -0
- package/modules/head-core/drivers/posthtml-head-elements.js +13 -18
- package/modules/head-core/plugins/head-core.js +19 -1
- package/modules/head-core/utils/head-utils.js +76 -10
- package/modules/multilang-core/plugins/multilang-core.js +26 -9
- package/modules/navigator-core/plugins/navigator-core.js +19 -1
- package/modules/navigator-core/templates/navigator-core.html +4 -4
- package/modules/sitemap-core/plugins/sitemap-core.js +23 -0
- package/modules/sitemap-core/templates/sitemap-core.html +27 -18
- package/modules/sitemap-core/templates/sitemap-index.html +1 -1
- package/package.json +1 -1
- package/modules/assets-esbuild/plugins/assets-esbuild.js +0 -71
- package/modules/assets-postcss/filters/inline-postcss.js +0 -38
- package/modules/assets-postcss/plugins/assets-postcss.js +0 -75
package/eleventy.config.js
CHANGED
|
@@ -30,12 +30,14 @@ export default function baseline(options = {}) {
|
|
|
30
30
|
/** @param {import("@11ty/eleventy").UserConfig} eleventyConfig */
|
|
31
31
|
const plugin = async function (eleventyConfig) {
|
|
32
32
|
try {
|
|
33
|
-
// Emit a warning message if the application is not using Eleventy 3.0 or newer (including prereleases).
|
|
34
33
|
eleventyConfig.versionCheck('>=3.0');
|
|
35
34
|
} catch (e) {
|
|
36
35
|
console.log(`[eleventy-plugin-baseline] WARN Eleventy plugin compatibility: ${e.message}`);
|
|
37
36
|
}
|
|
38
37
|
|
|
38
|
+
// --- Options ---
|
|
39
|
+
// Merge user options with defaults, detect environment capabilities,
|
|
40
|
+
// and expose everything as _baseline global data for templates.
|
|
39
41
|
const hasImageTransformPlugin = eleventyConfig.hasPlugin('eleventyImageTransformPlugin');
|
|
40
42
|
|
|
41
43
|
const userOptions = {
|
|
@@ -47,15 +49,15 @@ export default function baseline(options = {}) {
|
|
|
47
49
|
enableSitemapTemplate: options.enableSitemapTemplate ?? true,
|
|
48
50
|
filterAllCollection: options.filterAllCollection ?? true,
|
|
49
51
|
assets: {
|
|
50
|
-
esbuild: options.assetsESBuild ?? {
|
|
52
|
+
esbuild: options.assetsESBuild ?? {}
|
|
51
53
|
},
|
|
52
54
|
multilingual: options.multilingual ?? false,
|
|
53
55
|
...options
|
|
54
56
|
};
|
|
55
57
|
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
//
|
|
58
|
+
// --- Language normalization ---
|
|
59
|
+
// Accept languages as array or object; normalize to object map.
|
|
60
|
+
// Drives multilang-core registration and sitemap-core language config.
|
|
59
61
|
const normalizedLanguages = Array.isArray(userOptions.languages)
|
|
60
62
|
? Object.fromEntries(
|
|
61
63
|
userOptions.languages
|
|
@@ -78,11 +80,14 @@ export default function baseline(options = {}) {
|
|
|
78
80
|
const hasLanguages = languages && Object.keys(languages).length > 0;
|
|
79
81
|
const isMultilingual = userOptions.multilingual === true && userOptions.defaultLanguage && hasLanguages;
|
|
80
82
|
|
|
83
|
+
// --- Core setup ---
|
|
84
|
+
// Global data, globals registration, static passthrough, drafts preprocessor.
|
|
81
85
|
eleventyConfig.addGlobalData('_baseline', userOptions);
|
|
82
86
|
globals(eleventyConfig);
|
|
83
87
|
eleventyConfig.addPassthroughCopy({ './src/static': '/' });
|
|
84
88
|
|
|
85
|
-
//
|
|
89
|
+
// Drafts preprocessor — skip draft pages during production builds.
|
|
90
|
+
// Guarded against double-registration; user config wins if already set.
|
|
86
91
|
if (!eleventyConfig.preprocessors.drafts) {
|
|
87
92
|
eleventyConfig.addPreprocessor('drafts', '*', (data) => {
|
|
88
93
|
if (data.draft && process.env.ELEVENTY_RUN_MODE === 'build') {
|
|
@@ -91,6 +96,10 @@ export default function baseline(options = {}) {
|
|
|
91
96
|
});
|
|
92
97
|
}
|
|
93
98
|
|
|
99
|
+
// --- Modules ---
|
|
100
|
+
// Registration order matters: multilang first (sets up locale data),
|
|
101
|
+
// then assets, head, sitemap. Navigator is last (debug only).
|
|
102
|
+
|
|
94
103
|
if (isMultilingual) {
|
|
95
104
|
eleventyConfig.addPlugin(modules.multilangCore, {
|
|
96
105
|
defaultLanguage: userOptions.defaultLanguage,
|
|
@@ -98,20 +107,10 @@ export default function baseline(options = {}) {
|
|
|
98
107
|
});
|
|
99
108
|
}
|
|
100
109
|
|
|
101
|
-
// Modules.
|
|
102
110
|
eleventyConfig.addPlugin(modules.EleventyHtmlBasePlugin, {
|
|
103
111
|
baseHref: process.env.URL || eleventyConfig.pathPrefix
|
|
104
112
|
});
|
|
105
|
-
eleventyConfig.addPlugin(modules.assetsCore);
|
|
106
|
-
eleventyConfig.addPlugin(modules.assetsPostCSS);
|
|
107
|
-
eleventyConfig.addPlugin(modules.assetsESBuild, userOptions.assets.esbuild);
|
|
108
|
-
|
|
109
|
-
if (userOptions.filterAllCollection) {
|
|
110
|
-
// Override the default collection behavior. Adding js as template format collects 11tydata.js files.
|
|
111
|
-
eleventyConfig.addCollection('all', (collectionApi) =>
|
|
112
|
-
collectionApi.getAll().filter((item) => !item.inputPath.endsWith('11tydata.js'))
|
|
113
|
-
);
|
|
114
|
-
}
|
|
113
|
+
eleventyConfig.addPlugin(modules.assetsCore, { esbuild: userOptions.assets.esbuild });
|
|
115
114
|
|
|
116
115
|
eleventyConfig.addPlugin(modules.headCore);
|
|
117
116
|
eleventyConfig.addPlugin(modules.sitemapCore, {
|
|
@@ -120,29 +119,42 @@ export default function baseline(options = {}) {
|
|
|
120
119
|
languages
|
|
121
120
|
});
|
|
122
121
|
|
|
123
|
-
// Filters
|
|
122
|
+
// --- Filters ---
|
|
124
123
|
eleventyConfig.addFilter('markdownify', filters.markdownFilter);
|
|
125
124
|
eleventyConfig.addFilter('relatedPosts', filters.relatedPostsFilter);
|
|
126
125
|
eleventyConfig.addFilter('isString', filters.isStringFilter);
|
|
127
126
|
|
|
128
|
-
// Shortcodes
|
|
127
|
+
// --- Shortcodes ---
|
|
129
128
|
eleventyConfig.addShortcode('image', shortcodes.imageShortcode);
|
|
130
129
|
|
|
131
|
-
//
|
|
130
|
+
// --- Image dev server ---
|
|
131
|
+
// Serves on-demand image transforms during `--serve` without writing to disk.
|
|
132
132
|
eleventyConfig.addPlugin(eleventyImageOnRequestDuringServePlugin);
|
|
133
133
|
|
|
134
|
-
// Debug
|
|
134
|
+
// --- Debug ---
|
|
135
|
+
// Underscore-prefixed filters and navigator template for inspecting
|
|
136
|
+
// data at render time. Not part of the public API surface.
|
|
135
137
|
eleventyConfig.addFilter('_inspect', debug.inspect);
|
|
136
138
|
eleventyConfig.addFilter('_json', debug.json);
|
|
137
139
|
eleventyConfig.addFilter('_keys', debug.keys);
|
|
138
140
|
eleventyConfig.addPlugin(modules.navigatorCore, { enableNavigatorTemplate: userOptions.enableNavigatorTemplate });
|
|
141
|
+
|
|
142
|
+
// Temporary content map debug listener.
|
|
143
|
+
eleventyConfig.on('eleventy.contentMap', async ({ inputPathToUrl, urlToInputPath }) => {
|
|
144
|
+
let debuginput = inputPathToUrl;
|
|
145
|
+
let debugurl = urlToInputPath;
|
|
146
|
+
|
|
147
|
+
return (debuginput, debugurl);
|
|
148
|
+
});
|
|
139
149
|
};
|
|
140
150
|
|
|
141
|
-
// Set
|
|
151
|
+
// Set a named function identity so eleventyConfig.hasPlugin() can detect this plugin.
|
|
142
152
|
Object.defineProperty(plugin, 'name', { value: `${name}` });
|
|
143
153
|
return plugin;
|
|
144
154
|
}
|
|
145
155
|
|
|
156
|
+
// --- Eleventy directory and template config ---
|
|
157
|
+
// Exported separately so consuming sites can re-export without duplicating values.
|
|
146
158
|
export const config = {
|
|
147
159
|
dir: {
|
|
148
160
|
input: 'src',
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
1
2
|
import { TemplatePath } from '@11ty/eleventy-utils';
|
|
2
3
|
import { addTrailingSlash, resolveAssetsDir } from '../../../core/helpers.js';
|
|
3
4
|
import { warnIfVerbose, getVerbose } from '../../../core/logging.js';
|
|
4
5
|
|
|
6
|
+
import assetsESbuild from '../../assets-esbuild/process.js';
|
|
7
|
+
import assetsPostCSS from '../../assets-postcss/process.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Sync the cache object with resolved directory paths.
|
|
11
|
+
* Called once at registration time and again on the eleventy.directories event
|
|
12
|
+
* when Eleventy finalizes its directory config.
|
|
13
|
+
*/
|
|
5
14
|
const syncCacheFromDirectories = (cache, dirs, rawDir) => {
|
|
6
15
|
const inputDir = TemplatePath.addLeadingDotSlash(dirs.input || './');
|
|
7
16
|
const outputDir = TemplatePath.addLeadingDotSlash(dirs.output || './');
|
|
@@ -13,6 +22,10 @@ const syncCacheFromDirectories = (cache, dirs, rawDir) => {
|
|
|
13
22
|
cache.assetsOutput = assetsOutputDir;
|
|
14
23
|
};
|
|
15
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Guard: resolve directories from eleventyConfig.dir if the eleventy.directories
|
|
27
|
+
* event hasn't fired yet (e.g. when global data or watch targets are read early).
|
|
28
|
+
*/
|
|
16
29
|
const ensureCache = (cache, eleventyConfig, rawDir, verbose) => {
|
|
17
30
|
if (cache.assetsInput) return;
|
|
18
31
|
syncCacheFromDirectories(cache, eleventyConfig.dir || {}, rawDir);
|
|
@@ -22,31 +35,37 @@ const ensureCache = (cache, eleventyConfig, rawDir, verbose) => {
|
|
|
22
35
|
/**
|
|
23
36
|
* eleventy-plugin-assets-core
|
|
24
37
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
38
|
+
* The single assets plugin. Owns all Eleventy wiring for JS and CSS processing:
|
|
39
|
+
* directory resolution, template formats, extensions, compile guards, inline
|
|
40
|
+
* filters, watch targets, and global data. Processing logic lives in the
|
|
41
|
+
* pure functions imported from assets-esbuild and assets-postcss.
|
|
28
42
|
*
|
|
29
43
|
* Options:
|
|
30
44
|
* - verbose (boolean, default global baseline verbose): enable verbose logs.
|
|
45
|
+
* - esbuild (object): options forwarded to esbuild (minify, target).
|
|
46
|
+
* Defaults live in assets-esbuild/process.js — pass only overrides.
|
|
31
47
|
*/
|
|
32
48
|
/** @param {import("@11ty/eleventy").UserConfig} eleventyConfig */
|
|
33
49
|
export default function assetsCore(eleventyConfig, options = {}) {
|
|
34
50
|
const verbose = getVerbose(eleventyConfig) || options.verbose || false;
|
|
35
51
|
const userKey = 'assets';
|
|
36
52
|
|
|
37
|
-
// Extract raw directory value from config (can be done early)
|
|
53
|
+
// Extract raw directory value from config (can be done early).
|
|
38
54
|
const rawDir = eleventyConfig.dir?.[userKey] || userKey;
|
|
39
55
|
|
|
40
|
-
// Cache
|
|
56
|
+
// Cache holds resolved paths. Initialized as nulls, populated immediately
|
|
57
|
+
// by syncCacheFromDirectories, then updated when eleventy.directories fires.
|
|
41
58
|
const cache = {
|
|
42
|
-
input:
|
|
43
|
-
output:
|
|
44
|
-
assetsInput:
|
|
59
|
+
input: null,
|
|
60
|
+
output: null,
|
|
61
|
+
assetsInput: null,
|
|
45
62
|
assetsOutput: null
|
|
46
63
|
};
|
|
47
64
|
|
|
48
65
|
syncCacheFromDirectories(cache, eleventyConfig.dir || {}, rawDir);
|
|
49
66
|
|
|
67
|
+
// Update cache when Eleventy finalizes directories, and register a virtual
|
|
68
|
+
// `directories.assets` key so other code can read the resolved assets path.
|
|
50
69
|
eleventyConfig.on('eleventy.directories', (directories) => {
|
|
51
70
|
syncCacheFromDirectories(cache, directories, rawDir);
|
|
52
71
|
|
|
@@ -66,19 +85,113 @@ export default function assetsCore(eleventyConfig, options = {}) {
|
|
|
66
85
|
});
|
|
67
86
|
});
|
|
68
87
|
|
|
88
|
+
// Expose resolved assets paths as global data for templates.
|
|
89
|
+
// Templates use _baseline.assets.input to build paths for inline filters.
|
|
69
90
|
eleventyConfig.addGlobalData('_baseline.assets', () => {
|
|
70
91
|
ensureCache(cache, eleventyConfig, rawDir, verbose);
|
|
71
|
-
// Merge with existing _baseline.assets (e.g., esbuild config)
|
|
72
|
-
const existing = eleventyConfig.globalData?._baseline?.assets || {};
|
|
73
92
|
return {
|
|
74
93
|
input: cache.assetsInput,
|
|
75
|
-
output: cache.assetsOutput
|
|
76
|
-
...existing
|
|
94
|
+
output: cache.assetsOutput
|
|
77
95
|
};
|
|
78
96
|
});
|
|
79
97
|
|
|
80
|
-
// Watch
|
|
98
|
+
// Watch common asset formats so edits trigger reloads during --serve.
|
|
81
99
|
ensureCache(cache, eleventyConfig, rawDir, verbose);
|
|
82
100
|
const watchGlob = TemplatePath.join(cache.assetsInput, '**/*.{css,js,svg,png,jpeg,jpg,webp,gif,avif}');
|
|
83
101
|
eleventyConfig.addWatchTarget(watchGlob);
|
|
102
|
+
|
|
103
|
+
// --- JS (esbuild) ---
|
|
104
|
+
// Register js as a template format. Only index.js files under assets/js/
|
|
105
|
+
// are compiled; everything else (11tydata.js, non-entry scripts) is skipped
|
|
106
|
+
// by the compile guard. The inline filter wraps the same process function.
|
|
107
|
+
// Defaults (minify, target) live in assets-esbuild/process.js.
|
|
108
|
+
|
|
109
|
+
const esbuildOptions = options.esbuild || {};
|
|
110
|
+
const jsDir = `${cache.assetsInput}js/`;
|
|
111
|
+
|
|
112
|
+
eleventyConfig.addTemplateFormats('js');
|
|
113
|
+
|
|
114
|
+
// Prevent Eleventy from processing 11tydata.js files as templates.
|
|
115
|
+
// The compile guard below also filters these, but without this ignore
|
|
116
|
+
// Eleventy still enters them into the template graph (data cascade,
|
|
117
|
+
// permalink computation) before compile gets a chance to reject them.
|
|
118
|
+
eleventyConfig.ignores.add(`${cache.input}**/*.11tydata.js`);
|
|
119
|
+
|
|
120
|
+
eleventyConfig.addExtension('js', {
|
|
121
|
+
outputFileExtension: 'js',
|
|
122
|
+
useLayouts: false,
|
|
123
|
+
read: false,
|
|
124
|
+
compileOptions: {
|
|
125
|
+
permalink: true,
|
|
126
|
+
cache: true
|
|
127
|
+
},
|
|
128
|
+
// Compile guard: only process index.js files under the assets js directory.
|
|
129
|
+
// Returning undefined skips the file without error.
|
|
130
|
+
compile: async function (_inputContent, inputPath) {
|
|
131
|
+
if (
|
|
132
|
+
inputPath.includes('11tydata.js') ||
|
|
133
|
+
!inputPath.startsWith(jsDir) ||
|
|
134
|
+
path.basename(inputPath) !== 'index.js'
|
|
135
|
+
) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return async () => assetsESbuild(inputPath, esbuildOptions);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Inline filter: bundle a JS file and wrap in <script> tags.
|
|
144
|
+
// Accepts per-call esbuild options (merged with defaults in process.js).
|
|
145
|
+
// Eleventy's addAsyncFilter handles the Nunjucks callback bridge,
|
|
146
|
+
// so this is a plain async function.
|
|
147
|
+
eleventyConfig.addAsyncFilter('inlineESbuild', async function (inputPath, opts = {}) {
|
|
148
|
+
try {
|
|
149
|
+
const js = await assetsESbuild(inputPath, opts);
|
|
150
|
+
return `<script>${js}</script>`;
|
|
151
|
+
} catch {
|
|
152
|
+
// Non-fatal: return an error comment so the build doesn't break.
|
|
153
|
+
return `<script>/* Error processing JS */</script>`;
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// --- CSS (PostCSS) ---
|
|
158
|
+
// Register css as a template format. Only index.css files under assets/css/
|
|
159
|
+
// are compiled; non-entry CSS is skipped. Reads from disk (read: false) —
|
|
160
|
+
// the process function owns its own I/O. Config loading and caching live
|
|
161
|
+
// in assets-postcss/process.js.
|
|
162
|
+
|
|
163
|
+
const cssDir = `${cache.assetsInput}css/`;
|
|
164
|
+
|
|
165
|
+
eleventyConfig.addTemplateFormats('css');
|
|
166
|
+
|
|
167
|
+
eleventyConfig.addExtension('css', {
|
|
168
|
+
outputFileExtension: 'css',
|
|
169
|
+
useLayouts: false,
|
|
170
|
+
read: false,
|
|
171
|
+
compileOptions: {
|
|
172
|
+
permalink: true,
|
|
173
|
+
cache: true
|
|
174
|
+
},
|
|
175
|
+
// Compile guard: only process index.css files under the assets css directory.
|
|
176
|
+
compile: async function (_inputContent, inputPath) {
|
|
177
|
+
if (!inputPath.startsWith(cssDir) || path.basename(inputPath) !== 'index.css') {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return async () => assetsPostCSS(inputPath);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Inline filter: process a CSS file through PostCSS and wrap in <style> tags.
|
|
186
|
+
// Eleventy's addAsyncFilter handles the Nunjucks callback bridge,
|
|
187
|
+
// so this is a plain async function.
|
|
188
|
+
eleventyConfig.addAsyncFilter('inlinePostCSS', async function (inputPath) {
|
|
189
|
+
try {
|
|
190
|
+
const css = await assetsPostCSS(inputPath);
|
|
191
|
+
return `<style>${css}</style>`;
|
|
192
|
+
} catch {
|
|
193
|
+
// Non-fatal: return an error comment so the build doesn't break.
|
|
194
|
+
return `<style>/* Error processing CSS */</style>`;
|
|
195
|
+
}
|
|
196
|
+
});
|
|
84
197
|
}
|
|
@@ -2,7 +2,16 @@ import * as esbuild from 'esbuild';
|
|
|
2
2
|
|
|
3
3
|
const defaultOptions = { minify: true, target: 'es2020' };
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Bundle a JS file with esbuild.
|
|
7
|
+
*
|
|
8
|
+
* @param {string} jsFilePath - Absolute path to the entry file.
|
|
9
|
+
* @param {Object} [options] - esbuild options (merged with defaults).
|
|
10
|
+
* @param {boolean} [options.minify=true] - Minify output.
|
|
11
|
+
* @param {string} [options.target='es2020'] - esbuild target.
|
|
12
|
+
* @returns {Promise<string>} Bundled JS text, or an error comment on failure.
|
|
13
|
+
*/
|
|
14
|
+
export default async function assetsESbuild(jsFilePath, options = {}) {
|
|
6
15
|
const userOptions = { ...defaultOptions, ...options };
|
|
7
16
|
|
|
8
17
|
try {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import postcss from 'postcss';
|
|
3
|
+
import loadPostCSSConfig from 'postcss-load-config';
|
|
4
|
+
import fallbackPostCSSConfig from './fallback/postcss.config.js';
|
|
5
|
+
|
|
6
|
+
// Resolve user PostCSS config from the project root (cwd), not the Eleventy input dir.
|
|
7
|
+
const configRoot = process.cwd();
|
|
8
|
+
let cachedConfig = null;
|
|
9
|
+
|
|
10
|
+
async function getPostCSSConfig() {
|
|
11
|
+
if (cachedConfig) return cachedConfig;
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
// Prefer the consuming project's PostCSS config (postcss.config.* or package.json#postcss).
|
|
15
|
+
cachedConfig = await loadPostCSSConfig({}, configRoot);
|
|
16
|
+
} catch {
|
|
17
|
+
// If none is found, fall back to the bundled Baseline config to keep builds working.
|
|
18
|
+
const { plugins, ...options } = fallbackPostCSSConfig;
|
|
19
|
+
cachedConfig = { plugins, options };
|
|
20
|
+
}
|
|
21
|
+
return cachedConfig;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Process a CSS file through PostCSS.
|
|
26
|
+
* Reads from disk, uses project postcss.config.js or bundled fallback.
|
|
27
|
+
* Config is cached for the lifetime of the process.
|
|
28
|
+
*
|
|
29
|
+
* @param {string} cssFilePath - Absolute path to the entry file.
|
|
30
|
+
* @returns {Promise<string>} Processed CSS text, or an error comment on failure.
|
|
31
|
+
*/
|
|
32
|
+
export default async function assetsPostCSS(cssFilePath) {
|
|
33
|
+
try {
|
|
34
|
+
const cssContent = await fs.readFile(cssFilePath, 'utf8');
|
|
35
|
+
const { plugins, options } = await getPostCSSConfig();
|
|
36
|
+
|
|
37
|
+
const result = await postcss(plugins).process(cssContent, {
|
|
38
|
+
...options,
|
|
39
|
+
from: cssFilePath
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Return raw CSS; markup wrapping is handled in the plugin registration.
|
|
43
|
+
return result.css;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error(error);
|
|
46
|
+
// Surface a safe CSS string so the caller can decide how to wrap it.
|
|
47
|
+
return '/* Error processing CSS */';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -2,11 +2,6 @@
|
|
|
2
2
|
// Original: https://github.com/posthtml/posthtml-head-elements
|
|
3
3
|
// Adapted for Baseline head-core.
|
|
4
4
|
|
|
5
|
-
// Based on posthtml-head-elements (MIT License).
|
|
6
|
-
// Original: https://github.com/posthtml/posthtml-head-elements
|
|
7
|
-
// Adapted for Baseline head-core.
|
|
8
|
-
|
|
9
|
-
import util from 'node:util';
|
|
10
5
|
import { createRequire } from 'node:module';
|
|
11
6
|
|
|
12
7
|
const require = createRequire(import.meta.url);
|
|
@@ -22,33 +17,33 @@ function nonArray(type, content) {
|
|
|
22
17
|
}
|
|
23
18
|
|
|
24
19
|
function findElmType(type, objectData) {
|
|
25
|
-
|
|
20
|
+
const elementType = {
|
|
26
21
|
meta: function () {
|
|
27
22
|
if (Array.isArray(objectData)) {
|
|
28
23
|
return nonString(type, objectData);
|
|
29
24
|
} else {
|
|
30
|
-
|
|
25
|
+
console.warn('posthtml-head-elements: Please use the correct syntax for a meta element');
|
|
31
26
|
}
|
|
32
27
|
},
|
|
33
28
|
title: function () {
|
|
34
29
|
if (typeof objectData === 'string') {
|
|
35
30
|
return nonArray('title', objectData);
|
|
36
31
|
} else {
|
|
37
|
-
|
|
32
|
+
console.warn('posthtml-head-elements: Please use the correct syntax for a title element');
|
|
38
33
|
}
|
|
39
34
|
},
|
|
40
35
|
link: function () {
|
|
41
36
|
if (Array.isArray(objectData)) {
|
|
42
37
|
return nonString(type, objectData);
|
|
43
38
|
} else {
|
|
44
|
-
|
|
39
|
+
console.warn('posthtml-head-elements: Please use the correct syntax for a link element');
|
|
45
40
|
}
|
|
46
41
|
},
|
|
47
42
|
linkCanonical: function () {
|
|
48
43
|
if (Array.isArray(objectData)) {
|
|
49
44
|
return nonString('link', objectData);
|
|
50
45
|
} else {
|
|
51
|
-
|
|
46
|
+
console.warn('posthtml-head-elements: Please use the correct syntax for a linkCanonical element');
|
|
52
47
|
}
|
|
53
48
|
},
|
|
54
49
|
script: function () {
|
|
@@ -58,7 +53,7 @@ function findElmType(type, objectData) {
|
|
|
58
53
|
return content !== undefined ? { tag: 'script', attrs, content: [content] } : { tag: 'script', attrs };
|
|
59
54
|
});
|
|
60
55
|
} else {
|
|
61
|
-
|
|
56
|
+
console.warn('posthtml-head-elements: Please use the correct syntax for a script element');
|
|
62
57
|
}
|
|
63
58
|
},
|
|
64
59
|
style: function () {
|
|
@@ -68,30 +63,30 @@ function findElmType(type, objectData) {
|
|
|
68
63
|
return content !== undefined ? { tag: 'style', attrs, content: [content] } : { tag: 'style', attrs };
|
|
69
64
|
});
|
|
70
65
|
} else {
|
|
71
|
-
|
|
66
|
+
console.warn('posthtml-head-elements: Please use the correct syntax for a style element');
|
|
72
67
|
}
|
|
73
68
|
},
|
|
74
69
|
base: function () {
|
|
75
70
|
if (Array.isArray(objectData)) {
|
|
76
71
|
return nonString(type, objectData);
|
|
77
72
|
} else {
|
|
78
|
-
|
|
73
|
+
console.warn('posthtml-head-elements: Please use the correct syntax for a base element');
|
|
79
74
|
}
|
|
80
75
|
},
|
|
81
76
|
default: function () {
|
|
82
|
-
|
|
77
|
+
console.warn('posthtml-head-elements: Please make sure the HTML head type is correct');
|
|
83
78
|
}
|
|
84
79
|
};
|
|
85
80
|
|
|
86
81
|
if (type.indexOf('_') !== -1) {
|
|
87
|
-
type = type.
|
|
82
|
+
type = type.slice(0, type.indexOf('_'));
|
|
88
83
|
}
|
|
89
84
|
|
|
90
85
|
return elementType[type]() || elementType['default']();
|
|
91
86
|
}
|
|
92
87
|
|
|
93
88
|
function buildNewTree(headElements, EOL) {
|
|
94
|
-
|
|
89
|
+
const newHeadElements = [];
|
|
95
90
|
|
|
96
91
|
Object.keys(headElements).forEach(function (value) {
|
|
97
92
|
newHeadElements.push(findElmType(value, headElements[value]));
|
|
@@ -113,11 +108,11 @@ export default function (options) {
|
|
|
113
108
|
options.headElementsTag = options.headElementsTag || 'posthtml-head-elements';
|
|
114
109
|
|
|
115
110
|
if (!options.headElements) {
|
|
116
|
-
|
|
111
|
+
console.warn(
|
|
117
112
|
"posthtml-head-elements: Don't forget to add a link to the JSON file containing the head elements to insert"
|
|
118
113
|
);
|
|
119
114
|
}
|
|
120
|
-
|
|
115
|
+
const jsonOne = typeof options.headElements !== 'string' ? options.headElements : require(options.headElements);
|
|
121
116
|
|
|
122
117
|
return function posthtmlHeadElements(tree) {
|
|
123
118
|
tree.match({ tag: options.headElementsTag }, function () {
|
|
@@ -2,22 +2,37 @@ import headElements from '../drivers/posthtml-head-elements.js';
|
|
|
2
2
|
import { getVerbose, logIfVerbose } from '../../../core/logging.js';
|
|
3
3
|
import { buildHead } from '../utils/head-utils.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* eleventy-plugin-head-core
|
|
7
|
+
*
|
|
8
|
+
* Manages the <head> for every page. Merges site-level defaults, page-level
|
|
9
|
+
* overrides, and computed values (canonical URL, open graph, structured data)
|
|
10
|
+
* into a single head spec, then injects the result into HTML via a PostHTML
|
|
11
|
+
* transform. Pages control their head through a `head` data key.
|
|
12
|
+
*
|
|
13
|
+
* Depends on: core/logging, head-core/utils/head-utils, head-core/drivers/posthtml-head-elements.
|
|
14
|
+
* No cross-module dependencies.
|
|
15
|
+
*/
|
|
5
16
|
/** @param {import("@11ty/eleventy").UserConfig} eleventyConfig */
|
|
6
17
|
export default function headCore(eleventyConfig, options = {}) {
|
|
7
18
|
const verbose = getVerbose(eleventyConfig) || options.verbose || false;
|
|
8
19
|
|
|
9
|
-
//
|
|
20
|
+
// Internal options — not part of the public API.
|
|
10
21
|
const userKey = options.dirKey || 'head';
|
|
11
22
|
const headElementsTag = options.headElementsTag || 'baseline-head';
|
|
12
23
|
const eol = options.EOL || '\n';
|
|
13
24
|
const pathPrefix = options.pathPrefix ?? eleventyConfig?.pathPrefix ?? '';
|
|
14
25
|
const siteUrl = options.siteUrl;
|
|
15
26
|
|
|
27
|
+
// Cache the content map so canonical URLs can resolve inputPath → URL.
|
|
28
|
+
// Updated each build when Eleventy emits the contentMap event.
|
|
16
29
|
let cachedContentMap = {};
|
|
17
30
|
eleventyConfig.on('eleventy.contentMap', ({ inputPathToUrl, urlToInputPath }) => {
|
|
18
31
|
cachedContentMap = { inputPathToUrl, urlToInputPath };
|
|
19
32
|
});
|
|
20
33
|
|
|
34
|
+
// Computed global data: build the head spec for every page through the
|
|
35
|
+
// data cascade. Templates access the result via `page.head`.
|
|
21
36
|
eleventyConfig.addGlobalData('eleventyComputed.page.head', () => {
|
|
22
37
|
return (data) =>
|
|
23
38
|
buildHead(data, {
|
|
@@ -30,6 +45,9 @@ export default function headCore(eleventyConfig, options = {}) {
|
|
|
30
45
|
});
|
|
31
46
|
});
|
|
32
47
|
|
|
48
|
+
// HTML transform: inject the head spec into the document <head> using
|
|
49
|
+
// PostHTML. Replaces the <baseline-head> placeholder tag with real elements.
|
|
50
|
+
// Falls back to building the spec from context if page.head isn't available.
|
|
33
51
|
eleventyConfig.htmlTransformer.addPosthtmlPlugin('html', function (context) {
|
|
34
52
|
logIfVerbose(verbose, 'head-core: injecting head elements for', context?.page?.inputPath || context?.outputPath);
|
|
35
53
|
|