This commit is contained in:
2024-11-03 17:16:20 +01:00
commit fd6412d6f2
8090 changed files with 778406 additions and 0 deletions

55
node_modules/@11ty/eleventy/src/Benchmark/Benchmark.js generated vendored Normal file
View File

@@ -0,0 +1,55 @@
import { performance } from "node:perf_hooks";
class Benchmark {
constructor() {
// TypeScript slop
this.timeSpent = 0;
this.timesCalled = 0;
this.beforeTimers = [];
}
reset() {
this.timeSpent = 0;
this.timesCalled = 0;
this.beforeTimers = [];
}
getNewTimestamp() {
if (performance) {
return performance.now();
}
return new Date().getTime();
}
incrementCount() {
this.timesCalled++;
}
// TODO(slightlyoff):
// disable all of these hrtime requests when not benchmarking
before() {
this.timesCalled++;
this.beforeTimers.push(this.getNewTimestamp());
}
after() {
if (!this.beforeTimers.length) {
throw new Error("You called Benchmark after() without a before().");
}
let before = this.beforeTimers.pop();
if (!this.beforeTimers.length) {
this.timeSpent += this.getNewTimestamp() - before;
}
}
getTimesCalled() {
return this.timesCalled;
}
getTotal() {
return this.timeSpent;
}
}
export default Benchmark;

View File

@@ -0,0 +1,135 @@
import debugUtil from "debug";
import ConsoleLogger from "../Util/ConsoleLogger.js";
import isAsyncFunction from "../Util/IsAsyncFunction.js";
import Benchmark from "./Benchmark.js";
const debugBenchmark = debugUtil("Eleventy:Benchmark");
class BenchmarkGroup {
constructor() {
this.benchmarks = {};
// Warning: aggregate benchmarks automatically default to false via BenchmarkManager->getBenchmarkGroup
this.isVerbose = true;
this.logger = new ConsoleLogger();
this.minimumThresholdMs = 50;
this.minimumThresholdPercent = 8;
}
setIsVerbose(isVerbose) {
this.isVerbose = isVerbose;
this.logger.isVerbose = isVerbose;
}
reset() {
for (var type in this.benchmarks) {
this.benchmarks[type].reset();
}
}
// TODO use addAsync everywhere instead
add(type, callback) {
let benchmark = (this.benchmarks[type] = new Benchmark());
/** @this {any} */
let fn = function (...args) {
benchmark.before();
let ret = callback.call(this, ...args);
benchmark.after();
return ret;
};
Object.defineProperty(fn, "__eleventyInternal", {
value: {
type: isAsyncFunction(callback) ? "async" : "sync",
callback,
},
});
return fn;
}
// callback must return a promise
// async addAsync(type, callback) {
// let benchmark = (this.benchmarks[type] = new Benchmark());
// benchmark.before();
// // dont await here.
// let promise = callback.call(this);
// promise.then(function() {
// benchmark.after();
// });
// return promise;
// }
setMinimumThresholdMs(minimumThresholdMs) {
let val = parseInt(minimumThresholdMs, 10);
if (isNaN(val)) {
throw new Error("`setMinimumThresholdMs` expects a number argument.");
}
this.minimumThresholdMs = val;
}
setMinimumThresholdPercent(minimumThresholdPercent) {
let val = parseInt(minimumThresholdPercent, 10);
if (isNaN(val)) {
throw new Error("`setMinimumThresholdPercent` expects a number argument.");
}
this.minimumThresholdPercent = val;
}
has(type) {
return !!this.benchmarks[type];
}
get(type) {
if (!this.benchmarks[type]) {
this.benchmarks[type] = new Benchmark();
}
return this.benchmarks[type];
}
padNumber(num, length) {
if (("" + num).length >= length) {
return num;
}
let prefix = new Array(length + 1).join(" ");
return (prefix + num).slice(-1 * length);
}
finish(label, totalTimeSpent) {
for (var type in this.benchmarks) {
let bench = this.benchmarks[type];
let isAbsoluteMinimumComparison = this.minimumThresholdMs > 0;
let totalForBenchmark = bench.getTotal();
let percent = Math.round((totalForBenchmark * 100) / totalTimeSpent);
let callCount = bench.getTimesCalled();
let output = {
ms: this.padNumber(totalForBenchmark.toFixed(0), 6),
percent: this.padNumber(percent, 3),
calls: this.padNumber(callCount, 5),
};
let str = `Benchmark ${output.ms}ms ${output.percent}% ${output.calls}× (${label}) ${type}`;
if (
isAbsoluteMinimumComparison &&
totalForBenchmark >= this.minimumThresholdMs &&
percent > this.minimumThresholdPercent
) {
this.logger.warn(str);
}
// Opt out of logging if low count (1× or 2×) or 0ms / 1%
if (
callCount > 1 || // called more than once
Math.round(totalForBenchmark) > 0 // more than 0.5ms
) {
debugBenchmark(str);
}
}
}
}
export default BenchmarkGroup;

View File

@@ -0,0 +1,73 @@
import { performance } from "node:perf_hooks";
import BenchmarkGroup from "./BenchmarkGroup.js";
// TODO this should not be a singleton, it belongs in the config or somewhere on the Eleventy instance.
class BenchmarkManager {
constructor() {
this.benchmarkGroups = {};
this.isVerbose = true;
this.start = this.getNewTimestamp();
}
reset() {
this.start = this.getNewTimestamp();
for (var j in this.benchmarkGroups) {
this.benchmarkGroups[j].reset();
}
}
getNewTimestamp() {
if (performance) {
return performance.now();
}
return new Date().getTime();
}
setVerboseOutput(isVerbose) {
this.isVerbose = !!isVerbose;
}
hasBenchmarkGroup(name) {
return name in this.benchmarkGroups;
}
getBenchmarkGroup(name) {
if (!this.benchmarkGroups[name]) {
this.benchmarkGroups[name] = new BenchmarkGroup();
// Special behavior for aggregate benchmarks
// so they dont console.log every time
if (name === "Aggregate") {
this.benchmarkGroups[name].setIsVerbose(false);
} else {
this.benchmarkGroups[name].setIsVerbose(this.isVerbose);
}
}
return this.benchmarkGroups[name];
}
getAll() {
return this.benchmarkGroups;
}
get(name) {
if (name) {
return this.getBenchmarkGroup(name);
}
return this.getAll();
}
finish() {
let totalTimeSpentBenchmarking = this.getNewTimestamp() - this.start;
for (var j in this.benchmarkGroups) {
this.benchmarkGroups[j].finish(j, totalTimeSpentBenchmarking);
}
}
}
export default BenchmarkManager;

121
node_modules/@11ty/eleventy/src/Data/ComputedData.js generated vendored Normal file
View File

@@ -0,0 +1,121 @@
import lodash from "@11ty/lodash-custom";
import debugUtil from "debug";
import ComputedDataQueue from "./ComputedDataQueue.js";
import ComputedDataTemplateString from "./ComputedDataTemplateString.js";
import ComputedDataProxy from "./ComputedDataProxy.js";
const { set: lodashSet, get: lodashGet } = lodash;
const debug = debugUtil("Eleventy:ComputedData");
class ComputedData {
constructor(config) {
this.computed = {};
this.symbolParseFunctions = {};
this.templateStringKeyLookup = {};
this.computedKeys = new Set();
this.declaredDependencies = {};
this.queue = new ComputedDataQueue();
this.config = config;
}
add(key, renderFn, declaredDependencies = [], symbolParseFn, templateInstance) {
this.computedKeys.add(key);
this.declaredDependencies[key] = declaredDependencies;
// bind config filters/JS functions
if (typeof renderFn === "function") {
let fns = {};
// TODO bug? no access to non-universal config things?
if (this.config) {
fns = this.config.javascriptFunctions;
}
renderFn = renderFn.bind({
...fns,
tmpl: templateInstance,
});
}
lodashSet(this.computed, key, renderFn);
if (symbolParseFn) {
lodashSet(this.symbolParseFunctions, key, symbolParseFn);
}
}
addTemplateString(key, renderFn, declaredDependencies = [], symbolParseFn, templateInstance) {
this.add(key, renderFn, declaredDependencies, symbolParseFn, templateInstance);
this.templateStringKeyLookup[key] = true;
}
async resolveVarOrder(data) {
let proxyByTemplateString = new ComputedDataTemplateString(this.computedKeys);
let proxyByProxy = new ComputedDataProxy(this.computedKeys);
for (let key of this.computedKeys) {
let computed = lodashGet(this.computed, key);
if (typeof computed !== "function") {
// add nodes for non functions (primitives like booleans, etc)
// This will not handle template strings, as they are normalized to functions
this.queue.addNode(key);
} else {
this.queue.uses(key, this.declaredDependencies[key]);
let symbolParseFn = lodashGet(this.symbolParseFunctions, key);
let varsUsed = [];
if (symbolParseFn) {
// use the parseForSymbols function in the TemplateEngine
varsUsed = symbolParseFn();
} else if (symbolParseFn !== false) {
// skip resolution is this is false (just use declaredDependencies)
let isTemplateString = !!this.templateStringKeyLookup[key];
let proxy = isTemplateString ? proxyByTemplateString : proxyByProxy;
varsUsed = await proxy.findVarsUsed(computed, data);
}
debug("%o accesses %o variables", key, varsUsed);
let filteredVarsUsed = varsUsed.filter((varUsed) => {
return (
(varUsed !== key && this.computedKeys.has(varUsed)) ||
varUsed.startsWith("collections.")
);
});
this.queue.uses(key, filteredVarsUsed);
}
}
}
async _setupDataEntry(data, order) {
debug("Computed data order of execution: %o", order);
for (let key of order) {
let computed = lodashGet(this.computed, key);
if (typeof computed === "function") {
let ret = await computed(data);
lodashSet(data, key, ret);
} else if (computed !== undefined) {
lodashSet(data, key, computed);
}
}
}
async setupData(data, orderFilter) {
await this.resolveVarOrder(data);
await this.processRemainingData(data, orderFilter);
}
async processRemainingData(data, orderFilter) {
// process all variables
let order = this.queue.getOrder();
if (orderFilter && typeof orderFilter === "function") {
order = order.filter(orderFilter.bind(this.queue));
}
await this._setupDataEntry(data, order);
this.queue.markComputed(order);
}
}
export default ComputedData;

View File

@@ -0,0 +1,131 @@
import lodash from "@11ty/lodash-custom";
import { isPlainObject } from "@11ty/eleventy-utils";
const { set: lodashSet, get: lodashGet } = lodash;
/* Calculates computed data using Proxies */
class ComputedDataProxy {
constructor(computedKeys) {
if (Array.isArray(computedKeys)) {
this.computedKeys = new Set(computedKeys);
} else {
this.computedKeys = computedKeys;
}
}
isArrayOrPlainObject(data) {
return Array.isArray(data) || isPlainObject(data);
}
getProxyData(data, keyRef) {
// WARNING: SIDE EFFECTS
// Set defaults for keys not already set on parent data
// TODO should make another effort to get rid of this,
// See the ProxyWrap util for more proxy handlers that will likely fix this
let undefinedValue = "__11TY_UNDEFINED__";
if (this.computedKeys) {
for (let key of this.computedKeys) {
if (lodashGet(data, key, undefinedValue) === undefinedValue) {
lodashSet(data, key, "");
}
}
}
let proxyData = this._getProxyData(data, keyRef);
return proxyData;
}
_getProxyForObject(dataObj, keyRef, parentKey = "") {
return new Proxy(
{},
{
get: (obj, key) => {
if (typeof key !== "string") {
return obj[key];
}
let newKey = `${parentKey ? `${parentKey}.` : ""}${key}`;
// Issue #1137
// Special case for Collections, always return an Array for collection keys
// so they it works fine with Array methods like `filter`, `map`, etc
if (newKey === "collections") {
keyRef.add(newKey);
return new Proxy(
{},
{
get: (target, key) => {
if (typeof key === "string") {
keyRef.add(`collections.${key}`);
return [];
}
return target[key];
},
},
);
}
let newData = this._getProxyData(dataObj[key], keyRef, newKey);
if (!this.isArrayOrPlainObject(newData)) {
keyRef.add(newKey);
}
return newData;
},
},
);
}
_getProxyForArray(dataArr, keyRef, parentKey = "") {
return new Proxy(new Array(dataArr.length), {
get: (obj, key) => {
if (Array.prototype.hasOwnProperty(key)) {
// remove `filter`, `constructor`, `map`, etc
keyRef.add(parentKey);
return obj[key];
}
// Hm, this needs to be better
if (key === "then") {
keyRef.add(parentKey);
return;
}
let newKey = `${parentKey}[${key}]`;
let newData = this._getProxyData(dataArr[key], keyRef, newKey);
if (!this.isArrayOrPlainObject(newData)) {
keyRef.add(newKey);
}
return newData;
},
});
}
_getProxyData(data, keyRef, parentKey = "") {
if (isPlainObject(data)) {
return this._getProxyForObject(data, keyRef, parentKey);
} else if (Array.isArray(data)) {
return this._getProxyForArray(data, keyRef, parentKey);
}
// everything else!
return data;
}
async findVarsUsed(fn, data = {}) {
let keyRef = new Set();
// careful, logging proxyData will mess with test results!
let proxyData = this.getProxyData(data, keyRef);
// squelch console logs for this fake proxy data pass 😅
// let savedLog = console.log;
// console.log = () => {};
await fn(proxyData);
// console.log = savedLog;
return Array.from(keyRef);
}
}
export default ComputedDataProxy;

View File

@@ -0,0 +1,64 @@
import { DepGraph as DependencyGraph } from "dependency-graph";
/* Keeps track of the dependency graph between computed data variables
* Removes keys from the graph when they are computed.
*/
class ComputedDataQueue {
constructor() {
this.graph = new DependencyGraph();
}
getOrder() {
return this.graph.overallOrder();
}
getOrderFor(name) {
return this.graph.dependenciesOf(name);
}
getDependsOn(name) {
return this.graph.dependantsOf(name);
}
isUsesStartsWith(name, prefix) {
if (name.startsWith(prefix)) {
return true;
}
return (
this.graph.dependenciesOf(name).filter((entry) => {
return entry.startsWith(prefix);
}).length > 0
);
}
addNode(name) {
if (!this.graph.hasNode(name)) {
this.graph.addNode(name);
}
}
_uses(graph, name, varsUsed = []) {
if (!graph.hasNode(name)) {
graph.addNode(name);
}
for (let varUsed of varsUsed) {
if (!graph.hasNode(varUsed)) {
graph.addNode(varUsed);
}
graph.addDependency(name, varUsed);
}
}
uses(name, varsUsed = []) {
this._uses(this.graph, name, varsUsed);
}
markComputed(varsComputed = []) {
for (let varComputed of varsComputed) {
this.graph.removeNode(varComputed);
}
}
}
export default ComputedDataQueue;

View File

@@ -0,0 +1,70 @@
import lodash from "@11ty/lodash-custom";
import debugUtil from "debug";
const { set: lodashSet } = lodash;
const debug = debugUtil("Eleventy:ComputedDataTemplateString");
/* Calculates computed data in Template Strings.
* Ideally we would use the Proxy approach but it doesnt work
* in some template languages that visit all available data even if
* it isnt used in the template (Nunjucks)
*/
class ComputedDataTemplateString {
constructor(computedKeys) {
if (Array.isArray(computedKeys)) {
this.computedKeys = new Set(computedKeys);
} else {
this.computedKeys = computedKeys;
}
// is this ¯\_(lisp)_/¯
// must be strings that wont be escaped by template languages
this.prefix = "(((11ty(((";
this.suffix = ")))11ty)))";
}
getProxyData() {
let proxyData = {};
// use these special strings as a workaround to check the rendered output
// cant use proxies here as some template languages trigger proxy for all
// keys in data
for (let key of this.computedKeys) {
// TODO dont allow to set eleventyComputed.page? other disallowed computed things?
lodashSet(proxyData, key, this.prefix + key + this.suffix);
}
return proxyData;
}
findVarsInOutput(output = "") {
let vars = new Set();
let splits = output.split(this.prefix);
for (let split of splits) {
let varName = split.slice(0, split.indexOf(this.suffix) < 0 ? 0 : split.indexOf(this.suffix));
if (varName) {
vars.add(varName);
}
}
return Array.from(vars);
}
async findVarsUsed(fn) {
let proxyData = this.getProxyData();
let output;
// Mitigation for #1061, errors with filters in the first pass shouldnt fail the whole thing.
try {
output = await fn(proxyData);
} catch (e) {
debug("Computed Data first pass data resolution error: %o", e);
}
// page.outputPath on serverless urls returns false.
if (typeof output === "string") {
return this.findVarsInOutput(output);
}
return [];
}
}
export default ComputedDataTemplateString;

737
node_modules/@11ty/eleventy/src/Data/TemplateData.js generated vendored Normal file
View File

@@ -0,0 +1,737 @@
import path from "node:path";
import semver from "semver";
import lodash from "@11ty/lodash-custom";
import { Merge, TemplatePath, isPlainObject } from "@11ty/eleventy-utils";
import debugUtil from "debug";
import unique from "../Util/Objects/Unique.js";
import TemplateGlob from "../TemplateGlob.js";
import EleventyExtensionMap from "../EleventyExtensionMap.js";
import EleventyBaseError from "../Errors/EleventyBaseError.js";
import TemplateDataInitialGlobalData from "./TemplateDataInitialGlobalData.js";
import { getEleventyPackageJson, getWorkingProjectPackageJson } from "../Util/ImportJsonSync.js";
import { EleventyImport, EleventyLoadContent } from "../Util/Require.js";
import { DeepFreeze } from "../Util/Objects/DeepFreeze.js";
const { set: lodashSet, get: lodashGet } = lodash;
const debugWarn = debugUtil("Eleventy:Warnings");
const debug = debugUtil("Eleventy:TemplateData");
const debugDev = debugUtil("Dev:Eleventy:TemplateData");
class TemplateDataConfigError extends EleventyBaseError {}
class TemplateDataParseError extends EleventyBaseError {}
class TemplateData {
constructor(eleventyConfig) {
if (!eleventyConfig) {
throw new TemplateDataConfigError("Missing `config`.");
}
this.eleventyConfig = eleventyConfig;
this.config = this.eleventyConfig.getConfig();
this.benchmarks = {
data: this.config.benchmarkManager.get("Data"),
aggregate: this.config.benchmarkManager.get("Aggregate"),
};
this.rawImports = {};
this.globalData = null;
this.templateDirectoryData = {};
this.isEsm = false;
this.initialGlobalData = new TemplateDataInitialGlobalData(this.eleventyConfig);
}
get dirs() {
return this.eleventyConfig.directories;
}
get inputDir() {
return this.dirs.input;
}
// if this was set but `falsy` we would fallback to inputDir
get dataDir() {
return this.dirs.data;
}
// This was async in 2.0 and prior but doesnt need to be any more.
getInputDir() {
return this.dirs.input;
}
getDataDir() {
return this.dataDir;
}
get _fsExistsCache() {
// It's common for data files not to exist, so we avoid going to the FS to
// re-check if they do via a quick-and-dirty cache.
return this.eleventyConfig.existsCache;
}
setFileSystemSearch(fileSystemSearch) {
this.fileSystemSearch = fileSystemSearch;
}
setProjectUsingEsm(isEsmProject) {
this.isEsm = !!isEsmProject;
}
get extensionMap() {
if (!this._extensionMap) {
this._extensionMap = new EleventyExtensionMap(this.eleventyConfig);
this._extensionMap.setFormats([]);
}
return this._extensionMap;
}
set extensionMap(map) {
this._extensionMap = map;
}
get environmentVariables() {
return this._env;
}
set environmentVariables(env) {
this._env = env;
}
/* Used by tests */
_setConfig(config) {
this.config = config;
}
getRawImports() {
if (!this.config.keys.package) {
debug(
"Opted-out of package.json assignment for global data with falsy value for `keys.package` configuration.",
);
return this.rawImports;
} else if (Object.keys(this.rawImports).length > 0) {
return this.rawImports;
}
try {
let pkgJson = getWorkingProjectPackageJson();
this.rawImports[this.config.keys.package] = pkgJson;
if (this.config.freezeReservedData) {
DeepFreeze(this.rawImports);
}
} catch (e) {
debug("Could not find or require package.json import for global data.");
}
return this.rawImports;
}
clearData() {
this.globalData = null;
this.configApiGlobalData = null;
this.templateDirectoryData = {};
}
_getGlobalDataGlobByExtension(extension) {
return TemplateGlob.normalizePath(this.dataDir, `/**/*.${extension}`);
}
// This is a backwards compatibility helper with the old `jsDataFileSuffix` configuration API
getDataFileSuffixes() {
// New API
if (Array.isArray(this.config.dataFileSuffixes)) {
return this.config.dataFileSuffixes;
}
// Backwards compatibility
if (this.config.jsDataFileSuffix) {
let suffixes = [];
suffixes.push(this.config.jsDataFileSuffix); // e.g. filename.11tydata.json
suffixes.push(""); // suffix-less for free with old API, e.g. filename.json
return suffixes;
}
return []; // if both of these entries are set to false, use no files
}
// This is used exclusively for --watch and --serve chokidar targets
async getTemplateDataFileGlob() {
let suffixes = this.getDataFileSuffixes();
let globSuffixesWithLeadingDot = new Set();
globSuffixesWithLeadingDot.add("json"); // covers .11tydata.json too
let globSuffixesWithoutLeadingDot = new Set();
// Typically using [ '.11tydata', '' ] suffixes to find data files
for (let suffix of suffixes) {
// TODO the `suffix` truthiness check is purely for backwards compat?
if (suffix && typeof suffix === "string") {
if (suffix.startsWith(".")) {
// .suffix.js
globSuffixesWithLeadingDot.add(`${suffix.slice(1)}.mjs`);
globSuffixesWithLeadingDot.add(`${suffix.slice(1)}.cjs`);
globSuffixesWithLeadingDot.add(`${suffix.slice(1)}.js`);
} else {
// "suffix.js" without leading dot
globSuffixesWithoutLeadingDot.add(`${suffix || ""}.mjs`);
globSuffixesWithoutLeadingDot.add(`${suffix || ""}.cjs`);
globSuffixesWithoutLeadingDot.add(`${suffix || ""}.js`);
}
}
}
// Configuration Data Extensions e.g. yaml
if (this.hasUserDataExtensions()) {
for (let extension of this.getUserDataExtensions()) {
globSuffixesWithLeadingDot.add(extension); // covers .11tydata.{extension} too
}
}
let paths = [];
if (globSuffixesWithLeadingDot.size > 0) {
paths.push(`${this.inputDir}**/*.{${Array.from(globSuffixesWithLeadingDot).join(",")}}`);
}
if (globSuffixesWithoutLeadingDot.size > 0) {
paths.push(`${this.inputDir}**/*{${Array.from(globSuffixesWithoutLeadingDot).join(",")}}`);
}
return TemplatePath.addLeadingDotSlashArray(paths);
}
// For spidering dependencies
// TODO Can we reuse getTemplateDataFileGlob instead? Maybe just filter off the .json files before scanning for dependencies
getTemplateJavaScriptDataFileGlob() {
let paths = [];
let suffixes = this.getDataFileSuffixes();
for (let suffix of suffixes) {
if (suffix) {
// TODO this check is purely for backwards compat and I kinda feel like it shouldnt be here
// paths.push(`${this.inputDir}/**/*${suffix || ""}.cjs`); // Same as above
paths.push(`${this.inputDir}**/*${suffix || ""}.js`);
}
}
return TemplatePath.addLeadingDotSlashArray(paths);
}
getGlobalDataGlob() {
let extGlob = this.getGlobalDataExtensionPriorities().join(",");
return [this._getGlobalDataGlobByExtension("{" + extGlob + "}")];
}
getWatchPathCache() {
return this.pathCache;
}
getGlobalDataExtensionPriorities() {
return this.getUserDataExtensions().concat(["json", "mjs", "cjs", "js"]);
}
static calculateExtensionPriority(path, priorities) {
for (let i = 0; i < priorities.length; i++) {
let ext = priorities[i];
if (path.endsWith(ext)) {
return i;
}
}
return priorities.length;
}
async getGlobalDataFiles() {
let priorities = this.getGlobalDataExtensionPriorities();
let fsBench = this.benchmarks.aggregate.get("Searching the file system (data)");
fsBench.before();
let globs = this.getGlobalDataGlob();
let paths = await this.fileSystemSearch.search("global-data", globs);
fsBench.after();
// sort paths according to extension priorities
// here we use reverse ordering, because paths with bigger index in array will override the first ones
// example [path/file.json, path/file.js] here js will override json
paths = paths.sort((first, second) => {
let p1 = TemplateData.calculateExtensionPriority(first, priorities);
let p2 = TemplateData.calculateExtensionPriority(second, priorities);
if (p1 < p2) {
return -1;
}
if (p1 > p2) {
return 1;
}
return 0;
});
this.pathCache = paths;
return paths;
}
getObjectPathForDataFile(dataFilePath) {
let reducedPath = TemplatePath.stripLeadingSubPath(dataFilePath, this.dataDir);
let parsed = path.parse(reducedPath);
let folders = parsed.dir ? parsed.dir.split("/") : [];
folders.push(parsed.name);
return folders;
}
async getAllGlobalData() {
let globalData = {};
let files = TemplatePath.addLeadingDotSlashArray(await this.getGlobalDataFiles());
this.config.events.emit("eleventy.globalDataFiles", files);
let dataFileConflicts = {};
for (let j = 0, k = files.length; j < k; j++) {
let data = await this.getDataValue(files[j]);
let objectPathTarget = this.getObjectPathForDataFile(files[j]);
// Since we're joining directory paths and an array is not usable as an objectkey since two identical arrays are not double equal,
// we can just join the array by a forbidden character ("/"" is chosen here, since it works on Linux, Mac and Windows).
// If at some point this isn't enough anymore, it would be possible to just use JSON.stringify(objectPathTarget) since that
// is guaranteed to work but is signifivcantly slower.
let objectPathTargetString = objectPathTarget.join(path.sep);
// if two global files have the same path (but different extensions)
// and conflict, lets merge them.
if (dataFileConflicts[objectPathTargetString]) {
debugWarn(
`merging global data from ${files[j]} with an already existing global data file (${dataFileConflicts[objectPathTargetString]}). Overriding existing keys.`,
);
let oldData = lodashGet(globalData, objectPathTarget);
data = TemplateData.mergeDeep(this.config.dataDeepMerge, oldData, data);
}
dataFileConflicts[objectPathTargetString] = files[j];
debug(`Found global data file ${files[j]} and adding as: ${objectPathTarget}`);
lodashSet(globalData, objectPathTarget, data);
}
return globalData;
}
async #getInitialGlobalData() {
let globalData = await this.initialGlobalData.getData();
if (!("eleventy" in globalData)) {
globalData.eleventy = {};
}
// #2293 for meta[name=generator]
const pkg = getEleventyPackageJson();
globalData.eleventy.version = semver.coerce(pkg.version).toString();
globalData.eleventy.generator = `Eleventy v${globalData.eleventy.version}`;
if (this.environmentVariables) {
if (!("env" in globalData.eleventy)) {
globalData.eleventy.env = {};
}
Object.assign(globalData.eleventy.env, this.environmentVariables);
}
if (this.dirs) {
if (!("directories" in globalData.eleventy)) {
globalData.eleventy.directories = {};
}
Object.assign(globalData.eleventy.directories, this.dirs.getUserspaceInstance());
}
// Reserved
if (this.config.freezeReservedData) {
DeepFreeze(globalData.eleventy);
}
return globalData;
}
async getInitialGlobalData() {
if (!this.configApiGlobalData) {
this.configApiGlobalData = this.#getInitialGlobalData();
}
return this.configApiGlobalData;
}
async #getGlobalData() {
let rawImports = this.getRawImports();
let configApiGlobalData = await this.getInitialGlobalData();
let globalJson = await this.getAllGlobalData();
let mergedGlobalData = Merge(globalJson, configApiGlobalData);
// OK: Shallow merge when combining rawImports (pkg) with global data files
return Object.assign({}, mergedGlobalData, rawImports);
}
async getGlobalData() {
if (!this.globalData) {
this.globalData = this.#getGlobalData();
}
return this.globalData;
}
/* Template and Directory data files */
async combineLocalData(localDataPaths) {
let localData = {};
if (!Array.isArray(localDataPaths)) {
localDataPaths = [localDataPaths];
}
// Filter out files we know don't exist to avoid overhead for checking
const dataPaths = await Promise.all(
localDataPaths.map((path) => {
if (this._fsExistsCache.exists(path)) {
return path;
}
return false;
}),
);
localDataPaths = dataPaths.filter((pathOrFalse) => {
return pathOrFalse === false ? false : true;
});
this.config.events.emit("eleventy.dataFiles", localDataPaths);
if (!localDataPaths.length) {
return localData;
}
let dataSource = {};
for (let path of localDataPaths) {
let dataForPath = await this.getDataValue(path);
if (!isPlainObject(dataForPath)) {
debug(
"Warning: Template and Directory data files expect an object to be returned, instead `%o` returned `%o`",
path,
dataForPath,
);
} else {
// clean up data for template/directory data files only.
let cleanedDataForPath = TemplateData.cleanupData(dataForPath);
for (let key in cleanedDataForPath) {
if (Object.prototype.hasOwnProperty.call(dataSource, key)) {
debugWarn(
"Local data files have conflicting data. Overwriting '%s' with data from '%s'. Previous data location was from '%s'",
key,
path,
dataSource[key],
);
}
dataSource[key] = path;
}
TemplateData.mergeDeep(this.config.dataDeepMerge, localData, cleanedDataForPath);
}
}
return localData;
}
async getTemplateDirectoryData(templatePath) {
if (!this.templateDirectoryData[templatePath]) {
let localDataPaths = await this.getLocalDataPaths(templatePath);
let importedData = await this.combineLocalData(localDataPaths);
this.templateDirectoryData[templatePath] = Object.assign({}, importedData);
}
return this.templateDirectoryData[templatePath];
}
getUserDataExtensions() {
if (!this.config.dataExtensions) {
return [];
}
// returning extensions in reverse order to create proper extension order
// later added formats will override first ones
return Array.from(this.config.dataExtensions.keys()).reverse();
}
getUserDataParser(extension) {
return this.config.dataExtensions.get(extension);
}
isUserDataExtension(extension) {
return this.config.dataExtensions && this.config.dataExtensions.has(extension);
}
hasUserDataExtensions() {
return this.config.dataExtensions && this.config.dataExtensions.size > 0;
}
async _parseDataFile(path, parser, options = {}) {
let readFile = !("read" in options) || options.read === true;
let rawInput;
if (readFile) {
rawInput = EleventyLoadContent(path, options);
}
if (readFile && !rawInput) {
return {};
}
try {
if (readFile) {
return parser(rawInput, path);
} else {
// path as a first argument is when `read: false`
// path as a second argument is for consistency with `read: true` API
return parser(path, path);
}
} catch (e) {
throw new TemplateDataParseError(`Having trouble parsing data file ${path}`, e);
}
}
// ignoreProcessing = false for global data files
// ignoreProcessing = true for local data files
async getDataValue(path) {
let extension = TemplatePath.getExtension(path);
if (extension === "js" || extension === "cjs" || extension === "mjs") {
// JS data file or required JSON (no preprocessing needed)
let localPath = TemplatePath.absolutePath(path);
let exists = this._fsExistsCache.exists(localPath);
// Make sure that relative lookups benefit from cache
this._fsExistsCache.markExists(path, exists);
if (!exists) {
return {};
}
let aggregateDataBench = this.benchmarks.aggregate.get("Data File");
aggregateDataBench.before();
let dataBench = this.benchmarks.data.get(`\`${path}\``);
dataBench.before();
let type = "cjs";
if (extension === "mjs" || (extension === "js" && this.isEsm)) {
type = "esm";
}
// We always need to use `import()`, as `require` isnt available in ESM.
let returnValue = await EleventyImport(localPath, type);
// TODO special exception for Global data `permalink.js`
// module.exports = (data) => `${data.page.filePathStem}/`; // Does not work
// module.exports = () => ((data) => `${data.page.filePathStem}/`); // Works
if (typeof returnValue === "function") {
let configApiGlobalData = await this.getInitialGlobalData();
returnValue = await returnValue(configApiGlobalData || {});
}
dataBench.after();
aggregateDataBench.after();
return returnValue;
} else if (this.isUserDataExtension(extension)) {
// Other extensions
let { parser, options } = this.getUserDataParser(extension);
return this._parseDataFile(path, parser, options);
} else if (extension === "json") {
// File to string, parse with JSON (preprocess)
const parser = (content) => JSON.parse(content);
return this._parseDataFile(path, parser);
} else {
throw new TemplateDataParseError(
`Could not find an appropriate data parser for ${path}. Do you need to add a plugin to your config file?`,
);
}
}
_pushExtensionsToPaths(paths, curpath, extensions) {
for (let extension of extensions) {
paths.push(curpath + "." + extension);
}
}
_addBaseToPaths(paths, base, extensions, nonEmptySuffixesOnly = false) {
let suffixes = this.getDataFileSuffixes();
for (let suffix of suffixes) {
suffix = suffix || "";
if (nonEmptySuffixesOnly && suffix === "") {
continue;
}
// data suffix
if (suffix) {
paths.push(base + suffix + ".js");
paths.push(base + suffix + ".cjs");
paths.push(base + suffix + ".mjs");
}
paths.push(base + suffix + ".json"); // default: .11tydata.json
// inject user extensions
this._pushExtensionsToPaths(paths, base + suffix, extensions);
}
}
async getLocalDataPaths(templatePath) {
let paths = [];
let parsed = path.parse(templatePath);
let inputDir = this.inputDir;
debugDev("getLocalDataPaths(%o)", templatePath);
debugDev("parsed.dir: %o", parsed.dir);
let userExtensions = this.getUserDataExtensions();
if (parsed.dir) {
let fileNameNoExt = this.extensionMap.removeTemplateExtension(parsed.base);
// default dataSuffix: .11tydata, is appended in _addBaseToPaths
debug("Using %o suffixes to find data files.", this.getDataFileSuffixes());
// Template data file paths
let filePathNoExt = parsed.dir + "/" + fileNameNoExt;
this._addBaseToPaths(paths, filePathNoExt, userExtensions);
// Directory data file paths
let allDirs = TemplatePath.getAllDirs(parsed.dir);
debugDev("allDirs: %o", allDirs);
for (let dir of allDirs) {
let lastDir = TemplatePath.getLastPathSegment(dir);
let dirPathNoExt = dir + "/" + lastDir;
if (inputDir) {
debugDev("dirStr: %o; inputDir: %o", dir, inputDir);
}
if (!inputDir || (dir.startsWith(inputDir) && dir !== inputDir)) {
if (this.config.dataFileDirBaseNameOverride) {
let indexDataFile = dir + "/" + this.config.dataFileDirBaseNameOverride;
this._addBaseToPaths(paths, indexDataFile, userExtensions, true);
} else {
this._addBaseToPaths(paths, dirPathNoExt, userExtensions);
}
}
}
// 0.11.0+ include root input dir files
// if using `docs/` as input dir, looks for docs/docs.json et al
if (inputDir) {
let lastInputDir = TemplatePath.addLeadingDotSlash(
TemplatePath.join(inputDir, TemplatePath.getLastPathSegment(inputDir)),
);
// in root input dir, search for index.11tydata.json et al
if (this.config.dataFileDirBaseNameOverride) {
let indexDataFile =
TemplatePath.getDirFromFilePath(lastInputDir) +
"/" +
this.config.dataFileDirBaseNameOverride;
this._addBaseToPaths(paths, indexDataFile, userExtensions, true);
} else if (lastInputDir !== "./") {
this._addBaseToPaths(paths, lastInputDir, userExtensions);
}
}
}
debug("getLocalDataPaths(%o): %o", templatePath, paths);
return unique(paths).reverse();
}
static mergeDeep(deepMerge, target, ...source) {
if (!deepMerge && deepMerge !== undefined) {
return Object.assign(target, ...source);
} else {
return TemplateData.merge(target, ...source);
}
}
static merge(target, ...source) {
return Merge(target, ...source);
}
/* Like cleanupData() but does not mutate */
static getCleanedTagsImmutable(data) {
let tags = [];
if (isPlainObject(data) && data.tags) {
if (typeof data.tags === "string") {
tags = (data.tags || "").split(",");
} else if (Array.isArray(data.tags)) {
tags = data.tags;
}
// Deduplicate tags
return [...new Set(tags)];
}
return tags;
}
static cleanupData(data) {
if (isPlainObject(data) && "tags" in data) {
if (typeof data.tags === "string") {
data.tags = data.tags ? [data.tags] : [];
} else if (data.tags === null) {
data.tags = [];
}
// Deduplicate tags
data.tags = [...new Set(data.tags)];
}
return data;
}
static getNormalizedExcludedCollections(data) {
let excludes = [];
let key = "eleventyExcludeFromCollections";
if (data?.[key] !== true) {
if (Array.isArray(data[key])) {
excludes = data[key];
} else if (typeof data[key] === "string") {
excludes = (data[key] || "").split(",");
}
}
return {
excludes,
excludeAll: data?.eleventyExcludeFromCollections === true,
};
}
/* Same as getIncludedTagNames() but may also include "all" */
static getIncludedCollectionNames(data) {
let tags = TemplateData.getCleanedTagsImmutable(data);
if (tags.length > 0) {
let { excludes, excludeAll } = TemplateData.getNormalizedExcludedCollections(data);
if (excludeAll) {
return [];
} else {
return ["all", ...tags].filter((tag) => !excludes.includes(tag));
}
} else {
return ["all"];
}
}
static getIncludedTagNames(data) {
let tags = TemplateData.getCleanedTagsImmutable(data);
if (tags.length > 0) {
let { excludes, excludeAll } = TemplateData.getNormalizedExcludedCollections(data);
if (excludeAll) {
return [];
} else {
return tags.filter((tag) => !excludes.includes(tag));
}
} else {
return [];
}
}
}
export default TemplateData;

View File

@@ -0,0 +1,40 @@
import lodash from "@11ty/lodash-custom";
import EleventyBaseError from "../Errors/EleventyBaseError.js";
const { set: lodashSet } = lodash;
class TemplateDataConfigError extends EleventyBaseError {}
class TemplateDataInitialGlobalData {
constructor(templateConfig) {
if (!templateConfig || templateConfig.constructor.name !== "TemplateConfig") {
throw new TemplateDataConfigError("Missing or invalid `templateConfig` (via Render plugin).");
}
this.templateConfig = templateConfig;
this.config = this.templateConfig.getConfig();
}
async getData() {
let globalData = {};
// via eleventyConfig.addGlobalData
if (this.config.globalData) {
let keys = Object.keys(this.config.globalData);
for (let key of keys) {
let returnValue = this.config.globalData[key];
// This section is problematic when used with eleventyComputed #3389
if (typeof returnValue === "function") {
returnValue = await returnValue();
}
lodashSet(globalData, key, returnValue);
}
}
return globalData;
}
}
export default TemplateDataInitialGlobalData;

1472
node_modules/@11ty/eleventy/src/Eleventy.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

43
node_modules/@11ty/eleventy/src/EleventyCommonJs.cjs generated vendored Normal file
View File

@@ -0,0 +1,43 @@
function canRequireModules() {
// via --experimental-require-module or newer than Node 22 support when this flag is no longer necessary
try {
require("./Util/Objects/SampleModule.mjs");
return true;
} catch(e) {
if(e.code === "ERR_REQUIRE_ESM") {
return false;
}
// Rethrow if not an ESM require error.
throw e;
}
}
if(!canRequireModules()) {
let error = new Error(`\`require("@11ty/eleventy")\` is incompatible with Eleventy v3 and this version of Node. You have a few options:
1. (Easiest) Change the \`require\` to use a dynamic import inside of an asynchronous CommonJS configuration
callback, for example:
module.exports = async function {
const {EleventyRenderPlugin, EleventyI18nPlugin, EleventyHtmlBasePlugin} = await import("@11ty/eleventy");
}
2. (Easier) Update the JavaScript syntax in your configuration file from CommonJS to ESM (change \`require\`
to use \`import\` and rename the file to have an \`.mjs\` file extension).
3. (More work) Change your project to use ESM-first by adding \`"type": "module"\` to your package.json. Any
\`.js\` will need to be ported to use ESM syntax (or renamed to \`.cjs\`.)
4. (Short term workaround) Use the --experimental-require-module flag to enable this behavior. Read
more: https://nodejs.org/api/modules.html#loading-ecmascript-modules-using-require It is possible that the
newest version of Node has this enabled by default—you can try upgrading your version of Node.js.`);
error.skipOriginalStack = true;
throw error;
}
// If we made it here require(ESM) works fine (via --experimental-require-module or newer Node.js defaults)
let mod = require("./Eleventy.js");
module.exports = mod;

284
node_modules/@11ty/eleventy/src/EleventyExtensionMap.js generated vendored Normal file
View File

@@ -0,0 +1,284 @@
import { TemplatePath } from "@11ty/eleventy-utils";
import TemplateEngineManager from "./Engines/TemplateEngineManager.js";
import EleventyBaseError from "./Errors/EleventyBaseError.js";
class EleventyExtensionMapConfigError extends EleventyBaseError {}
class EleventyExtensionMap {
constructor(config) {
this.config = config;
this._spiderJsDepsCache = {};
/** @type {Array} */
this.validTemplateLanguageKeys;
}
setFormats(formatKeys = []) {
// raw
this.formatKeys = formatKeys;
this.unfilteredFormatKeys = formatKeys.map(function (key) {
return key.trim().toLowerCase();
});
this.validTemplateLanguageKeys = this.unfilteredFormatKeys.filter((key) =>
this.hasExtension(key),
);
this.passthroughCopyKeys = this.unfilteredFormatKeys.filter((key) => !this.hasExtension(key));
}
set config(cfg) {
if (!cfg || cfg.constructor.name !== "TemplateConfig") {
throw new EleventyExtensionMapConfigError(
"Missing or invalid `config` argument (via setter).",
);
}
this.eleventyConfig = cfg;
}
get config() {
return this.eleventyConfig.getConfig();
}
get engineManager() {
if (!this._engineManager) {
this._engineManager = new TemplateEngineManager(this.eleventyConfig);
}
return this._engineManager;
}
reset() {
this.engineManager.reset();
}
/* Used for layout path resolution */
getFileList(path, dir) {
if (!path) {
return [];
}
let files = [];
this.validTemplateLanguageKeys.forEach((key) => {
this.getExtensionsFromKey(key).forEach(function (extension) {
files.push((dir ? dir + "/" : "") + path + "." + extension);
});
});
return files;
}
// Warning: this would false positive on an include, but is only used
// on paths found from the file system glob search.
// TODO: Method name might just need to be renamed to something more accurate.
isFullTemplateFilePath(path) {
for (let extension of this.validTemplateLanguageKeys) {
if (path.endsWith(`.${extension}`)) {
return true;
}
}
return false;
}
getCustomExtensionEntry(extension) {
if (!this.config.extensionMap) {
return;
}
for (let entry of this.config.extensionMap) {
if (entry.extension === extension) {
return entry;
}
}
}
getValidExtensionsForPath(path) {
let extensions = new Set();
for (let extension in this.extensionToKeyMap) {
if (path.endsWith(`.${extension}`)) {
extensions.add(extension);
}
}
// if multiple extensions are valid, sort from longest to shortest
// e.g. .11ty.js and .js
let sorted = Array.from(extensions)
.filter((extension) => this.validTemplateLanguageKeys.includes(extension))
.sort((a, b) => b.length - a.length);
return sorted;
}
async shouldSpiderJavaScriptDependencies(path) {
let extensions = this.getValidExtensionsForPath(path);
for (let extension of extensions) {
if (extension in this._spiderJsDepsCache) {
return this._spiderJsDepsCache[extension];
}
let cls = await this.engineManager.getEngineClassByExtension(extension);
if (cls) {
let entry = this.getCustomExtensionEntry(extension);
let shouldSpider = cls.shouldSpiderJavaScriptDependencies(entry);
this._spiderJsDepsCache[extension] = shouldSpider;
return shouldSpider;
}
}
return false;
}
getPassthroughCopyGlobs(inputDir) {
return this._getGlobs(this.passthroughCopyKeys, inputDir);
}
getValidGlobs(inputDir) {
return this._getGlobs(this.validTemplateLanguageKeys, inputDir);
}
getGlobs(inputDir) {
return this._getGlobs(this.unfilteredFormatKeys, inputDir);
}
_getGlobs(formatKeys, inputDir) {
let dir = TemplatePath.convertToRecursiveGlobSync(inputDir);
let extensions = new Set();
for (let key of formatKeys) {
if (this.hasExtension(key)) {
for (let extension of this.getExtensionsFromKey(key)) {
extensions.add(extension);
}
} else {
extensions.add(key);
}
}
if (extensions.size === 1) {
return [`${dir}/*.${Array.from(extensions)[0]}`];
} else if (extensions.size > 1) {
return [
// extra curly brackets /*.{cjs,txt}
`${dir}/*.{${Array.from(extensions).join(",")}}`,
];
}
return [];
}
hasExtension(key) {
for (let extension in this.extensionToKeyMap) {
if (
this.extensionToKeyMap[extension].key === key ||
this.extensionToKeyMap[extension].aliasKey === key
) {
return true;
}
}
return false;
}
getExtensionsFromKey(key) {
let extensions = new Set();
for (let extension in this.extensionToKeyMap) {
if (this.extensionToKeyMap[extension].aliasKey) {
// only add aliased extension if explicitly referenced in formats
// overrides will not have an aliasKey (md => md)
if (this.extensionToKeyMap[extension].aliasKey === key) {
extensions.add(extension);
}
} else if (this.extensionToKeyMap[extension].key === key) {
extensions.add(extension);
}
}
return Array.from(extensions);
}
// Only `addExtension` configuration API extensions
getExtensionEntriesFromKey(key) {
let entries = new Set();
if ("extensionMap" in this.config) {
for (let entry of this.config.extensionMap) {
if (entry.key === key) {
entries.add(entry);
}
}
}
return Array.from(entries);
}
// Determines whether a path is a passthrough copy file or a template (via TemplateWriter)
hasEngine(pathOrKey) {
return !!this.getKey(pathOrKey);
}
getKey(pathOrKey) {
pathOrKey = (pathOrKey || "").toLowerCase();
for (let extension in this.extensionToKeyMap) {
if (pathOrKey === extension || pathOrKey.endsWith("." + extension)) {
let key =
this.extensionToKeyMap[extension].aliasKey || this.extensionToKeyMap[extension].key;
// must be a valid format key passed (e.g. via --formats)
if (this.validTemplateLanguageKeys.includes(key)) {
return key;
}
}
}
}
getExtensionEntry(pathOrKey) {
pathOrKey = (pathOrKey || "").toLowerCase();
for (let extension in this.extensionToKeyMap) {
if (pathOrKey === extension || pathOrKey.endsWith("." + extension)) {
return this.extensionToKeyMap[extension];
}
}
}
removeTemplateExtension(path) {
for (let extension in this.extensionToKeyMap) {
if (path === extension || path.endsWith("." + extension)) {
return path.slice(
0,
path.length - 1 - extension.length < 0 ? 0 : path.length - 1 - extension.length,
);
}
}
return path;
}
// keys are file extensions
// values are template language keys
get extensionToKeyMap() {
if (!this._extensionToKeyMap) {
this._extensionToKeyMap = {
md: { key: "md", extension: "md" },
html: { key: "html", extension: "html" },
njk: { key: "njk", extension: "njk" },
liquid: { key: "liquid", extension: "liquid" },
"11ty.js": { key: "11ty.js", extension: "11ty.js" },
"11ty.cjs": { key: "11ty.js", extension: "11ty.cjs" },
"11ty.mjs": { key: "11ty.js", extension: "11ty.mjs" },
};
if ("extensionMap" in this.config) {
for (let entry of this.config.extensionMap) {
// extension and key are only different when aliasing.
this._extensionToKeyMap[entry.extension] = entry;
}
}
}
return this._extensionToKeyMap;
}
getReadableFileExtensions() {
return Object.keys(this.extensionToKeyMap).join(" ");
}
}
export default EleventyExtensionMap;

517
node_modules/@11ty/eleventy/src/EleventyFiles.js generated vendored Normal file
View File

@@ -0,0 +1,517 @@
import fs from "node:fs";
import { TemplatePath, isPlainObject } from "@11ty/eleventy-utils";
import debugUtil from "debug";
import EleventyExtensionMap from "./EleventyExtensionMap.js";
import TemplateData from "./Data/TemplateData.js";
import TemplateGlob from "./TemplateGlob.js";
import TemplatePassthroughManager from "./TemplatePassthroughManager.js";
import EleventyBaseError from "./Errors/EleventyBaseError.js";
import checkPassthroughCopyBehavior from "./Util/PassthroughCopyBehaviorCheck.js";
const debug = debugUtil("Eleventy:EleventyFiles");
class EleventyFilesError extends EleventyBaseError {}
class EleventyFiles {
constructor(formats, eleventyConfig) {
if (!eleventyConfig) {
throw new EleventyFilesError("Missing `eleventyConfig`` argument.");
}
this.eleventyConfig = eleventyConfig;
this.config = eleventyConfig.getConfig();
this.aggregateBench = this.config.benchmarkManager.get("Aggregate");
this.formats = formats;
this.eleventyIgnoreContent = false;
}
get dirs() {
return this.eleventyConfig.directories;
}
get inputDir() {
return this.dirs.input;
}
get outputDir() {
return this.dirs.output;
}
get includesDir() {
return this.dirs.includes;
}
get layoutsDir() {
return this.dirs.layouts;
}
get dataDir() {
return this.dirs.data;
}
// Backwards compat
getDataDir() {
return this.dataDir;
}
setFileSystemSearch(fileSystemSearch) {
this.fileSystemSearch = fileSystemSearch;
}
init() {
if (this.dirs.inputFile || this.dirs.inputGlob) {
this.templateGlobs = TemplateGlob.map([this.dirs.inputFile || this.dirs.inputGlob]);
} else {
// Input is a directory
this.templateGlobs = this.extensionMap.getGlobs(this.inputDir);
}
this.initPassthroughManager();
this.setupGlobs();
}
get validTemplateGlobs() {
if (!this._validTemplateGlobs) {
let globs;
// Input is a file
if (this.inputFile) {
globs = this.templateGlobs;
} else {
// input is a directory
globs = this.extensionMap.getValidGlobs(this.inputDir);
}
this._validTemplateGlobs = globs;
}
return this._validTemplateGlobs;
}
get passthroughGlobs() {
let paths = new Set();
// stuff added in addPassthroughCopy()
for (let path of this.passthroughManager.getConfigPathGlobs()) {
paths.add(path);
}
// non-template language extensions
for (let path of this.extensionMap.getPassthroughCopyGlobs(this.inputDir)) {
paths.add(path);
}
return Array.from(paths);
}
restart() {
this.passthroughManager.reset();
this.setupGlobs();
this._glob = null;
}
/* For testing */
_setConfig(config) {
if (!config.ignores) {
config.ignores = new Set();
config.ignores.add("**/node_modules/**");
}
this.config = config;
}
/* Set command root for local project paths */
// This is only used by tests
_setLocalPathRoot(dir) {
this.localPathRoot = dir;
}
set extensionMap(extensionMap) {
this._extensionMap = extensionMap;
}
get extensionMap() {
// for tests
if (!this._extensionMap) {
this._extensionMap = new EleventyExtensionMap(this.eleventyConfig);
this._extensionMap.setFormats(this.formats);
this._extensionMap.config = this.eleventyConfig;
}
return this._extensionMap;
}
setRunMode(runMode) {
this.runMode = runMode;
}
initPassthroughManager() {
let mgr = new TemplatePassthroughManager(this.eleventyConfig);
mgr.setRunMode(this.runMode);
mgr.extensionMap = this.extensionMap;
mgr.setFileSystemSearch(this.fileSystemSearch);
this.passthroughManager = mgr;
}
getPassthroughManager() {
return this.passthroughManager;
}
setPassthroughManager(mgr) {
mgr.extensionMap = this.extensionMap;
this.passthroughManager = mgr;
}
set templateData(templateData) {
this._templateData = templateData;
}
get templateData() {
if (!this._templateData) {
this._templateData = new TemplateData(this.eleventyConfig);
}
return this._templateData;
}
setupGlobs() {
this.fileIgnores = this.getIgnores();
this.extraIgnores = this._getIncludesAndDataDirs();
this.uniqueIgnores = this.getIgnoreGlobs();
// Conditional added for tests that dont have a config
if (this.config?.events) {
this.config.events.emit("eleventy.ignores", this.uniqueIgnores);
}
this.normalizedTemplateGlobs = this.templateGlobs;
}
getIgnoreGlobs() {
let uniqueIgnores = new Set();
for (let ignore of this.fileIgnores) {
uniqueIgnores.add(ignore);
}
for (let ignore of this.extraIgnores) {
uniqueIgnores.add(ignore);
}
// Placing the config ignores last here is important to the tests
for (let ignore of this.config.ignores) {
uniqueIgnores.add(TemplateGlob.normalizePath(this.localPathRoot || ".", ignore));
}
return Array.from(uniqueIgnores);
}
static getFileIgnores(ignoreFiles) {
if (!Array.isArray(ignoreFiles)) {
ignoreFiles = [ignoreFiles];
}
let ignores = [];
for (let ignorePath of ignoreFiles) {
ignorePath = TemplatePath.normalize(ignorePath);
let dir = TemplatePath.getDirFromFilePath(ignorePath);
if (fs.existsSync(ignorePath) && fs.statSync(ignorePath).size > 0) {
let ignoreContent = fs.readFileSync(ignorePath, "utf8");
ignores = ignores.concat(EleventyFiles.normalizeIgnoreContent(dir, ignoreContent));
}
}
ignores.forEach((path) => debug(`${ignoreFiles} ignoring: ${path}`));
return ignores;
}
static normalizeIgnoreContent(dir, ignoreContent) {
let ignores = [];
if (ignoreContent) {
ignores = ignoreContent
.split("\n")
.map((line) => {
return line.trim();
})
.filter((line) => {
if (line.charAt(0) === "!") {
debug(
">>> When processing .gitignore/.eleventyignore, Eleventy does not currently support negative patterns but encountered one:",
);
debug(">>>", line);
debug("Follow along at https://github.com/11ty/eleventy/issues/693 to track support.");
}
// empty lines or comments get filtered out
return line.length > 0 && line.charAt(0) !== "#" && line.charAt(0) !== "!";
})
.map((line) => {
let path = TemplateGlob.normalizePath(dir, "/", line);
path = TemplatePath.addLeadingDotSlash(TemplatePath.relativePath(path));
try {
// Note these folders must exist to get /** suffix
let stat = fs.statSync(path);
if (stat.isDirectory()) {
return path + "/**";
}
return path;
} catch (e) {
return path;
}
});
}
return ignores;
}
setEleventyIgnoreContent(content) {
this.eleventyIgnoreContent = content;
}
getIgnores() {
let files = new Set();
for (let ignore of EleventyFiles.getFileIgnores(this.getIgnoreFiles())) {
files.add(ignore);
}
// testing API
if (this.eleventyIgnoreContent !== false) {
files.add(this.eleventyIgnoreContent);
}
// ignore output dir (unless this excludes all input)
// input: . and output: . (skip)
// input: ./content and output . (skip)
// input: . and output: ./_site (add)
if (!this.inputDir.startsWith(this.outputDir)) {
// both are already normalized in 3.0
files.add(TemplateGlob.map(this.outputDir + "/**"));
}
return Array.from(files);
}
getIgnoreFiles() {
let ignoreFiles = new Set();
let rootDirectory = this.localPathRoot || ".";
if (this.config.useGitIgnore) {
ignoreFiles.add(TemplatePath.join(rootDirectory, ".gitignore"));
}
if (this.eleventyIgnoreContent === false) {
let absoluteInputDir = TemplatePath.absolutePath(this.inputDir);
ignoreFiles.add(TemplatePath.join(rootDirectory, ".eleventyignore"));
if (rootDirectory !== absoluteInputDir) {
ignoreFiles.add(TemplatePath.join(this.inputDir, ".eleventyignore"));
}
}
return Array.from(ignoreFiles);
}
/* Backwards compat */
getIncludesDir() {
return this.includesDir;
}
/* Backwards compat */
getLayoutsDir() {
return this.layoutsDir;
}
getFileGlobs() {
return this.normalizedTemplateGlobs;
}
getRawFiles() {
return this.templateGlobs;
}
async getWatchPathCache() {
// Issue #1325: make sure passthrough copy files are not included here
if (!this.pathCache) {
throw new Error("Watching requires `.getFiles()` to be called first in EleventyFiles");
}
let ret = [];
// Filter out the passthrough copy paths.
for (let path of this.pathCache) {
if (
this.extensionMap.isFullTemplateFilePath(path) &&
(await this.extensionMap.shouldSpiderJavaScriptDependencies(path))
) {
ret.push(path);
}
}
return ret;
}
_globSearch() {
let globs = this.getFileGlobs();
// returns a promise
debug("Searching for: %o", globs);
return this.fileSystemSearch.search("templates", globs, {
ignore: this.uniqueIgnores,
});
}
getPathsWithVirtualTemplates(paths) {
// Support for virtual templates added in 3.0
if (this.config.virtualTemplates && isPlainObject(this.config.virtualTemplates)) {
let virtualTemplates = Object.keys(this.config.virtualTemplates)
.filter((path) => {
// Filter out includes/layouts
return this.dirs.isTemplateFile(path);
})
.map((path) => {
let fullVirtualPath = this.dirs.getInputPath(path);
if (!this.extensionMap.getKey(fullVirtualPath)) {
this.eleventyConfig.logger.warn(
`The virtual template at ${fullVirtualPath} is using a template format thats not valid for your project. Your project is using: "${this.formats}". Read more about formats: https://v3.11ty.dev/docs/config/#template-formats`,
);
}
return fullVirtualPath;
});
paths = paths.concat(virtualTemplates);
// Virtual templates can not live at the same place as files on the file system!
if (paths.length !== new Set(paths).size) {
let conflicts = {};
for (let path of paths) {
if (conflicts[path]) {
throw new Error(
`A virtual template had the same path as a file on the file system: "${path}"`,
);
}
conflicts[path] = true;
}
}
}
return paths;
}
async getFiles() {
let bench = this.aggregateBench.get("Searching the file system (templates)");
bench.before();
let globResults = await this._globSearch();
let paths = TemplatePath.addLeadingDotSlashArray(globResults);
bench.after();
// Note 2.0.0-canary.19 removed a `filter` option for custom template syntax here that was unpublished and unused.
paths = this.getPathsWithVirtualTemplates(paths);
this.pathCache = paths;
return paths;
}
getFileShape(paths, filePath) {
if (!filePath) {
return;
}
if (this.isPassthroughCopyFile(paths, filePath)) {
return "copy";
}
if (this.isFullTemplateFile(paths, filePath)) {
return "template";
}
// include/layout/unknown
}
isPassthroughCopyFile(paths, filePath) {
return this.passthroughManager.isPassthroughCopyFile(paths, filePath);
}
// Assumption here that filePath is not a passthrough copy file
isFullTemplateFile(paths, filePath) {
if (!filePath) {
return false;
}
for (let path of paths) {
if (path === filePath) {
return true;
}
}
return false;
}
/* For `eleventy --watch` */
getGlobWatcherFiles() {
// TODO improvement: tie the includes and data to specific file extensions (currently using `**`)
let directoryGlobs = this._getIncludesAndDataDirs();
if (checkPassthroughCopyBehavior(this.config, this.runMode)) {
return this.validTemplateGlobs.concat(directoryGlobs);
}
// Revert to old passthroughcopy copy files behavior
return this.validTemplateGlobs.concat(this.passthroughGlobs).concat(directoryGlobs);
}
/* For `eleventy --watch` */
getGlobWatcherFilesForPassthroughCopy() {
return this.passthroughGlobs;
}
/* For `eleventy --watch` */
async getGlobWatcherTemplateDataFiles() {
let templateData = this.templateData;
return await templateData.getTemplateDataFileGlob();
}
/* For `eleventy --watch` */
// TODO this isnt great but reduces complexity avoiding using TemplateData:getLocalDataPaths for each template in the cache
async getWatcherTemplateJavaScriptDataFiles() {
let globs = this.templateData.getTemplateJavaScriptDataFileGlob();
let bench = this.aggregateBench.get("Searching the file system (watching)");
bench.before();
let results = TemplatePath.addLeadingDotSlashArray(
await this.fileSystemSearch.search("js-dependencies", globs, {
ignore: ["**/node_modules/**"],
}),
);
bench.after();
return results;
}
/* Ignored by `eleventy --watch` */
getGlobWatcherIgnores() {
// convert to format without ! since they are passed in as a separate argument to glob watcher
let entries = new Set(
this.fileIgnores.map((ignore) => TemplatePath.stripLeadingDotSlash(ignore)),
);
for (let ignore of this.config.watchIgnores) {
entries.add(TemplateGlob.normalizePath(this.localPathRoot || ".", ignore));
}
// de-duplicated
return Array.from(entries);
}
_getIncludesAndDataDirs() {
let rawPaths = new Set();
rawPaths.add(this.includesDir);
if (this.layoutsDir) {
rawPaths.add(this.layoutsDir);
}
rawPaths.add(this.dataDir);
return Array.from(rawPaths)
.filter((entry) => {
// never ignore the input directory (even if config file returns "" for these)
return entry && entry !== this.inputDir;
})
.map((entry) => {
return TemplateGlob.map(entry + "**");
});
}
}
export default EleventyFiles;

305
node_modules/@11ty/eleventy/src/EleventyServe.js generated vendored Normal file
View File

@@ -0,0 +1,305 @@
import assert from "node:assert";
import debugUtil from "debug";
import { Merge, DeepCopy, TemplatePath } from "@11ty/eleventy-utils";
import EleventyDevServer from "@11ty/eleventy-dev-server";
import EleventyBaseError from "./Errors/EleventyBaseError.js";
import ConsoleLogger from "./Util/ConsoleLogger.js";
import PathPrefixer from "./Util/PathPrefixer.js";
import checkPassthroughCopyBehavior from "./Util/PassthroughCopyBehaviorCheck.js";
import { getModulePackageJson } from "./Util/ImportJsonSync.js";
import { EleventyImport } from "./Util/Require.js";
import { isGlobMatch } from "./Util/GlobMatcher.js";
const debug = debugUtil("Eleventy:EleventyServe");
class EleventyServeConfigError extends EleventyBaseError {}
const DEFAULT_SERVER_OPTIONS = {
module: "@11ty/eleventy-dev-server",
port: 8080,
// pathPrefix: "/",
// setup: function() {},
// logger: { info: function() {}, error: function() {} }
};
class EleventyServe {
constructor() {
this.logger = new ConsoleLogger();
this._initOptionsFetched = false;
this._aliases = undefined;
this._watchedFiles = new Set();
}
get config() {
if (!this.eleventyConfig) {
throw new EleventyServeConfigError(
"You need to set the eleventyConfig property on EleventyServe.",
);
}
return this.eleventyConfig.getConfig();
}
set config(config) {
throw new Error("Its not allowed to set config on EleventyServe. Set eleventyConfig instead.");
}
setAliases(aliases) {
this._aliases = aliases;
if (this._server && "setAliases" in this._server) {
this._server.setAliases(aliases);
}
}
get eleventyConfig() {
if (!this._eleventyConfig) {
throw new EleventyServeConfigError(
"You need to set the eleventyConfig property on EleventyServe.",
);
}
return this._eleventyConfig;
}
set eleventyConfig(config) {
this._eleventyConfig = config;
if (checkPassthroughCopyBehavior(this._eleventyConfig.userConfig, "serve")) {
this._eleventyConfig.userConfig.events.on("eleventy.passthrough", ({ map }) => {
// for-free passthrough copy
this.setAliases(map);
});
}
}
// TODO directorynorm
setOutputDir(outputDir) {
// TODO check if this is different and if so, restart server (if already running)
// This applies if you change the output directory in your config file during watch/serve
this.outputDir = outputDir;
}
async getServerModule(name) {
try {
if (!name || name === DEFAULT_SERVER_OPTIONS.module) {
return EleventyDevServer;
}
// Look for peer dep in local project
let projectNodeModulesPath = TemplatePath.absolutePath("./node_modules/");
let serverPath = TemplatePath.absolutePath(projectNodeModulesPath, name);
// No references outside of the project node_modules are allowed
if (!serverPath.startsWith(projectNodeModulesPath)) {
throw new Error("Invalid node_modules name for Eleventy server instance, received:" + name);
}
let serverPackageJson = getModulePackageJson(serverPath);
// Normalize with `main` entry from
if (TemplatePath.isDirectorySync(serverPath)) {
if (serverPackageJson.main) {
serverPath = TemplatePath.absolutePath(
projectNodeModulesPath,
name,
serverPackageJson.main,
);
} else {
throw new Error(
`Eleventy server ${name} is missing a \`main\` entry in its package.json file. Traversed up from ${serverPath}.`,
);
}
}
let module = await EleventyImport(serverPath);
if (!("getServer" in module)) {
throw new Error(
`Eleventy server module requires a \`getServer\` static method. Could not find one on module: \`${name}\``,
);
}
if (serverPackageJson["11ty"]?.compatibility) {
try {
this.eleventyConfig.userConfig.versionCheck(serverPackageJson["11ty"].compatibility);
} catch (e) {
this.logger.warn(`Warning: \`${name}\` Plugin Compatibility: ${e.message}`);
}
}
return module;
} catch (e) {
this.logger.error(
"There was an error with your custom Eleventy server. Were using the default server instead.\n" +
e.message,
);
debug("Eleventy server error %o", e);
return EleventyDevServer;
}
}
get options() {
if (this._options) {
return this._options;
}
this._options = Object.assign(
{
pathPrefix: PathPrefixer.normalizePathPrefix(this.config.pathPrefix),
logger: this.logger,
},
DEFAULT_SERVER_OPTIONS,
this.config.serverOptions,
);
this._savedConfigOptions = DeepCopy({}, this.config.serverOptions);
if (!this._initOptionsFetched && this.getSetupCallback()) {
throw new Error(
"Init options have not yet been fetched in the setup callback. This probably means that `init()` has not yet been called.",
);
}
return this._options;
}
get server() {
if (!this._server) {
throw new Error("Missing server instance. Did you call .initServerInstance?");
}
return this._server;
}
async initServerInstance() {
if (this._server) {
return;
}
let serverModule = await this.getServerModule(this.options.module);
// Static method `getServer` was already checked in `getServerModule`
this._server = serverModule.getServer("eleventy-server", this.outputDir, this.options);
this.setAliases(this._aliases);
if (this._globsNeedWatching) {
this._server.watchFiles(this._watchedFiles);
this._globsNeedWatching = false;
}
}
getSetupCallback() {
let setupCallback = this.config.serverOptions.setup;
if (setupCallback && typeof setupCallback === "function") {
return setupCallback;
}
}
async #init() {
let setupCallback = this.getSetupCallback();
if (setupCallback) {
let opts = await setupCallback();
this._initOptionsFetched = true;
if (opts) {
Merge(this.options, opts);
}
}
}
async init() {
if (!this._initPromise) {
this._initPromise = this.#init();
}
return this._initPromise;
}
// Port comes in here from --port on the command line
async serve(port) {
this._commandLinePort = port;
await this.init();
await this.initServerInstance();
this.server.serve(port || this.options.port);
}
async close() {
if (this._server) {
await this._server.close();
this._server = undefined;
}
}
async sendError({ error }) {
if (this._server) {
await this.server.sendError({
error,
});
}
}
// Restart the server entirely
// We dont want to use a native `restart` method (e.g. restart() in Vite) so that
// we can correctly handle a `module` property change (changing the server type)
async restart() {
// Blow away cached options
delete this._options;
await this.close();
// saved --port in `serve()`
await this.serve(this._commandLinePort);
// rewatch the saved watched files (passthrough copy)
if ("watchFiles" in this.server) {
this.server.watchFiles(this._watchedFiles);
}
}
// checkPassthroughCopyBehavior check is called upstream in Eleventy.js
// TODO globs are not removed from watcher
watchPassthroughCopy(globs) {
this._watchedFiles = globs;
if (this._server && "watchFiles" in this.server) {
this.server.watchFiles(globs);
this._globsNeedWatching = false;
} else {
this._globsNeedWatching = true;
}
}
isEmulatedPassthroughCopyMatch(filepath) {
return isGlobMatch(filepath, this._watchedFiles);
}
hasOptionsChanged() {
try {
assert.deepStrictEqual(this.config.serverOptions, this._savedConfigOptions);
return false;
} catch (e) {
return true;
}
}
// Live reload the server
async reload(reloadEvent = {}) {
if (!this._server) {
return;
}
// Restart the server if the options have changed
if (this.hasOptionsChanged()) {
debug("Server options changed, were restarting the server");
await this.restart();
} else {
await this.server.reload(reloadEvent);
}
}
}
export default EleventyServe;

131
node_modules/@11ty/eleventy/src/EleventyWatch.js generated vendored Executable file
View File

@@ -0,0 +1,131 @@
import { TemplatePath } from "@11ty/eleventy-utils";
import PathNormalizer from "./Util/PathNormalizer.js";
/* Decides when to watch and in what mode to watch
* Incremental builds dont batch changes, they queue.
* Nonincremental builds batch.
*/
class EleventyWatch {
constructor() {
this.incremental = false;
this.isActive = false;
this.activeQueue = [];
}
isBuildRunning() {
return this.isActive;
}
setBuildRunning() {
this.isActive = true;
// pop waiting queue into the active queue
this.activeQueue = this.popNextActiveQueue();
}
setBuildFinished() {
this.isActive = false;
this.activeQueue = [];
}
getIncrementalFile() {
if (this.incremental) {
return this.activeQueue.length ? this.activeQueue[0] : false;
}
return false;
}
/* Returns the changed files currently being operated on in the current `watch` build
* Works with or without incremental (though in incremental only one file per time will be processed)
*/
getActiveQueue() {
if (!this.isActive) {
return [];
} else if (this.incremental && this.activeQueue.length === 0) {
return [];
} else if (this.incremental) {
return [this.activeQueue[0]];
}
return this.activeQueue;
}
_queueMatches(file) {
let filterCallback;
if (typeof file === "function") {
filterCallback = file;
} else {
filterCallback = (path) => path === file;
}
return this.activeQueue.filter(filterCallback);
}
hasAllQueueFiles(file) {
return (
this.activeQueue.length > 0 && this.activeQueue.length === this._queueMatches(file).length
);
}
hasQueuedFile(file) {
if (file) {
return this._queueMatches(file).length > 0;
}
return false;
}
hasQueuedFiles(files) {
for (const file of files) {
if (this.hasQueuedFile(file)) {
return true;
}
}
return false;
}
get pendingQueue() {
if (!this._queue) {
this._queue = [];
}
return this._queue;
}
set pendingQueue(value) {
this._queue = value;
}
addToPendingQueue(path) {
if (path) {
path = PathNormalizer.normalizeSeperator(TemplatePath.addLeadingDotSlash(path));
this.pendingQueue.push(path);
}
}
getPendingQueueSize() {
return this.pendingQueue.length;
}
getPendingQueue() {
return this.pendingQueue;
}
getActiveQueueSize() {
return this.activeQueue.length;
}
// returns array
popNextActiveQueue() {
if (this.incremental) {
return this.pendingQueue.length ? [this.pendingQueue.shift()] : [];
}
let ret = this.pendingQueue.slice();
this.pendingQueue = [];
return ret;
}
}
export default EleventyWatch;

164
node_modules/@11ty/eleventy/src/EleventyWatchTargets.js generated vendored Normal file
View File

@@ -0,0 +1,164 @@
import { TemplatePath } from "@11ty/eleventy-utils";
import { DepGraph } from "dependency-graph";
import JavaScriptDependencies from "./Util/JavaScriptDependencies.js";
import eventBus from "./EventBus.js";
class EleventyWatchTargets {
#templateConfig;
constructor(templateConfig) {
this.targets = new Set();
this.dependencies = new Set();
this.newTargets = new Set();
this.isEsm = false;
this.graph = new DepGraph();
this.#templateConfig = templateConfig;
}
setProjectUsingEsm(isEsmProject) {
this.isEsm = !!isEsmProject;
}
isJavaScriptDependency(path) {
return this.dependencies.has(path);
}
reset() {
this.newTargets = new Set();
}
isWatched(target) {
return this.targets.has(target);
}
addToDependencyGraph(parent, deps) {
if (!this.graph.hasNode(parent)) {
this.graph.addNode(parent);
}
for (let dep of deps) {
if (!this.graph.hasNode(dep)) {
this.graph.addNode(dep);
}
this.graph.addDependency(parent, dep);
}
}
uses(parent, dep) {
return this.getDependenciesOf(parent).includes(dep);
}
getDependenciesOf(parent) {
if (!this.graph.hasNode(parent)) {
return [];
}
return this.graph.dependenciesOf(parent);
}
getDependantsOf(child) {
if (!this.graph.hasNode(child)) {
return [];
}
return this.graph.dependantsOf(child);
}
addRaw(targets, isDependency) {
for (let target of targets) {
let path = TemplatePath.addLeadingDotSlash(target);
if (!this.isWatched(path)) {
this.newTargets.add(path);
}
this.targets.add(path);
if (isDependency) {
this.dependencies.add(path);
}
}
}
static normalize(targets) {
if (!targets) {
return [];
} else if (Array.isArray(targets)) {
return targets;
}
return [targets];
}
// add only a target
add(targets) {
this.addRaw(EleventyWatchTargets.normalize(targets));
}
static normalizeToGlobs(targets) {
return EleventyWatchTargets.normalize(targets).map((entry) =>
TemplatePath.convertToRecursiveGlobSync(entry),
);
}
addAndMakeGlob(targets) {
this.addRaw(EleventyWatchTargets.normalizeToGlobs(targets));
}
// add only a targets dependencies
async addDependencies(targets, filterCallback) {
if (this.#templateConfig && !this.#templateConfig.shouldSpiderJavaScriptDependencies()) {
return;
}
targets = EleventyWatchTargets.normalize(targets);
let deps = await JavaScriptDependencies.getDependencies(targets, this.isEsm);
if (filterCallback) {
deps = deps.filter(filterCallback);
}
for (let target of targets) {
this.addToDependencyGraph(target, deps);
}
this.addRaw(deps, true);
}
setWriter(templateWriter) {
this.writer = templateWriter;
}
clearImportCacheFor(filePathArray) {
let paths = new Set();
for (const filePath of filePathArray) {
paths.add(filePath);
// Delete from require cache so that updates to the module are re-required
let importsTheChangedFile = this.getDependantsOf(filePath);
for (let dep of importsTheChangedFile) {
paths.add(dep);
}
let isImportedInTheChangedFile = this.getDependenciesOf(filePath);
for (let dep of isImportedInTheChangedFile) {
paths.add(dep);
}
// Use GlobalDependencyMap
if (this.#templateConfig) {
for (let dep of this.#templateConfig.usesGraph.getDependantsFor(filePath)) {
paths.add(dep);
}
}
}
eventBus.emit("eleventy.importCacheReset", paths);
}
getNewTargetsSinceLastReset() {
return Array.from(this.newTargets);
}
getTargets() {
return Array.from(this.targets);
}
}
export default EleventyWatchTargets;

338
node_modules/@11ty/eleventy/src/Engines/Custom.js generated vendored Normal file
View File

@@ -0,0 +1,338 @@
import TemplateEngine from "./TemplateEngine.js";
import getJavaScriptData from "../Util/GetJavaScriptData.js";
import eventBus from "../EventBus.js";
let lastModifiedFile = undefined;
eventBus.on("eleventy.resourceModified", (path) => {
lastModifiedFile = path;
});
class CustomEngine extends TemplateEngine {
constructor(name, eleventyConfig) {
super(name, eleventyConfig);
this.entry = this.getExtensionMapEntry();
this.needsInit = "init" in this.entry && typeof this.entry.init === "function";
this._defaultEngine = undefined;
// Enable cacheability for this template
if (this.entry?.compileOptions?.cache) {
this.cacheable = this.entry.compileOptions.cache;
} else if (this.needsToReadFileContents()) {
this.cacheable = true;
}
}
getExtensionMapEntry() {
if ("extensionMap" in this.config) {
let name = this.name.toLowerCase();
// Iterates over only the user config `addExtension` entries
for (let entry of this.config.extensionMap) {
let entryKey = (entry.aliasKey || entry.key || "").toLowerCase();
if (entryKey === name) {
return entry;
}
}
}
throw Error(
`Could not find a custom extension for ${this.name}. Did you add it to your config file?`,
);
}
setDefaultEngine(defaultEngine) {
this._defaultEngine = defaultEngine;
}
async getInstanceFromInputPath(inputPath) {
if (
"getInstanceFromInputPath" in this.entry &&
typeof this.entry.getInstanceFromInputPath === "function"
) {
// returns Promise
return this.entry.getInstanceFromInputPath(inputPath);
}
// aliased upstream type
if (
this._defaultEngine &&
"getInstanceFromInputPath" in this._defaultEngine &&
typeof this._defaultEngine.getInstanceFromInputPath === "function"
) {
// returns Promise
return this._defaultEngine.getInstanceFromInputPath(inputPath);
}
return false;
}
/**
* Whether to use the module loader directly
*
* @override
*/
useJavaScriptImport() {
if ("useJavaScriptImport" in this.entry) {
return this.entry.useJavaScriptImport;
}
if (
this._defaultEngine &&
"useJavaScriptImport" in this._defaultEngine &&
typeof this._defaultEngine.useJavaScriptImport === "function"
) {
return this._defaultEngine.useJavaScriptImport();
}
return false;
}
/**
* @override
*/
needsToReadFileContents() {
if ("read" in this.entry) {
return this.entry.read;
}
// Handle aliases to `11ty.js` templates, avoid reading files in the alias, see #2279
// Here, we are short circuiting fallback to defaultRenderer, does not account for compile
// functions that call defaultRenderer explicitly
if (this._defaultEngine && "needsToReadFileContents" in this._defaultEngine) {
return this._defaultEngine.needsToReadFileContents();
}
return true;
}
// If we init from multiple places, wait for the first init to finish before continuing on.
async _runningInit() {
if (this.needsInit) {
if (!this._initing) {
this._initBench = this.benchmarks.aggregate.get(`Engine (${this.name}) Init`);
this._initBench.before();
this._initing = this.entry.init.bind({
config: this.config,
bench: this.benchmarks.aggregate,
})();
}
await this._initing;
this.needsInit = false;
if (this._initBench) {
this._initBench.after();
this._initBench = undefined;
}
}
}
async getExtraDataFromFile(inputPath) {
if (this.entry.getData === false) {
return;
}
if (!("getData" in this.entry)) {
// Handle aliases to `11ty.js` templates, use upstream default engine data fetch, see #2279
if (this._defaultEngine && "getExtraDataFromFile" in this._defaultEngine) {
return this._defaultEngine.getExtraDataFromFile(inputPath);
}
return;
}
await this._runningInit();
if (typeof this.entry.getData === "function") {
let dataBench = this.benchmarks.aggregate.get(
`Engine (${this.name}) Get Data From File (Function)`,
);
dataBench.before();
let data = this.entry.getData(inputPath);
dataBench.after();
return data;
}
let keys = new Set();
if (this.entry.getData === true) {
keys.add("data");
} else if (Array.isArray(this.entry.getData)) {
for (let key of this.entry.getData) {
keys.add(key);
}
}
let dataBench = this.benchmarks.aggregate.get(`Engine (${this.name}) Get Data From File`);
dataBench.before();
let inst = await this.getInstanceFromInputPath(inputPath);
if (inst === false) {
dataBench.after();
return Promise.reject(
new Error(
`\`getInstanceFromInputPath\` callback missing from '${this.name}' template engine plugin. It is required when \`getData\` is in use. You can set \`getData: false\` to opt-out of this.`,
),
);
}
// override keys set at the plugin level in the individual template
if (inst.eleventyDataKey) {
keys = new Set(inst.eleventyDataKey);
}
let mixins;
if (this.config) {
// Object.assign usage: see TemplateRenderCustomTest.js: `JavaScript functions should not be mutable but not *that* mutable`
mixins = Object.assign({}, this.config.javascriptFunctions);
}
let promises = [];
for (let key of keys) {
promises.push(
getJavaScriptData(inst, inputPath, key, {
mixins,
isObjectRequired: key === "data",
}),
);
}
let results = await Promise.all(promises);
let data = {};
for (let result of results) {
Object.assign(data, result);
}
dataBench.after();
return data;
}
async compile(str, inputPath, ...args) {
await this._runningInit();
let defaultCompilationFn;
if (this._defaultEngine) {
defaultCompilationFn = async (data) => {
const renderFn = await this._defaultEngine.compile(str, inputPath, ...args);
return renderFn(data);
};
}
// Fall back to default compiler if the user does not provide their own
if (!this.entry.compile) {
if (defaultCompilationFn) {
return defaultCompilationFn;
} else {
throw new Error(
`Missing \`compile\` property for custom template syntax definition eleventyConfig.addExtension("${this.name}"). This is not necessary when aliasing to an existing template syntax.`,
);
}
}
// TODO generalize this (look at JavaScript.js)
let fn = this.entry.compile.bind({
config: this.config,
addDependencies: (from, toArray = []) => {
this.config.uses.addDependency(from, toArray);
},
defaultRenderer: defaultCompilationFn, // bind defaultRenderer to compile function
})(str, inputPath);
// Support `undefined` to skip compile/render
if (fn) {
// Bind defaultRenderer to render function
if ("then" in fn && typeof fn.then === "function") {
// Promise, wait to bind
return fn.then((fn) => {
if (typeof fn === "function") {
return fn.bind({
defaultRenderer: defaultCompilationFn,
});
}
return fn;
});
} else if ("bind" in fn && typeof fn.bind === "function") {
return fn.bind({
defaultRenderer: defaultCompilationFn,
});
}
}
return fn;
}
get defaultTemplateFileExtension() {
return this.entry.outputFileExtension ?? "html";
}
// Whether or not to wrap in Eleventy layouts
useLayouts() {
// TODO future change fallback to `this.defaultTemplateFileExtension === "html"`
return this.entry.useLayouts ?? true;
}
hasDependencies(inputPath) {
if (this.config.uses.getDependencies(inputPath) === false) {
return false;
}
return true;
}
isFileRelevantTo(inputPath, comparisonFile, includeLayouts) {
return this.config.uses.isFileRelevantTo(inputPath, comparisonFile, includeLayouts);
}
getCompileCacheKey(str, inputPath) {
// Return this separately so we know whether or not to use the cached version
// but still return a key to cache this new render for next time
let useCache = !this.isFileRelevantTo(inputPath, lastModifiedFile, false);
if (this.entry.compileOptions && "getCacheKey" in this.entry.compileOptions) {
if (typeof this.entry.compileOptions.getCacheKey !== "function") {
throw new Error(
`\`compileOptions.getCacheKey\` must be a function in addExtension for the ${this.name} type`,
);
}
return {
useCache,
key: this.entry.compileOptions.getCacheKey(str, inputPath),
};
}
let { key } = super.getCompileCacheKey(str, inputPath);
return {
useCache,
key,
};
}
permalinkNeedsCompilation(/*str*/) {
if (this.entry.compileOptions && "permalink" in this.entry.compileOptions) {
let p = this.entry.compileOptions.permalink;
if (p === "raw") {
return false;
}
// permalink: false is aliased to permalink: () => false
if (p === false) {
return () => false;
}
return this.entry.compileOptions.permalink;
}
// Breaking: default changed from `true` to `false` in 3.0.0-alpha.13
return false;
}
static shouldSpiderJavaScriptDependencies(entry) {
if (entry.compileOptions && "spiderJavaScriptDependencies" in entry.compileOptions) {
return entry.compileOptions.spiderJavaScriptDependencies;
}
return false;
}
}
export default CustomEngine;

View File

@@ -0,0 +1,34 @@
import { RetrieveGlobals } from "node-retrieve-globals";
// `javascript` Front Matter Type
export default function (frontMatterCode, context = {}) {
let { filePath } = context;
// context.language would be nice as a guard, but was unreliable
if (frontMatterCode.trimStart().startsWith("{")) {
return context.engines.jsLegacy.parse(frontMatterCode, context);
}
let vm = new RetrieveGlobals(frontMatterCode, {
filePath,
// ignored if vm.Module is stable (or --experimental-vm-modules)
transformEsmImports: true,
});
// Future warning until vm.Module is stable:
// If the frontMatterCode uses `import` this uses the `experimentalModuleApi`
// option in node-retrieve-globals to workaround https://github.com/zachleat/node-retrieve-globals/issues/2
let data = {
page: {
// Theoretically fileSlug and filePathStem could be added here but require extensionMap
inputPath: filePath,
},
};
// this is async, but its handled in Eleventy upstream.
return vm.getGlobalContext(data, {
reuseGlobal: true,
dynamicImport: true,
// addRequire: true,
});
}

28
node_modules/@11ty/eleventy/src/Engines/Html.js generated vendored Normal file
View File

@@ -0,0 +1,28 @@
import TemplateEngine from "./TemplateEngine.js";
class Html extends TemplateEngine {
constructor(name, eleventyConfig) {
super(name, eleventyConfig);
this.cacheable = true;
}
async compile(str, inputPath, preTemplateEngine) {
if (preTemplateEngine) {
let engine = await this.engineManager.getEngine(preTemplateEngine, this.extensionMap);
let fnReady = engine.compile(str, inputPath);
return async function (data) {
let fn = await fnReady;
return fn(data);
};
}
return function () {
// do nothing with data if parseHtmlWith is falsy
return str;
};
}
}
export default Html;

237
node_modules/@11ty/eleventy/src/Engines/JavaScript.js generated vendored Normal file
View File

@@ -0,0 +1,237 @@
import { TemplatePath, isPlainObject } from "@11ty/eleventy-utils";
import TemplateEngine from "./TemplateEngine.js";
import EleventyBaseError from "../Errors/EleventyBaseError.js";
import getJavaScriptData from "../Util/GetJavaScriptData.js";
import EventBusUtil from "../Util/EventBusUtil.js";
import { EleventyImport } from "../Util/Require.js";
import { augmentFunction, augmentObject } from "./Util/ContextAugmenter.js";
class JavaScriptTemplateNotDefined extends EleventyBaseError {}
class JavaScript extends TemplateEngine {
constructor(name, templateConfig) {
super(name, templateConfig);
this.instances = {};
this.cacheable = false;
EventBusUtil.soloOn("eleventy.templateModified", (inputPath, metadata = {}) => {
let { usedByDependants, relevantLayouts } = metadata;
// Remove from cached instances when modified
let instancesToDelete = [
inputPath,
...(usedByDependants || []),
...(relevantLayouts || []),
].map((entry) => TemplatePath.addLeadingDotSlash(entry));
for (let inputPath of instancesToDelete) {
if (inputPath in this.instances) {
delete this.instances[inputPath];
}
}
});
}
normalize(result) {
if (Buffer.isBuffer(result)) {
return result.toString();
}
return result;
}
// String, Buffer, Promise
// Function, Class
// Object
// Module
_getInstance(mod) {
let noop = function () {
return "";
};
let originalModData = mod?.data;
if (typeof mod === "object" && mod.default && this.eleventyConfig.getIsProjectUsingEsm()) {
mod = mod.default;
}
if (typeof mod === "string" || mod instanceof Buffer || mod.then) {
return { render: () => mod };
} else if (typeof mod === "function") {
if (mod.prototype?.data || mod.prototype?.render) {
if (!("render" in mod.prototype)) {
mod.prototype.render = noop;
}
if (!("data" in mod.prototype) && !mod.data && originalModData) {
mod.prototype.data = originalModData;
}
return new mod();
} else {
return {
...(originalModData ? { data: originalModData } : undefined),
render: mod,
};
}
} else if ("data" in mod || "render" in mod) {
if (!mod.render) {
mod.render = noop;
}
if (!mod.data && originalModData) {
mod.data = originalModData;
}
return mod;
}
}
async #getInstanceFromInputPath(inputPath) {
let mod;
let relativeInputPath =
this.eleventyConfig.directories.getInputPathRelativeToInputDirectory(inputPath);
if (this.eleventyConfig.userConfig.isVirtualTemplate(relativeInputPath)) {
mod = this.eleventyConfig.userConfig.virtualTemplates[relativeInputPath].content;
} else {
let isEsm = this.eleventyConfig.getIsProjectUsingEsm();
mod = await EleventyImport(inputPath, isEsm ? "esm" : "cjs");
}
let inst = this._getInstance(mod);
if (inst) {
this.instances[inputPath] = inst;
} else {
throw new JavaScriptTemplateNotDefined(
`No JavaScript template returned from ${inputPath}. Did you assign module.exports (CommonJS) or export (ESM)?`,
);
}
return inst;
}
async getInstanceFromInputPath(inputPath) {
if (!this.instances[inputPath]) {
this.instances[inputPath] = this.#getInstanceFromInputPath(inputPath);
}
return this.instances[inputPath];
}
/**
* JavaScript files defer to the module loader rather than read the files to strings
*
* @override
*/
needsToReadFileContents() {
return false;
}
/**
* Use the module loader directly
*
* @override
*/
useJavaScriptImport() {
return true;
}
async getExtraDataFromFile(inputPath) {
let inst = await this.getInstanceFromInputPath(inputPath);
return getJavaScriptData(inst, inputPath);
}
getJavaScriptFunctions(inst) {
let fns = {};
let configFns = this.config.javascriptFunctions;
for (let key in configFns) {
// prefer pre-existing `page` javascriptFunction, if one exists
fns[key] = augmentFunction(configFns[key], {
source: inst,
overwrite: false,
});
}
return fns;
}
// Backwards compat
static wrapJavaScriptFunction(inst, fn) {
return augmentFunction(fn, {
source: inst,
});
}
addExportsToBundles(inst, url) {
let cfg = this.eleventyConfig.userConfig;
if (!("getBundleManagers" in cfg)) {
return;
}
let managers = cfg.getBundleManagers();
for (let name in managers) {
let mgr = managers[name];
let key = mgr.getBundleExportKey();
if (!key) {
continue;
}
if (typeof inst[key] === "string") {
// export const css = ``;
mgr.addToPage(url, inst[key]);
} else if (isPlainObject(inst[key])) {
if (typeof inst[key][name] === "string") {
// Object with bundle names:
// export const bundle = {
// css: ``
// };
mgr.addToPage(url, inst[key][name]);
} else if (isPlainObject(inst[key][name])) {
// Object with bucket names:
// export const bundle = {
// css: {
// default: ``
// }
// };
for (let bucketName in inst[key][name]) {
mgr.addToPage(url, inst[key][name][bucketName], bucketName);
}
}
}
}
}
async compile(str, inputPath) {
let inst;
if (str) {
// When str has a value, it's being used for permalinks in data
inst = this._getInstance(str);
} else {
// For normal templates, str will be falsy.
inst = await this.getInstanceFromInputPath(inputPath);
}
if (inst?.render) {
return function (data = {}) {
// TODO does this do anything meaningful for non-classes?
// `inst` should have a normalized `render` function from _getInstance
// Map exports to bundles
if (data.page?.url) {
this.addExportsToBundles(inst, data.page.url);
}
augmentObject(inst, {
source: data,
overwrite: false,
});
Object.assign(inst, this.getJavaScriptFunctions(inst));
return this.normalize(inst.render.call(inst, data));
}.bind(this);
}
}
static shouldSpiderJavaScriptDependencies() {
return true;
}
}
export default JavaScript;

326
node_modules/@11ty/eleventy/src/Engines/Liquid.js generated vendored Normal file
View File

@@ -0,0 +1,326 @@
import moo from "moo";
import { Tokenizer, TokenKind, evalToken, Liquid as LiquidJs } from "liquidjs";
import { TemplatePath } from "@11ty/eleventy-utils";
// import debugUtil from "debug";
import TemplateEngine from "./TemplateEngine.js";
import { augmentObject } from "./Util/ContextAugmenter.js";
// const debug = debugUtil("Eleventy:Liquid");
class Liquid extends TemplateEngine {
static argumentLexerOptions = {
number: /[0-9]+\.*[0-9]*/,
doubleQuoteString: /"(?:\\["\\]|[^\n"\\])*"/,
singleQuoteString: /'(?:\\['\\]|[^\n'\\])*'/,
keyword: /[a-zA-Z0-9.\-_]+/,
"ignore:whitespace": /[, \t]+/, // includes comma separator
};
constructor(name, eleventyConfig) {
super(name, eleventyConfig);
this.liquidOptions = this.config.liquidOptions || {};
this.setLibrary(this.config.libraryOverrides.liquid);
this.argLexer = moo.compile(Liquid.argumentLexerOptions);
this.cacheable = true;
}
setLibrary(override) {
// warning, the include syntax supported here does not exactly match what Jekyll uses.
this.liquidLib = override || new LiquidJs(this.getLiquidOptions());
this.setEngineLib(this.liquidLib);
this.addFilters(this.config.liquidFilters);
// TODO these all go to the same place (addTag), add warnings for overwrites
this.addCustomTags(this.config.liquidTags);
this.addAllShortcodes(this.config.liquidShortcodes);
this.addAllPairedShortcodes(this.config.liquidPairedShortcodes);
}
getLiquidOptions() {
let defaults = {
root: [this.dirs.includes, this.dirs.input], // supplemented in compile with inputPath below
extname: ".liquid",
strictFilters: true,
// TODO?
// cache: true,
};
let options = Object.assign(defaults, this.liquidOptions || {});
// debug("Liquid constructor options: %o", options);
return options;
}
static wrapFilter(name, fn) {
/**
* @this {object}
*/
return function (...args) {
// Set this.eleventy and this.page
if (typeof this.context?.get === "function") {
augmentObject(this, {
source: this.context,
getter: (key, context) => context.get([key]),
lazy: this.context.strictVariables,
});
}
// We *dont* wrap this in an EleventyFilterError because Liquid has a better error message with line/column information in the template
return fn.call(this, ...args);
};
}
// Shortcodes
static normalizeScope(context) {
let obj = {};
if (context) {
obj.ctx = context; // Full context available on `ctx`
// Set this.eleventy and this.page
augmentObject(obj, {
source: context,
getter: (key, context) => context.get([key]),
lazy: context.strictVariables,
});
}
return obj;
}
addCustomTags(tags) {
for (let name in tags) {
this.addTag(name, tags[name]);
}
}
addFilters(filters) {
for (let name in filters) {
this.addFilter(name, filters[name]);
}
}
addFilter(name, filter) {
this.liquidLib.registerFilter(name, Liquid.wrapFilter(name, filter));
}
addTag(name, tagFn) {
let tagObj;
if (typeof tagFn === "function") {
tagObj = tagFn(this.liquidLib);
} else {
throw new Error(
"Liquid.addTag expects a callback function to be passed in: addTag(name, function(liquidEngine) { return { parse: …, render: … } })",
);
}
this.liquidLib.registerTag(name, tagObj);
}
addAllShortcodes(shortcodes) {
for (let name in shortcodes) {
this.addShortcode(name, shortcodes[name]);
}
}
addAllPairedShortcodes(shortcodes) {
for (let name in shortcodes) {
this.addPairedShortcode(name, shortcodes[name]);
}
}
static parseArguments(lexer, str) {
let argArray = [];
if (!lexer) {
lexer = moo.compile(Liquid.argumentLexerOptions);
}
if (typeof str === "string") {
lexer.reset(str);
let arg = lexer.next();
while (arg) {
/*{
type: 'doubleQuoteString',
value: '"test 2"',
text: '"test 2"',
toString: [Function: tokenToString],
offset: 0,
lineBreaks: 0,
line: 1,
col: 1 }*/
if (arg.type.indexOf("ignore:") === -1) {
// Push the promise into an array instead of awaiting it here.
// This forces the promises to run in order with the correct scope value for each arg.
// Otherwise they run out of order and can lead to undefined values for arguments in layout template shortcodes.
// console.log( arg.value, scope, engine );
argArray.push(arg.value);
}
arg = lexer.next();
}
}
return argArray;
}
static parseArgumentsBuiltin(args) {
let tokenizer = new Tokenizer(args);
let parsedArgs = [];
let value = tokenizer.readValue();
while (value) {
parsedArgs.push(value);
tokenizer.skipBlank();
if (tokenizer.peek() === ",") {
tokenizer.advance();
}
value = tokenizer.readValue();
}
tokenizer.end();
return parsedArgs;
}
addShortcode(shortcodeName, shortcodeFn) {
let _t = this;
this.addTag(shortcodeName, function (liquidEngine) {
return {
parse(tagToken) {
this.name = tagToken.name;
if (_t.config.liquidParameterParsing === "builtin") {
this.orderedArgs = Liquid.parseArgumentsBuiltin(tagToken.args);
// note that Liquid does have a Hash class for name-based argument parsing but offers no easy to support both modes in one class
} else {
this.legacyArgs = tagToken.args;
}
},
render: function* (ctx) {
let argArray = [];
if (this.legacyArgs) {
let rawArgs = Liquid.parseArguments(_t.argLexer, this.legacyArgs);
for (let arg of rawArgs) {
let b = yield liquidEngine.evalValue(arg, ctx);
argArray.push(b);
}
} else if (this.orderedArgs) {
for (let arg of this.orderedArgs) {
let b = yield evalToken(arg, ctx);
argArray.push(b);
}
}
let ret = yield shortcodeFn.call(Liquid.normalizeScope(ctx), ...argArray);
return ret;
},
};
});
}
addPairedShortcode(shortcodeName, shortcodeFn) {
let _t = this;
this.addTag(shortcodeName, function (liquidEngine) {
return {
parse(tagToken, remainTokens) {
this.name = tagToken.name;
if (_t.config.liquidParameterParsing === "builtin") {
this.orderedArgs = Liquid.parseArgumentsBuiltin(tagToken.args);
// note that Liquid does have a Hash class for name-based argument parsing but offers no easy to support both modes in one class
} else {
this.legacyArgs = tagToken.args;
}
this.templates = [];
var stream = liquidEngine.parser
.parseStream(remainTokens)
.on("template", (tpl) => this.templates.push(tpl))
.on("tag:end" + shortcodeName, () => stream.stop())
.on("end", () => {
throw new Error(`tag ${tagToken.raw} not closed`);
});
stream.start();
},
render: function* (ctx /*, emitter*/) {
let argArray = [];
if (this.legacyArgs) {
let rawArgs = Liquid.parseArguments(_t.argLexer, this.legacyArgs);
for (let arg of rawArgs) {
let b = yield liquidEngine.evalValue(arg, ctx);
argArray.push(b);
}
} else if (this.orderedArgs) {
for (let arg of this.orderedArgs) {
let b = yield evalToken(arg, ctx);
argArray.push(b);
}
}
const html = yield liquidEngine.renderer.renderTemplates(this.templates, ctx);
let ret = yield shortcodeFn.call(Liquid.normalizeScope(ctx), html, ...argArray);
return ret;
},
};
});
}
parseForSymbols(str) {
let tokenizer = new Tokenizer(str);
/** @type {Array} */
let tokens = tokenizer.readTopLevelTokens();
let symbols = tokens
.filter((token) => token.kind === TokenKind.Output)
.map((token) => {
// manually remove filters 😅
return token.content.split("|").map((entry) => entry.trim())[0];
});
return symbols;
}
// Dont return a boolean if permalink is a function (see TemplateContent->renderPermalink)
/** @returns {boolean|undefined} */
permalinkNeedsCompilation(str) {
if (typeof str === "string") {
return this.needsCompilation(str);
}
}
needsCompilation(str) {
let options = this.liquidLib.options;
return (
str.indexOf(options.tagDelimiterLeft) !== -1 ||
str.indexOf(options.outputDelimiterLeft) !== -1
);
}
async compile(str, inputPath) {
let engine = this.liquidLib;
let tmplReady = engine.parse(str, inputPath);
// Required for relative includes
let options = {};
if (!inputPath || inputPath === "liquid" || inputPath === "md") {
// do nothing
} else {
options.root = [TemplatePath.getDirFromFilePath(inputPath)];
}
return async function (data) {
let tmpl = await tmplReady;
return engine.render(tmpl, data, options);
};
}
}
export default Liquid;

93
node_modules/@11ty/eleventy/src/Engines/Markdown.js generated vendored Normal file
View File

@@ -0,0 +1,93 @@
import markdownIt from "markdown-it";
import TemplateEngine from "./TemplateEngine.js";
class Markdown extends TemplateEngine {
constructor(name, eleventyConfig) {
super(name, eleventyConfig);
this.markdownOptions = {};
this.setLibrary(this.config.libraryOverrides.md);
this.cacheable = true;
}
setLibrary(mdLib) {
this.mdLib = mdLib || markdownIt(this.getMarkdownOptions());
// Overrides a highlighter set in `markdownOptions`
// This is separate so devs can pass in a new mdLib and still use the official eleventy plugin for markdown highlighting
if (this.config.markdownHighlighter && typeof this.mdLib.set === "function") {
this.mdLib.set({
highlight: this.config.markdownHighlighter,
});
}
if (typeof this.mdLib.disable === "function") {
// Disable indented code blocks by default (Issue #2438)
this.mdLib.disable("code");
}
this.setEngineLib(this.mdLib);
}
setMarkdownOptions(options) {
this.markdownOptions = options;
}
getMarkdownOptions() {
// work with "mode" presets https://github.com/markdown-it/markdown-it#init-with-presets-and-options
if (typeof this.markdownOptions === "string") {
return this.markdownOptions;
}
return Object.assign(
{
html: true,
},
this.markdownOptions || {},
);
}
async compile(str, inputPath, preTemplateEngine, bypassMarkdown) {
let mdlib = this.mdLib;
if (preTemplateEngine) {
let engine;
if (typeof preTemplateEngine === "string") {
engine = await this.engineManager.getEngine(preTemplateEngine, this.extensionMap);
} else {
engine = preTemplateEngine;
}
let fnReady = engine.compile(str, inputPath);
if (bypassMarkdown) {
return async function (data) {
let fn = await fnReady;
return fn(data);
};
} else {
return async function (data) {
let fn = await fnReady;
let preTemplateEngineRender = await fn(data);
let finishedRender = mdlib.render(preTemplateEngineRender, data);
return finishedRender;
};
}
} else {
if (bypassMarkdown) {
return function () {
return str;
};
} else {
return function (data) {
return mdlib.render(str, data);
};
}
}
}
}
export default Markdown;

447
node_modules/@11ty/eleventy/src/Engines/Nunjucks.js generated vendored Executable file
View File

@@ -0,0 +1,447 @@
import NunjucksLib from "nunjucks";
import { TemplatePath } from "@11ty/eleventy-utils";
import TemplateEngine from "./TemplateEngine.js";
import EleventyBaseError from "../Errors/EleventyBaseError.js";
import EventBusUtil from "../Util/EventBusUtil.js";
import { augmentObject } from "./Util/ContextAugmenter.js";
class EleventyNunjucksError extends EleventyBaseError {}
class Nunjucks extends TemplateEngine {
constructor(name, eleventyConfig) {
super(name, eleventyConfig);
this.nunjucksEnvironmentOptions = this.config.nunjucksEnvironmentOptions || { dev: true };
this.nunjucksPrecompiledTemplates = this.config.nunjucksPrecompiledTemplates || {};
this._usingPrecompiled = Object.keys(this.nunjucksPrecompiledTemplates).length > 0;
this.setLibrary(this.config.libraryOverrides.njk);
this.cacheable = true;
}
_setEnv(override) {
if (override) {
this.njkEnv = override;
} else if (this._usingPrecompiled) {
// Precompiled templates to avoid eval!
const NodePrecompiledLoader = function () {};
NodePrecompiledLoader.prototype.getSource = (name) => {
// https://github.com/mozilla/nunjucks/blob/fd500902d7c88672470c87170796de52fc0f791a/nunjucks/src/precompiled-loader.js#L5
return {
src: {
type: "code",
obj: this.nunjucksPrecompiledTemplates[name],
},
// Maybe add this?
// path,
// noCache: true
};
};
this.njkEnv = new NunjucksLib.Environment(
new NodePrecompiledLoader(),
this.nunjucksEnvironmentOptions,
);
} else {
let paths = new Set();
paths.add(super.getIncludesDir());
paths.add(TemplatePath.getWorkingDir());
// Filter out undefined paths
let fsLoader = new NunjucksLib.FileSystemLoader(Array.from(paths).filter(Boolean));
this.njkEnv = new NunjucksLib.Environment(fsLoader, this.nunjucksEnvironmentOptions);
}
this.config.events.emit("eleventy.engine.njk", {
nunjucks: NunjucksLib,
environment: this.njkEnv,
});
}
setLibrary(override) {
this._setEnv(override);
// Correct, but overbroad. Better would be to evict more granularly, but
// resolution from paths isn't straightforward.
EventBusUtil.soloOn("eleventy.templateModified", (/*path*/) => {
this.njkEnv.invalidateCache();
});
this.setEngineLib(this.njkEnv);
this.addFilters(this.config.nunjucksFilters);
this.addFilters(this.config.nunjucksAsyncFilters, true);
// TODO these all go to the same place (addTag), add warnings for overwrites
// TODO(zachleat): variableName should work with quotes or without quotes (same as {% set %})
this.addPairedShortcode("setAsync", function (content, variableName) {
this.ctx[variableName] = content;
return "";
});
this.addCustomTags(this.config.nunjucksTags);
this.addAllShortcodes(this.config.nunjucksShortcodes);
this.addAllShortcodes(this.config.nunjucksAsyncShortcodes, true);
this.addAllPairedShortcodes(this.config.nunjucksPairedShortcodes);
this.addAllPairedShortcodes(this.config.nunjucksAsyncPairedShortcodes, true);
this.addGlobals(this.config.nunjucksGlobals);
}
addFilters(filters, isAsync) {
for (let name in filters) {
this.njkEnv.addFilter(name, Nunjucks.wrapFilter(name, filters[name]), isAsync);
}
}
static wrapFilter(name, fn) {
return function (...args) {
try {
augmentObject(this, {
source: this.ctx,
lazy: false, // context.env?.opts.throwOnUndefined,
});
return fn.call(this, ...args);
} catch (e) {
throw new EleventyNunjucksError(
`Error in Nunjucks Filter \`${name}\`${this.page ? ` (${this.page.inputPath})` : ""}`,
e,
);
}
};
}
// Shortcodes
static normalizeContext(context) {
let obj = {};
if (context.ctx) {
obj.ctx = context.ctx;
obj.env = context.env;
augmentObject(obj, {
source: context.ctx,
lazy: false, // context.env?.opts.throwOnUndefined,
});
}
return obj;
}
addCustomTags(tags) {
for (let name in tags) {
this.addTag(name, tags[name]);
}
}
addTag(name, tagFn) {
let tagObj;
if (typeof tagFn === "function") {
tagObj = tagFn(NunjucksLib, this.njkEnv);
} else {
throw new Error(
"Nunjucks.addTag expects a callback function to be passed in: addTag(name, function(nunjucksEngine) {})",
);
}
this.njkEnv.addExtension(name, tagObj);
}
addGlobals(globals) {
for (let name in globals) {
this.addGlobal(name, globals[name]);
}
}
addGlobal(name, globalFn) {
this.njkEnv.addGlobal(name, globalFn);
}
addAllShortcodes(shortcodes, isAsync = false) {
for (let name in shortcodes) {
this.addShortcode(name, shortcodes[name], isAsync);
}
}
addAllPairedShortcodes(shortcodes, isAsync = false) {
for (let name in shortcodes) {
this.addPairedShortcode(name, shortcodes[name], isAsync);
}
}
_getShortcodeFn(shortcodeName, shortcodeFn, isAsync = false) {
return function ShortcodeFunction() {
this.tags = [shortcodeName];
this.parse = function (parser, nodes) {
let args;
let tok = parser.nextToken();
args = parser.parseSignature(true, true);
// Nunjucks bug with non-paired custom tags bug still exists even
// though this issue is closed. Works fine for paired.
// https://github.com/mozilla/nunjucks/issues/158
if (args.children.length === 0) {
args.addChild(new nodes.Literal(0, 0, ""));
}
parser.advanceAfterBlockEnd(tok.value);
if (isAsync) {
return new nodes.CallExtensionAsync(this, "run", args);
}
return new nodes.CallExtension(this, "run", args);
};
this.run = function (...args) {
let resolve;
if (isAsync) {
resolve = args.pop();
}
let [context, ...argArray] = args;
if (isAsync) {
let ret = shortcodeFn.call(Nunjucks.normalizeContext(context), ...argArray);
// #3286 error messaging when the shortcode is not a promise
if (!ret?.then) {
resolve(
new EleventyNunjucksError(
`Error with Nunjucks shortcode \`${shortcodeName}\`: it was defined as asynchronous but was actually synchronous. This is important for Nunjucks.`,
),
);
}
ret.then(
function (returnValue) {
resolve(null, new NunjucksLib.runtime.SafeString("" + returnValue));
},
function (e) {
resolve(
new EleventyNunjucksError(`Error with Nunjucks shortcode \`${shortcodeName}\``, e),
);
},
);
} else {
try {
let ret = shortcodeFn.call(Nunjucks.normalizeContext(context), ...argArray);
return new NunjucksLib.runtime.SafeString("" + ret);
} catch (e) {
throw new EleventyNunjucksError(
`Error with Nunjucks shortcode \`${shortcodeName}\``,
e,
);
}
}
};
};
}
_getPairedShortcodeFn(shortcodeName, shortcodeFn, isAsync = false) {
return function PairedShortcodeFunction() {
this.tags = [shortcodeName];
this.parse = function (parser, nodes) {
var tok = parser.nextToken();
var args = parser.parseSignature(true, true);
parser.advanceAfterBlockEnd(tok.value);
var body = parser.parseUntilBlocks("end" + shortcodeName);
parser.advanceAfterBlockEnd();
return new nodes.CallExtensionAsync(this, "run", args, [body]);
};
this.run = function (...args) {
let resolve = args.pop();
let body = args.pop();
let [context, ...argArray] = args;
body(function (e, bodyContent) {
if (e) {
resolve(
new EleventyNunjucksError(
`Error with Nunjucks paired shortcode \`${shortcodeName}\``,
e,
),
);
}
if (isAsync) {
let ret = shortcodeFn.call(
Nunjucks.normalizeContext(context),
bodyContent,
...argArray,
);
// #3286 error messaging when the shortcode is not a promise
if (!ret?.then) {
throw new EleventyNunjucksError(
`Error with Nunjucks shortcode \`${shortcodeName}\`: it was defined as asynchronous but was actually synchronous. This is important for Nunjucks.`,
);
}
ret.then(
function (returnValue) {
resolve(null, new NunjucksLib.runtime.SafeString(returnValue));
},
function (e) {
resolve(
new EleventyNunjucksError(
`Error with Nunjucks paired shortcode \`${shortcodeName}\``,
e,
),
);
},
);
} else {
try {
resolve(
null,
new NunjucksLib.runtime.SafeString(
shortcodeFn.call(Nunjucks.normalizeContext(context), bodyContent, ...argArray),
),
);
} catch (e) {
resolve(
new EleventyNunjucksError(
`Error with Nunjucks paired shortcode \`${shortcodeName}\``,
e,
),
);
}
}
});
};
};
}
addShortcode(shortcodeName, shortcodeFn, isAsync = false) {
let fn = this._getShortcodeFn(shortcodeName, shortcodeFn, isAsync);
this.njkEnv.addExtension(shortcodeName, new fn());
}
addPairedShortcode(shortcodeName, shortcodeFn, isAsync = false) {
let fn = this._getPairedShortcodeFn(shortcodeName, shortcodeFn, isAsync);
this.njkEnv.addExtension(shortcodeName, new fn());
}
// Dont return a boolean if permalink is a function (see TemplateContent->renderPermalink)
permalinkNeedsCompilation(str) {
if (typeof str === "string") {
return this.needsCompilation(str);
}
}
needsCompilation(str) {
// Defend against syntax customisations:
// https://mozilla.github.io/nunjucks/api.html#customizing-syntax
let optsTags = this.njkEnv.opts.tags || {};
let blockStart = optsTags.blockStart || "{%";
let variableStart = optsTags.variableStart || "{{";
let commentStart = optsTags.variableStart || "{#";
return (
str.indexOf(blockStart) !== -1 ||
str.indexOf(variableStart) !== -1 ||
str.indexOf(commentStart) !== -1
);
}
_getParseExtensions() {
if (this._parseExtensions) {
return this._parseExtensions;
}
// add extensions so the parser knows about our custom tags/blocks
let ext = [];
for (let name in this.config.nunjucksTags) {
let fn = this._getShortcodeFn(name, () => {});
ext.push(new fn());
}
for (let name in this.config.nunjucksShortcodes) {
let fn = this._getShortcodeFn(name, () => {});
ext.push(new fn());
}
for (let name in this.config.nunjucksAsyncShortcodes) {
let fn = this._getShortcodeFn(name, () => {}, true);
ext.push(new fn());
}
for (let name in this.config.nunjucksPairedShortcodes) {
let fn = this._getPairedShortcodeFn(name, () => {});
ext.push(new fn());
}
for (let name in this.config.nunjucksAsyncPairedShortcodes) {
let fn = this._getPairedShortcodeFn(name, () => {}, true);
ext.push(new fn());
}
this._parseExtensions = ext;
return ext;
}
/* Outputs an Array of lodash get selectors */
parseForSymbols(str) {
const { parser, nodes } = NunjucksLib;
let obj = parser.parse(str, this._getParseExtensions());
let linesplit = str.split("\n");
let values = obj.findAll(nodes.Value);
let symbols = obj.findAll(nodes.Symbol).map((entry) => {
let name = [entry.value];
let nestedIndex = -1;
for (let val of values) {
if (nestedIndex > -1) {
/* deep.object.syntax */
if (linesplit[val.lineno].charAt(nestedIndex) === ".") {
name.push(val.value);
nestedIndex += val.value.length + 1;
} else {
nestedIndex = -1;
}
} else if (
val.lineno === entry.lineno &&
val.colno === entry.colno &&
val.value === entry.value
) {
nestedIndex = entry.colno + entry.value.length;
}
}
return name.join(".");
});
let uniqueSymbols = Array.from(new Set(symbols));
return uniqueSymbols;
}
async compile(str, inputPath) {
let tmpl;
// *All* templates are precompiled to avoid runtime eval
if (this._usingPrecompiled) {
tmpl = this.njkEnv.getTemplate(str, true);
} else if (!inputPath || inputPath === "njk" || inputPath === "md") {
tmpl = new NunjucksLib.Template(str, this.njkEnv, null, false);
} else {
tmpl = new NunjucksLib.Template(str, this.njkEnv, inputPath, false);
}
return function (data) {
return new Promise(function (resolve, reject) {
tmpl.render(data, function (err, res) {
if (err) {
reject(err);
} else {
resolve(res);
}
});
});
};
}
}
export default Nunjucks;

View File

@@ -0,0 +1,184 @@
import EleventyExtensionMap from "../EleventyExtensionMap.js";
import EleventyBaseError from "../Errors/EleventyBaseError.js";
class TemplateEngineConfigError extends EleventyBaseError {}
class TemplateEngine {
constructor(name, eleventyConfig) {
this.name = name;
this.engineLib = null;
this.cacheable = false;
if (!eleventyConfig) {
throw new TemplateEngineConfigError("Missing `eleventyConfig` argument.");
}
this.eleventyConfig = eleventyConfig;
}
get dirs() {
return this.eleventyConfig.directories;
}
get inputDir() {
return this.dirs.input;
}
get includesDir() {
return this.dirs.includes;
}
get config() {
if (this.eleventyConfig.constructor.name !== "TemplateConfig") {
throw new Error("Expecting a TemplateConfig instance.");
}
return this.eleventyConfig.getConfig();
}
get benchmarks() {
if (!this._benchmarks) {
this._benchmarks = {
aggregate: this.config.benchmarkManager.get("Aggregate"),
};
}
return this._benchmarks;
}
get engineManager() {
return this._engineManager;
}
set engineManager(manager) {
this._engineManager = manager;
}
get extensionMap() {
if (!this._extensionMap) {
this._extensionMap = new EleventyExtensionMap(this.eleventyConfig);
this._extensionMap.setFormats([]);
}
return this._extensionMap;
}
set extensionMap(map) {
this._extensionMap = map;
}
get extensions() {
if (!this._extensions) {
this._extensions = this.extensionMap.getExtensionsFromKey(this.name);
}
return this._extensions;
}
get extensionEntries() {
if (!this._extensionEntries) {
this._extensionEntries = this.extensionMap.getExtensionEntriesFromKey(this.name);
}
return this._extensionEntries;
}
getName() {
return this.name;
}
// Backwards compat
getIncludesDir() {
return this.includesDir;
}
/**
* @protected
*/
setEngineLib(engineLib) {
this.engineLib = engineLib;
// Run engine amendments (via issue #2438)
for (let amendment of this.config.libraryAmendments[this.name] || []) {
// TODO itd be nice if this were async friendly
amendment(engineLib);
}
}
getEngineLib() {
return this.engineLib;
}
async _testRender(str, data) {
// @ts-ignore
let fn = await this.compile(str);
return fn(data);
}
useJavaScriptImport() {
return false;
}
// JavaScript files defer to the module loader rather than read the files to strings
needsToReadFileContents() {
return true;
}
getExtraDataFromFile() {
return {};
}
getCompileCacheKey(str, inputPath) {
// Changing to use inputPath and contents, using only file contents (`str`) caused issues when two
// different files had identical content (2.0.0-canary.16)
// Caches are now segmented based on inputPath so using inputPath here is superfluous (2.0.0-canary.19)
// But we do want a non-falsy value here even if `str` is an empty string.
return {
useCache: true,
key: inputPath + str,
};
}
get defaultTemplateFileExtension() {
return "html";
}
// Whether or not to wrap in Eleventy layouts
useLayouts() {
return true;
}
/** @returns {boolean|undefined} */
permalinkNeedsCompilation(str) {
return this.needsCompilation();
}
// whether or not compile is needed or can we return the plaintext?
needsCompilation(str) {
return true;
}
/**
* Make sure compile is implemented downstream.
* @abstract
* @return {Promise}
*/
async compile() {
throw new Error("compile() must be implemented by engine");
}
// See https://v3.11ty.dev/docs/watch-serve/#watch-javascript-dependencies
static shouldSpiderJavaScriptDependencies() {
return false;
}
hasDependencies(inputPath) {
if (this.config.uses.getDependencies(inputPath) === false) {
return false;
}
return true;
}
isFileRelevantTo(inputPath, comparisonFile) {
return this.config.uses.isFileRelevantTo(inputPath, comparisonFile);
}
}
export default TemplateEngine;

View File

@@ -0,0 +1,197 @@
import debugUtil from "debug";
import EleventyBaseError from "../Errors/EleventyBaseError.js";
import { EleventyImportFromEleventy } from "../Util/Require.js";
const debug = debugUtil("Eleventy:TemplateEngineManager");
class TemplateEngineManagerConfigError extends EleventyBaseError {}
class TemplateEngineManager {
constructor(eleventyConfig) {
if (!eleventyConfig || eleventyConfig.constructor.name !== "TemplateConfig") {
throw new TemplateEngineManagerConfigError("Missing or invalid `config` argument.");
}
this.eleventyConfig = eleventyConfig;
this.engineCache = {};
this.importCache = {};
}
get config() {
return this.eleventyConfig.getConfig();
}
static isAlias(entry) {
if (entry.aliasKey) {
return true;
}
return entry.key !== entry.extension;
}
static isSimpleAlias(entry) {
if (!this.isAlias(entry)) {
return false;
}
// has keys other than key, extension, and aliasKey
return (
Object.keys(entry).some((key) => {
return key !== "key" && key !== "extension" && key !== "aliasKey";
}) === false
);
}
get keyToClassNameMap() {
if (!this._keyToClassNameMap) {
this._keyToClassNameMap = {
md: "Markdown",
html: "Html",
njk: "Nunjucks",
liquid: "Liquid",
"11ty.js": "JavaScript",
};
// Custom entries *can* overwrite default entries above
if ("extensionMap" in this.config) {
for (let entry of this.config.extensionMap) {
// either the key does not already exist or it is not a simple alias and is an override: https://v3.11ty.dev/docs/languages/custom/#overriding-an-existing-template-language
let existingTarget = this._keyToClassNameMap[entry.key];
let isAlias = TemplateEngineManager.isAlias(entry);
if (!existingTarget && isAlias) {
throw new Error(
`An attempt to alias ${entry.aliasKey} to ${entry.key} was made, but ${entry.key} is not a recognized template syntax.`,
);
}
if (isAlias) {
// only `key` and `extension`, not `compile` or other options
if (!TemplateEngineManager.isSimpleAlias(entry)) {
this._keyToClassNameMap[entry.aliasKey] = "Custom";
} else {
this._keyToClassNameMap[entry.aliasKey] = this._keyToClassNameMap[entry.key];
}
} else {
// not an alias, so `key` and `extension` are the same here.
// *can* override a built-in extension!
this._keyToClassNameMap[entry.key] = "Custom";
}
}
}
}
return this._keyToClassNameMap;
}
reset() {
this.engineCache = {};
}
getClassNameFromTemplateKey(key) {
return this.keyToClassNameMap[key];
}
hasEngine(name) {
return !!this.getClassNameFromTemplateKey(name);
}
isEngineRemovedFromCore(name) {
return ["ejs", "hbs", "mustache", "haml", "pug"].includes(name) && !this.hasEngine(name);
}
async getEngineClassByExtension(extension) {
if (this.importCache[extension]) {
return this.importCache[extension];
}
let promise;
// We include these as raw strings (and not more readable variables) so theyre parsed by a bundler.
if (extension === "md") {
promise = EleventyImportFromEleventy("./src/Engines/Markdown.js");
} else if (extension === "html") {
promise = EleventyImportFromEleventy("./src/Engines/Html.js");
} else if (extension === "njk") {
promise = EleventyImportFromEleventy("./src/Engines/Nunjucks.js");
} else if (extension === "liquid") {
promise = EleventyImportFromEleventy("./src/Engines/Liquid.js");
} else if (extension === "11ty.js") {
promise = EleventyImportFromEleventy("./src/Engines/JavaScript.js");
} else {
promise = this.getCustomEngineClass();
}
this.importCache[extension] = promise;
return promise;
}
async getCustomEngineClass() {
if (!this._CustomEngine) {
this._CustomEngine = EleventyImportFromEleventy("./src/Engines/Custom.js");
}
return this._CustomEngine;
}
async #getEngine(name, extensionMap) {
let cls = await this.getEngineClassByExtension(name);
let instance = new cls(name, this.eleventyConfig);
instance.extensionMap = extensionMap;
instance.engineManager = this;
let extensionEntry = extensionMap.getExtensionEntry(name);
// Override a built-in extension (md => md)
// If provided a "Custom" engine using addExtension, but that engine's instance is *not* custom,
// The user must be overriding a built-in engine i.e. addExtension('md', { ...overrideBehavior })
let className = this.getClassNameFromTemplateKey(name);
if (className === "Custom" && instance.constructor.name !== "CustomEngine") {
let CustomEngine = await this.getCustomEngineClass();
let overrideCustomEngine = new CustomEngine(name, this.eleventyConfig);
// Keep track of the "default" engine 11ty would normally use
// This allows the user to access the default engine in their override
overrideCustomEngine.setDefaultEngine(instance);
instance = overrideCustomEngine;
// Alias to a built-in extension (11ty.tsx => 11ty.js)
} else if (
instance.constructor.name === "CustomEngine" &&
TemplateEngineManager.isAlias(extensionEntry)
) {
// add defaultRenderer for complex aliases with their own compile functions.
let originalEngineInstance = await this.getEngine(extensionEntry.key, extensionMap);
instance.setDefaultEngine(originalEngineInstance);
}
return instance;
}
async getEngine(name, extensionMap) {
// Warning about engine deprecation
if (this.isEngineRemovedFromCore(name)) {
throw new Error(
`Per the 11ty Community Survey (2023), the "${name}" template language was moved from core to an officially supported plugin in v3.0. These plugins live here: https://github.com/11ty/eleventy-plugin-template-languages and are documented on their respective template language docs at https://v3.11ty.dev/docs/languages/ You are also empowered to implement *any* template language yourself using https://v3.11ty.dev/docs/languages/custom/`,
);
}
if (!this.hasEngine(name)) {
throw new Error(`Template Engine ${name} does not exist in getEngine()`);
}
// TODO these cached engines should be based on extensions not name, then we can remove the error in
// "Double override (not aliases) throws an error" test in TemplateRenderCustomTest.js
if (!this.engineCache[name]) {
debug("Engine cache miss %o (should only happen once per type)", name);
// Make sure cache key is based on name and not path
// Custom class is used for all plugins, cache once per plugin
this.engineCache[name] = this.#getEngine(name, extensionMap);
}
return this.engineCache[name];
}
}
export default TemplateEngineManager;

View File

@@ -0,0 +1,67 @@
const DATA_KEYS = ["page", "eleventy"];
function augmentFunction(fn, options = {}) {
let t = typeof fn;
if (t !== "function") {
throw new Error(
"Invalid type passed to `augmentFunction`. A function was expected and received: " + t,
);
}
/** @this {object} */
return function (...args) {
let context = augmentObject(this || {}, options);
return fn.call(context, ...args);
};
}
function augmentObject(targetObject, options = {}) {
options = Object.assign(
{
source: undefined, // where to copy from
overwrite: true,
lazy: false, // lazily fetch the property
// getter: function() {},
},
options,
);
for (let key of DATA_KEYS) {
// Skip if overwrite: false and prop already exists on target
if (!options.overwrite && targetObject[key]) {
continue;
}
if (options.lazy) {
let value;
if (typeof options.getter == "function") {
value = () => options.getter(key, options.source);
} else {
value = () => options.source?.[key];
}
// lazy getter important for Liquid strictVariables support
Object.defineProperty(targetObject, key, {
writable: true,
configurable: true,
enumerable: true,
value,
});
} else {
let value;
if (typeof options.getter == "function") {
value = options.getter(key, options.source);
} else {
value = options.source?.[key];
}
if (value) {
targetObject[key] = value;
}
}
}
return targetObject;
}
export { DATA_KEYS as augmentKeys, augmentFunction, augmentObject };

View File

@@ -0,0 +1,9 @@
import EleventyBaseError from "./EleventyBaseError.js";
class DuplicatePermalinkOutputError extends EleventyBaseError {
get removeDuplicateErrorStringFromOutput() {
return true;
}
}
export default DuplicatePermalinkOutputError;

View File

@@ -0,0 +1,24 @@
/**
* This class serves as basis for all Eleventy-specific errors.
* @ignore
*/
class EleventyBaseError extends Error {
/**
* @param {string} message - The error message to display.
* @param {unknown} [originalError] - The original error caught.
*/
constructor(message, originalError) {
super(message);
this.name = this.constructor.name;
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
if (originalError) {
this.originalError = originalError;
}
}
}
export default EleventyBaseError;

View File

@@ -0,0 +1,151 @@
import util from "node:util";
import debugUtil from "debug";
import ConsoleLogger from "../Util/ConsoleLogger.js";
import EleventyErrorUtil from "./EleventyErrorUtil.js";
const debug = debugUtil("Eleventy:EleventyErrorHandler");
class EleventyErrorHandler {
constructor() {
this._isVerbose = true;
}
get isVerbose() {
return this._isVerbose;
}
set isVerbose(verbose) {
this._isVerbose = !!verbose;
this.logger.isVerbose = !!verbose;
}
get logger() {
if (!this._logger) {
this._logger = new ConsoleLogger();
this._logger.isVerbose = this.isVerbose;
}
return this._logger;
}
set logger(logger) {
this._logger = logger;
}
warn(e, msg) {
if (msg) {
this.initialMessage(msg, "warn", "yellow");
}
this.log(e, "warn");
}
fatal(e, msg) {
this.error(e, msg);
process.exitCode = 1;
}
once(type, e, msg) {
if (e.__errorAlreadyLogged) {
return;
}
this[type || "error"](e, msg);
Object.defineProperty(e, "__errorAlreadyLogged", {
value: true,
});
}
error(e, msg) {
if (msg) {
this.initialMessage(msg, "error", "red", true);
}
this.log(e, "error", undefined, true);
}
static getTotalErrorCount(e) {
let totalErrorCount = 0;
let errorCountRef = e;
while (errorCountRef) {
totalErrorCount++;
errorCountRef = errorCountRef.originalError;
}
return totalErrorCount;
}
//https://nodejs.org/api/process.html
log(e, type = "log", chalkColor = "", forceToConsole = false) {
if (process.env.DEBUG) {
debug("Full error object: %o", util.inspect(e, { showHidden: false, depth: null }));
}
let showStack = true;
if (e.skipOriginalStack) {
showStack = false;
}
let totalErrorCount = EleventyErrorHandler.getTotalErrorCount(e);
let ref = e;
let index = 1;
while (ref) {
let nextRef = ref.originalError;
// Nunjucks wraps errors and puts the original in error.cause
if (nextRef?.cause?.originalError) {
nextRef = nextRef.cause.originalError;
}
if (!nextRef && EleventyErrorUtil.hasEmbeddedError(ref.message)) {
nextRef = EleventyErrorUtil.deconvertErrorToObject(ref);
}
if (nextRef?.skipOriginalStack) {
showStack = false;
}
this.logger.message(
`${totalErrorCount > 1 ? `${index}. ` : ""}${(
EleventyErrorUtil.cleanMessage(ref.message) || "(No error message provided)"
).trim()}${ref.name !== "Error" ? ` (via ${ref.name})` : ""}`,
type,
chalkColor,
forceToConsole,
);
if (process.env.DEBUG) {
debug(`(${type} stack): ${ref.stack}`);
} else if (!nextRef) {
// last error in the loop
// remove duplicate error messages if the stack contains the original message output above
let stackStr = ref.stack || "";
if (e.removeDuplicateErrorStringFromOutput) {
stackStr = stackStr.replace(
`${ref.name}: ${ref.message}`,
"(Repeated output has been truncated…)",
);
}
if (showStack) {
this.logger.message(
"\nOriginal error stack trace: " + stackStr,
type,
chalkColor,
forceToConsole,
);
}
}
ref = nextRef;
index++;
}
}
initialMessage(message, type = "log", chalkColor = "blue", forceToConsole = false) {
if (message) {
this.logger.message(message + ":", type, chalkColor, forceToConsole);
}
}
}
export { EleventyErrorHandler };

View File

@@ -0,0 +1,72 @@
import TemplateContentPrematureUseError from "./TemplateContentPrematureUseError.js";
/* Hack to workaround the variety of error handling schemes in template languages */
class EleventyErrorUtil {
static get prefix() {
return ">>>>>11ty>>>>>";
}
static get suffix() {
return "<<<<<11ty<<<<<";
}
static hasEmbeddedError(msg) {
if (!msg) {
return false;
}
return msg.includes(EleventyErrorUtil.prefix) && msg.includes(EleventyErrorUtil.suffix);
}
static cleanMessage(msg) {
if (!msg) {
return "";
}
if (!EleventyErrorUtil.hasEmbeddedError(msg)) {
return "" + msg;
}
return msg.slice(0, Math.max(0, msg.indexOf(EleventyErrorUtil.prefix)));
}
static deconvertErrorToObject(error) {
if (!error || !error.message) {
throw new Error(`Could not convert error object from: ${error}`);
}
if (!EleventyErrorUtil.hasEmbeddedError(error.message)) {
return error;
}
let msg = error.message;
let objectString = msg.substring(
msg.indexOf(EleventyErrorUtil.prefix) + EleventyErrorUtil.prefix.length,
msg.lastIndexOf(EleventyErrorUtil.suffix),
);
let obj = JSON.parse(objectString);
obj.name = error.name;
return obj;
}
// pass an error through a random template engines error handling unscathed
static convertErrorToString(error) {
return (
EleventyErrorUtil.prefix +
JSON.stringify({ message: error.message, stack: error.stack }) +
EleventyErrorUtil.suffix
);
}
static isPrematureTemplateContentError(e) {
// TODO the rest of the template engines
return (
e instanceof TemplateContentPrematureUseError ||
(e.originalError &&
(e.originalError.name === "RenderError" ||
e.originalError.name === "UndefinedVariableError") &&
e.originalError.originalError instanceof TemplateContentPrematureUseError) || // Liquid
(e.message || "").indexOf("TemplateContentPrematureUseError") > -1
); // Nunjucks
}
}
export default EleventyErrorUtil;

View File

@@ -0,0 +1,5 @@
import EleventyBaseError from "./EleventyBaseError.js";
class TemplateContentPrematureUseError extends EleventyBaseError {}
export default TemplateContentPrematureUseError;

View File

@@ -0,0 +1,5 @@
import EleventyBaseError from "./EleventyBaseError.js";
class TemplateContentUnrenderedTemplateError extends EleventyBaseError {}
export default TemplateContentUnrenderedTemplateError;

View File

@@ -0,0 +1,5 @@
import EleventyBaseError from "./EleventyBaseError.js";
class UsingCircularTemplateContentReferenceError extends EleventyBaseError {}
export default UsingCircularTemplateContentReferenceError;

23
node_modules/@11ty/eleventy/src/EventBus.js generated vendored Normal file
View File

@@ -0,0 +1,23 @@
import debugUtil from "debug";
import EventEmitter from "./Util/AsyncEventEmitter.js";
const debug = debugUtil("Eleventy:EventBus");
/**
* @module 11ty/eleventy/EventBus
* @ignore
*/
debug("Setting up global EventBus.");
/**
* Provides a global event bus that modules deep down in the stack can
* subscribe to from a global singleton for decoupled pub/sub.
* @type {module:11ty/eleventy/Util/AsyncEventEmitter~AsyncEventEmitter}
*/
let bus = new EventEmitter();
bus.setMaxListeners(100);
debug("EventBus max listener count: %o", bus.getMaxListeners());
export default bus;

102
node_modules/@11ty/eleventy/src/FileSystemSearch.js generated vendored Normal file
View File

@@ -0,0 +1,102 @@
import fastglob from "fast-glob";
import { TemplatePath } from "@11ty/eleventy-utils";
import debugUtil from "debug";
import { isGlobMatch } from "./Util/GlobMatcher.js";
const debug = debugUtil("Eleventy:FastGlobManager");
class FileSystemSearch {
constructor() {
this.inputs = {};
this.outputs = {};
this.promises = {};
this.count = 0;
}
getCacheKey(key, globs, options) {
if (Array.isArray(globs)) {
globs = globs.sort();
}
return key + JSON.stringify(globs) + JSON.stringify(options);
}
// returns a promise
search(key, globs, options = {}) {
debug("Glob search (%o) searching for: %o", key, globs);
if (!Array.isArray(globs)) {
globs = [globs];
}
// Strip leading slashes from everything!
globs = globs.map((entry) => TemplatePath.stripLeadingDotSlash(entry));
if (options.ignore && Array.isArray(options.ignore)) {
options.ignore = options.ignore.map((entry) => TemplatePath.stripLeadingDotSlash(entry));
debug("Glob search (%o) ignoring: %o", key, options.ignore);
}
let cacheKey = this.getCacheKey(key, globs, options);
// Only after the promise has resolved
if (this.outputs[cacheKey]) {
return Array.from(this.outputs[cacheKey]);
}
if (!this.promises[cacheKey]) {
this.inputs[cacheKey] = {
input: globs,
options,
};
this.count++;
this.promises[cacheKey] = fastglob(
globs,
Object.assign(
{
caseSensitiveMatch: false, // insensitive
dot: true,
},
options,
),
).then((results) => {
this.outputs[cacheKey] = new Set(
results.map((entry) => TemplatePath.addLeadingDotSlash(entry)),
);
return Array.from(this.outputs[cacheKey]);
});
}
// may be an unresolved promise
return this.promises[cacheKey];
}
_modify(path, setOperation) {
path = TemplatePath.stripLeadingDotSlash(path);
let normalized = TemplatePath.addLeadingDotSlash(path);
for (let key in this.inputs) {
let { input, options } = this.inputs[key];
if (
isGlobMatch(path, input, {
ignore: options.ignore,
})
) {
this.outputs[key][setOperation](normalized);
}
}
}
add(path) {
this._modify(path, "add");
}
delete(path) {
this._modify(path, "delete");
}
}
export default FileSystemSearch;

View File

@@ -0,0 +1,20 @@
export default function getCollectionItem(collection, page, modifier = 0) {
let j = 0;
let index;
for (let item of collection) {
if (
item.inputPath === page.inputPath &&
(item.outputPath === page.outputPath || item.url === page.url)
) {
index = j;
break;
}
j++;
}
if (index !== undefined && collection?.length) {
if (index + modifier >= 0 && index + modifier < collection.length) {
return collection[index + modifier];
}
}
}

View File

@@ -0,0 +1,17 @@
// TODO locale-friendly, see GetLocaleCollectionItem.js)
export default function getCollectionItemIndex(collection, page) {
if (!page) {
page = this.page;
}
let j = 0;
for (let item of collection) {
if (
item.inputPath === page.inputPath &&
(item.outputPath === page.outputPath || item.url === page.url)
) {
return j;
}
j++;
}
}

View File

@@ -0,0 +1,47 @@
import getCollectionItem from "./GetCollectionItem.js";
// Work with I18n Plugin src/Plugins/I18nPlugin.js to retrieve root pages (not i18n pages)
function resolveRootPage(config, pageOverride, languageCode) {
let localeFilter = config.getFilter("locale_page");
if (!localeFilter || typeof localeFilter !== "function") {
return pageOverride;
}
// returns root/default-language `page` object
return localeFilter.call(this, pageOverride, languageCode);
}
function getLocaleCollectionItem(config, collection, pageOverride, langCode, indexModifier = 0) {
if (!langCode) {
// if page.lang exists (2.0.0-canary.14 and i18n plugin added, use page language)
if (this.page.lang) {
langCode = this.page.lang;
} else {
return getCollectionItem(collection, pageOverride || this.page, indexModifier);
}
}
let rootPage = resolveRootPage.call(this, config, pageOverride); // implied current page, default language
let modifiedRootItem = getCollectionItem(collection, rootPage, indexModifier);
if (!modifiedRootItem) {
return; // no root item exists for the previous/next page
}
// Resolve modified root `page` back to locale `page`
// This will return a non localized version of the page as a fallback
let modifiedLocalePage = resolveRootPage.call(this, config, modifiedRootItem.data.page, langCode);
// already localized (or default language)
if (!("__locale_page_resolved" in modifiedLocalePage)) {
return modifiedRootItem;
}
// find the modified locale `page` again in `collections.all`
let all =
this.collections?.all ||
this.ctx?.collections?.all ||
this.context?.environments?.collections?.all ||
[];
return getCollectionItem(all, modifiedLocalePage, 0);
}
export default getLocaleCollectionItem;

14
node_modules/@11ty/eleventy/src/Filters/Slug.js generated vendored Normal file
View File

@@ -0,0 +1,14 @@
import slugify from "slugify";
export default function (str, options = {}) {
return slugify(
"" + str,
Object.assign(
{
replacement: "-",
lower: true,
},
options,
),
);
}

14
node_modules/@11ty/eleventy/src/Filters/Slugify.js generated vendored Normal file
View File

@@ -0,0 +1,14 @@
import slugify from "@sindresorhus/slugify";
export default function (str, options = {}) {
return slugify(
"" + str,
Object.assign(
{
// lowercase: true, // default
decamelize: false,
},
options,
),
);
}

35
node_modules/@11ty/eleventy/src/Filters/Url.js generated vendored Normal file
View File

@@ -0,0 +1,35 @@
import { TemplatePath } from "@11ty/eleventy-utils";
import isValidUrl from "../Util/ValidUrl.js";
// Note: This filter is used in the Eleventy Navigation plugin in versions prior to 0.3.4
export default function (url, pathPrefix) {
// work with undefined
url = url || "";
if (isValidUrl(url) || (url.startsWith("//") && url !== "//")) {
return url;
}
if (pathPrefix === undefined || typeof pathPrefix !== "string") {
// When you retrieve this with config.getFilter("url") it
// grabs the pathPrefix argument from your config for you (see defaultConfig.js)
throw new Error("pathPrefix (String) is required in the `url` filter.");
}
let normUrl = TemplatePath.normalizeUrlPath(url);
let normRootDir = TemplatePath.normalizeUrlPath("/", pathPrefix);
let normFull = TemplatePath.normalizeUrlPath("/", pathPrefix, url);
let isRootDirTrailingSlash =
normRootDir.length && normRootDir.charAt(normRootDir.length - 1) === "/";
// minor difference with straight `normalize`, "" resolves to root dir and not "."
// minor difference with straight `normalize`, "/" resolves to root dir
if (normUrl === "/" || normUrl === normRootDir) {
return normRootDir + (!isRootDirTrailingSlash ? "/" : "");
} else if (normUrl.indexOf("/") === 0) {
return normFull;
}
return normUrl;
}

424
node_modules/@11ty/eleventy/src/GlobalDependencyMap.js generated vendored Normal file
View File

@@ -0,0 +1,424 @@
import { DepGraph } from "dependency-graph";
import debugUtil from "debug";
import { TemplatePath } from "@11ty/eleventy-utils";
import JavaScriptDependencies from "./Util/JavaScriptDependencies.js";
import PathNormalizer from "./Util/PathNormalizer.js";
const debug = debugUtil("Eleventy:Dependencies");
class GlobalDependencyMap {
// dependency-graph requires these keys to be alphabetic strings
static LAYOUT_KEY = "layout";
static COLLECTION_PREFIX = "__collection:";
#templateConfig;
reset() {
this._map = undefined;
}
setIsEsm(isEsm) {
this.isEsm = isEsm;
}
setTemplateConfig(templateConfig) {
this.#templateConfig = templateConfig;
}
setConfig(config) {
if (this.config) {
return;
}
this.config = config;
// These have leading dot slashes, but so do the paths from Eleventy
this.config.events.once("eleventy.layouts", async (layouts) => {
await this.addLayoutsToMap(layouts);
});
}
filterOutLayouts(nodes = []) {
return nodes.filter((node) => {
let data = this.map.getNodeData(node);
if (data?.type === GlobalDependencyMap.LAYOUT_KEY) {
return false;
}
return true;
});
}
filterOutCollections(nodes = []) {
return nodes.filter((node) => !node.startsWith(GlobalDependencyMap.COLLECTION_PREFIX));
}
removeLayoutNodes(normalizedLayouts) {
let nodes = this.map.overallOrder();
for (let node of nodes) {
let data = this.map.getNodeData(node);
if (!data || !data.type || data.type !== GlobalDependencyMap.LAYOUT_KEY) {
continue;
}
// previous layout is not in the new layout map (no templates are using it)
if (!normalizedLayouts[node]) {
this.map.removeNode(node);
}
// important: if the layout map changed to have different templates (but was not removed)
// this is already handled by `resetNode` called via TemplateMap
}
}
// Eleventy Layouts dont show up in the dependency graph, so we handle those separately
async addLayoutsToMap(layouts) {
let normalizedLayouts = this.normalizeLayoutsObject(layouts);
// Clear out any previous layout relationships to make way for the new ones
this.removeLayoutNodes(normalizedLayouts);
for (let layout in normalizedLayouts) {
// We add this pre-emptively to add the `layout` data
if (!this.map.hasNode(layout)) {
this.map.addNode(layout, {
type: GlobalDependencyMap.LAYOUT_KEY,
});
} else {
this.map.setNodeData(layout, {
type: GlobalDependencyMap.LAYOUT_KEY,
});
}
// Potential improvement: only add the first template in the chain for a template and manage any upstream layouts by their own relationships
for (let pageTemplate of normalizedLayouts[layout]) {
this.addDependency(pageTemplate, [layout]);
}
if (this.#templateConfig?.shouldSpiderJavaScriptDependencies()) {
let deps = await JavaScriptDependencies.getDependencies([layout], this.isEsm);
this.addDependency(layout, deps);
}
}
}
get map() {
if (!this._map) {
this._map = new DepGraph({ circular: true });
}
return this._map;
}
set map(graph) {
this._map = graph;
}
normalizeNode(node) {
if (!node) {
return;
}
// TODO tests for this
// Fix URL objects passed in (sass does this)
if (typeof node !== "string" && "toString" in node) {
node = node.toString();
}
if (typeof node !== "string") {
throw new Error("`addDependencies` files must be strings. Received:" + node);
}
return PathNormalizer.fullNormalization(node);
}
normalizeLayoutsObject(layouts) {
let o = {};
for (let rawLayout in layouts) {
let layout = this.normalizeNode(rawLayout);
o[layout] = layouts[rawLayout].map((entry) => this.normalizeNode(entry));
}
return o;
}
getDependantsFor(node) {
if (!node) {
return new Set();
}
node = this.normalizeNode(node);
if (!this.map.hasNode(node)) {
return new Set();
}
// Direct dependants and dependencies, both publish and consume from collections
return this.map.directDependantsOf(node);
}
hasNode(node) {
return this.map.hasNode(this.normalizeNode(node));
}
findCollectionsRemovedFrom(node, collectionNames) {
if (!this.hasNode(node)) {
return new Set();
}
let prevDeps = this.getDependantsFor(node)
.filter((entry) => {
return entry.startsWith(GlobalDependencyMap.COLLECTION_PREFIX);
})
.map((entry) => {
return GlobalDependencyMap.getEntryFromCollectionKey(entry);
});
let prevDepsSet = new Set(prevDeps);
let deleted = new Set();
for (let dep of prevDepsSet) {
if (!collectionNames.has(dep)) {
deleted.add(dep);
}
}
return deleted;
}
resetNode(node) {
node = this.normalizeNode(node);
if (!this.map.hasNode(node)) {
return;
}
// We dont want to remove relationships that consume this, controlled by the upstream content
// for (let dep of this.map.directDependantsOf(node)) {
// this.map.removeDependency(dep, node);
// }
for (let dep of this.map.directDependenciesOf(node)) {
this.map.removeDependency(node, dep);
}
}
getTemplatesThatConsumeCollections(collectionNames) {
let templates = new Set();
for (let name of collectionNames) {
let collectionName = GlobalDependencyMap.getCollectionKeyForEntry(name);
if (!this.map.hasNode(collectionName)) {
continue;
}
for (let node of this.map.dependantsOf(collectionName)) {
if (!node.startsWith(GlobalDependencyMap.COLLECTION_PREFIX)) {
let data = this.map.getNodeData(node);
if (!data || !data.type || data.type != GlobalDependencyMap.LAYOUT_KEY) {
templates.add(node);
}
}
}
}
return templates;
}
getLayoutsUsedBy(node) {
node = this.normalizeNode(node);
if (!this.map.hasNode(node)) {
return [];
}
let layouts = [];
// include self, if layout
if (this.map.getNodeData(node)?.type === GlobalDependencyMap.LAYOUT_KEY) {
layouts.push(node);
}
this.map.dependantsOf(node).forEach((node) => {
let data = this.map.getNodeData(node);
// we only want layouts
if (data?.type === GlobalDependencyMap.LAYOUT_KEY) {
return layouts.push(node);
}
});
return layouts;
}
// In order
// Does not include original templatePaths (unless *they* are second-order relevant)
getTemplatesRelevantToTemplateList(templatePaths) {
let overallOrder = this.map.overallOrder();
overallOrder = this.filterOutLayouts(overallOrder);
overallOrder = this.filterOutCollections(overallOrder);
let relevantLookup = {};
for (let inputPath of templatePaths) {
inputPath = TemplatePath.stripLeadingDotSlash(inputPath);
let deps = this.getDependencies(inputPath, false);
if (Array.isArray(deps)) {
let paths = this.filterOutCollections(deps);
for (let node of paths) {
relevantLookup[node] = true;
}
}
}
return overallOrder.filter((node) => {
if (relevantLookup[node]) {
return true;
}
return false;
});
}
// Layouts are not relevant to compile cache and can be ignored
getDependencies(node, includeLayouts = true) {
node = this.normalizeNode(node);
// `false` means the Node was unknown
if (!this.map.hasNode(node)) {
return false;
}
return this.map.dependenciesOf(node).filter((node) => {
if (includeLayouts) {
return true;
}
// When includeLayouts is `false` we want to filter out layouts
let data = this.map.getNodeData(node);
if (data?.type === GlobalDependencyMap.LAYOUT_KEY) {
return false;
}
return true;
});
}
// node arguments are already normalized
_addDependency(from, toArray = []) {
this.map.addNode(from);
if (!Array.isArray(toArray)) {
throw new Error("Second argument to `addDependency` must be an Array.");
}
// debug("%o depends on %o", from, toArray);
for (let to of toArray) {
if (!this.map.hasNode(to)) {
this.map.addNode(to);
}
if (from !== to) {
this.map.addDependency(from, to);
}
}
}
addDependency(from, toArray = []) {
this._addDependency(
this.normalizeNode(from),
toArray.map((to) => this.normalizeNode(to)),
);
}
static getEntryFromCollectionKey(entry) {
return entry.slice(GlobalDependencyMap.COLLECTION_PREFIX.length);
}
static getCollectionKeyForEntry(entry) {
return `${GlobalDependencyMap.COLLECTION_PREFIX}${entry}`;
}
addDependencyConsumesCollection(from, collectionName) {
let nodeName = this.normalizeNode(from);
debug("%o depends on collection: %o", nodeName, collectionName);
this._addDependency(nodeName, [GlobalDependencyMap.getCollectionKeyForEntry(collectionName)]);
}
addDependencyPublishesToCollection(from, collectionName) {
let normalizedFrom = this.normalizeNode(from);
this._addDependency(GlobalDependencyMap.getCollectionKeyForEntry(collectionName), [
normalizedFrom,
]);
}
// Layouts are not relevant to compile cache and can be ignored
hasDependency(from, to, includeLayouts) {
to = this.normalizeNode(to);
let deps = this.getDependencies(from, includeLayouts); // normalizes `from`
if (!deps) {
return false;
}
return deps.includes(to);
}
// Layouts are not relevant to compile cache and can be ignored
isFileRelevantTo(fullTemplateInputPath, comparisonFile, includeLayouts) {
fullTemplateInputPath = this.normalizeNode(fullTemplateInputPath);
comparisonFile = this.normalizeNode(comparisonFile);
// No watch/serve changed file
if (!comparisonFile) {
return false;
}
// The file that changed is the relevant file
if (fullTemplateInputPath === comparisonFile) {
return true;
}
// The file that changed is a dependency of the template
// comparisonFile is used by fullTemplateInputPath
if (this.hasDependency(fullTemplateInputPath, comparisonFile, includeLayouts)) {
return true;
}
return false;
}
isFileUsedBy(parent, child, includeLayouts) {
if (this.hasDependency(parent, child, includeLayouts)) {
// child is used by parent
return true;
}
return false;
}
stringify() {
return JSON.stringify(this.map, function replacer(key, value) {
// Serialize internal Map objects.
if (value instanceof Map) {
let obj = {};
for (let [k, v] of value) {
obj[k] = v;
}
return obj;
}
return value;
});
}
restore(persisted) {
let obj = JSON.parse(persisted);
let graph = new DepGraph({ circular: true });
// https://github.com/jriecken/dependency-graph/issues/44
// Restore top level serialized Map objects (in stringify above)
for (let key in obj) {
let map = graph[key];
for (let k in obj[key]) {
let v = obj[key][k];
map.set(k, v);
}
}
this.map = graph;
}
}
export default GlobalDependencyMap;

View File

@@ -0,0 +1,151 @@
import { DeepCopy } from "@11ty/eleventy-utils";
import urlFilter from "../Filters/Url.js";
import PathPrefixer from "../Util/PathPrefixer.js";
import { HtmlTransformer } from "../Util/HtmlTransformer.js";
import isValidUrl from "../Util/ValidUrl.js";
function addPathPrefixToUrl(url, pathPrefix, base) {
let u;
if (base) {
u = new URL(url, base);
} else {
u = new URL(url);
}
// Add pathPrefix **after** url is transformed using base
if (pathPrefix) {
u.pathname = PathPrefixer.joinUrlParts(pathPrefix, u.pathname);
}
return u.toString();
}
// pathprefix is only used when overrideBase is a full URL
function transformUrl(url, base, opts = {}) {
let { pathPrefix, pageUrl } = opts;
// full URL, return as-is
if (isValidUrl(url)) {
return url;
}
// Not a full URL, but with a full base URL
// e.g. relative urls like "subdir/", "../subdir", "./subdir"
if (isValidUrl(base)) {
// convert relative paths to absolute path first using pageUrl
if (pageUrl && !url.startsWith("/")) {
let urlObj = new URL(url, `http://example.com${pageUrl}`);
url = urlObj.pathname + (urlObj.hash || "");
}
return addPathPrefixToUrl(url, pathPrefix, base);
}
// Not a full URL, nor a full base URL (call the built-in `url` filter)
return urlFilter(url, base);
}
function eleventyHtmlBasePlugin(eleventyConfig, defaultOptions = {}) {
let opts = DeepCopy(
{
// eleventyConfig.pathPrefix is new in Eleventy 2.0.0-canary.15
// `base` can be a directory (for path prefix transformations)
// OR a full URL with origin and pathname
baseHref: eleventyConfig.pathPrefix,
extensions: "html",
},
defaultOptions,
);
// `filters` option to rename filters was removed in 3.0.0-alpha.13
// Renaming these would cause issues in other plugins (e.g. RSS)
if (opts.filters !== undefined) {
throw new Error(
"The `filters` option in the HTML Base plugin was removed to prevent future cross-plugin compatibility issues.",
);
}
if (opts.baseHref === undefined) {
throw new Error("The `base` option is required in the HTML Base plugin.");
}
eleventyConfig.addFilter("addPathPrefixToFullUrl", function (url) {
return addPathPrefixToUrl(url, eleventyConfig.pathPrefix);
});
// Apply to one URL
eleventyConfig.addFilter(
"htmlBaseUrl",
/** @this {object} */
function (url, baseOverride, pageUrlOverride) {
let base = baseOverride || opts.baseHref;
// Do nothing with a default base
if (base === "/") {
return url;
}
return transformUrl(url, base, {
pathPrefix: eleventyConfig.pathPrefix,
pageUrl: pageUrlOverride || this.page?.url,
});
},
);
// Apply to a block of HTML
eleventyConfig.addAsyncFilter(
"transformWithHtmlBase",
/** @this {object} */
function (content, baseOverride, pageUrlOverride) {
let base = baseOverride || opts.baseHref;
// Do nothing with a default base
if (base === "/") {
return content;
}
return HtmlTransformer.transformStandalone(content, (url) => {
return transformUrl(url.trim(), base, {
pathPrefix: eleventyConfig.pathPrefix,
pageUrl: pageUrlOverride || this.page?.url,
});
});
},
);
// Apply to all HTML output in your project
eleventyConfig.htmlTransformer.addUrlTransform(
opts.extensions,
/** @this {object} */
function (urlInMarkup) {
// baseHref override is via renderTransforms filter for adding the absolute URL (e.g. https://example.com/pathPrefix/) for RSS/Atom/JSON feeds
return transformUrl(urlInMarkup.trim(), this.baseHref || opts.baseHref, {
pathPrefix: eleventyConfig.pathPrefix,
pageUrl: this.url,
});
},
{
priority: -1, // run last (especially after PathToUrl transform)
enabled: function (context) {
// Enabled when pathPrefix is non-default or via renderTransforms
return context.baseHref || opts.baseHref !== "/";
},
},
);
}
Object.defineProperty(eleventyHtmlBasePlugin, "eleventyPackage", {
value: "@11ty/eleventy/html-base-plugin",
});
Object.defineProperty(eleventyHtmlBasePlugin, "eleventyPluginOptions", {
value: {
unique: true,
},
});
export default eleventyHtmlBasePlugin;
export { transformUrl as applyBaseToUrl };

317
node_modules/@11ty/eleventy/src/Plugins/I18nPlugin.js generated vendored Normal file
View File

@@ -0,0 +1,317 @@
import { bcp47Normalize } from "bcp-47-normalize";
import iso639 from "iso-639-1";
import { DeepCopy } from "@11ty/eleventy-utils";
// pathPrefix note:
// When using `locale_url` filter with the `url` filter, `locale_url` must run first like
// `| locale_url | url`. If you run `| url | locale_url` it wont match correctly.
// TODO improvement would be to throw an error if `locale_url` finds a url with the
// path prefix at the beginning? Would need a better way to know `url` has transformed a string
// rather than just raw comparison.
// e.g. --pathprefix=/en/ should return `/en/en/` for `/en/index.liquid`
class LangUtils {
static getLanguageCodeFromInputPath(filepath) {
return (filepath || "").split("/").find((entry) => Comparator.isLangCode(entry));
}
static getLanguageCodeFromUrl(url) {
let s = (url || "").split("/");
return s.length > 0 && Comparator.isLangCode(s[1]) ? s[1] : "";
}
static swapLanguageCodeNoCheck(str, langCode) {
let found = false;
return str
.split("/")
.map((entry) => {
// only match the first one
if (!found && Comparator.isLangCode(entry)) {
found = true;
return langCode;
}
return entry;
})
.join("/");
}
static swapLanguageCode(str, langCode) {
if (!Comparator.isLangCode(langCode)) {
return str;
}
return LangUtils.swapLanguageCodeNoCheck(str, langCode);
}
}
class Comparator {
// https://en.wikipedia.org/wiki/IETF_language_tag#Relation_to_other_standards
// Requires a ISO-639-1 language code at the start (2 characters before the first -)
static isLangCode(code) {
let [s] = (code || "").split("-");
if (!iso639.validate(s)) {
return false;
}
if (!bcp47Normalize(code)) {
return false;
}
return true;
}
static urlHasLangCode(url, code) {
if (!Comparator.isLangCode(code)) {
return false;
}
return url.split("/").some((entry) => entry === code);
}
}
function normalizeInputPath(inputPath, extensionMap) {
if (extensionMap) {
return extensionMap.removeTemplateExtension(inputPath);
}
return inputPath;
}
/*
* Input: {
* '/en-us/test/': './test/stubs-i18n/en-us/test.11ty.js',
* '/en/test/': './test/stubs-i18n/en/test.liquid',
* '/es/test/': './test/stubs-i18n/es/test.njk',
* '/non-lang-file/': './test/stubs-i18n/non-lang-file.njk'
* }
*
* Output: {
* '/en-us/test/': [ { url: '/en/test/' }, { url: '/es/test/' } ],
* '/en/test/': [ { url: '/en-us/test/' }, { url: '/es/test/' } ],
* '/es/test/': [ { url: '/en-us/test/' }, { url: '/en/test/' } ]
* }
*/
function getLocaleUrlsMap(urlToInputPath, extensionMap, options = {}) {
let filemap = {};
for (let url in urlToInputPath) {
// Group number comes from Pagination.js
let { inputPath: originalFilepath, groupNumber } = urlToInputPath[url];
let filepath = normalizeInputPath(originalFilepath, extensionMap);
let replaced =
LangUtils.swapLanguageCodeNoCheck(filepath, "__11ty_i18n") + `_group:${groupNumber}`;
if (!filemap[replaced]) {
filemap[replaced] = [];
}
let langCode = LangUtils.getLanguageCodeFromInputPath(originalFilepath);
if (!langCode) {
langCode = LangUtils.getLanguageCodeFromUrl(url);
}
if (!langCode) {
langCode = options.defaultLanguage;
}
if (langCode) {
filemap[replaced].push({
url,
lang: langCode,
label: iso639.getNativeName(langCode.split("-")[0]),
});
} else {
filemap[replaced].push({ url });
}
}
// Default sorted by lang code
for (let key in filemap) {
filemap[key].sort(function (a, b) {
if (a.lang < b.lang) {
return -1;
}
if (a.lang > b.lang) {
return 1;
}
return 0;
});
}
// map of input paths => array of localized urls
let urlMap = {};
for (let filepath in filemap) {
for (let entry of filemap[filepath]) {
let url = entry.url;
if (!urlMap[url]) {
urlMap[url] = filemap[filepath].filter((entry) => {
if (entry.lang) {
return true;
}
return entry.url !== url;
});
}
}
}
return urlMap;
}
function eleventyI18nPlugin(eleventyConfig, opts = {}) {
let options = DeepCopy(
{
defaultLanguage: "",
filters: {
url: "locale_url",
links: "locale_links",
},
errorMode: "strict", // allow-fallback, never
},
opts,
);
if (!options.defaultLanguage) {
throw new Error(
"You must specify a `defaultLanguage` in Eleventys Internationalization (I18N) plugin.",
);
}
let extensionMap;
eleventyConfig.on("eleventy.extensionmap", (map) => {
extensionMap = map;
});
let bench = eleventyConfig.benchmarkManager.get("Aggregate");
let contentMaps = {};
eleventyConfig.on("eleventy.contentMap", function ({ urlToInputPath, inputPathToUrl }) {
let b = bench.get("(i18n Plugin) Setting up content map.");
b.before();
contentMaps.inputPathToUrl = inputPathToUrl;
contentMaps.urlToInputPath = urlToInputPath;
contentMaps.localeUrlsMap = getLocaleUrlsMap(urlToInputPath, extensionMap, options);
b.after();
});
eleventyConfig.addGlobalData("eleventyComputed.page.lang", () => {
// if addGlobalData receives a function it will execute it immediately,
// so we return a nested function for computed data
return (data) => {
return LangUtils.getLanguageCodeFromUrl(data.page.url) || options.defaultLanguage;
};
});
// Normalize a theoretical URL based on the current pages language
// If a non-localized file exists, returns the URL without a language assigned
// Fails if no file exists (localized and not localized)
eleventyConfig.addFilter(options.filters.url, function (url, langCodeOverride) {
let langCode =
langCodeOverride ||
LangUtils.getLanguageCodeFromUrl(this.page?.url) ||
options.defaultLanguage;
// Already has a language code on it and has a relevant url with the target language code
if (
contentMaps.localeUrlsMap[url] ||
(!url.endsWith("/") && contentMaps.localeUrlsMap[`${url}/`])
) {
for (let existingUrlObj of contentMaps.localeUrlsMap[url] ||
contentMaps.localeUrlsMap[`${url}/`]) {
if (Comparator.urlHasLangCode(existingUrlObj.url, langCode)) {
return existingUrlObj.url;
}
}
}
// Needs the language code prepended to the URL
let prependedLangCodeUrl = `/${langCode}${url}`;
if (
contentMaps.localeUrlsMap[prependedLangCodeUrl] ||
(!prependedLangCodeUrl.endsWith("/") && contentMaps.localeUrlsMap[`${prependedLangCodeUrl}/`])
) {
return prependedLangCodeUrl;
}
if (
contentMaps.urlToInputPath[url] ||
(!url.endsWith("/") && contentMaps.urlToInputPath[`${url}/`])
) {
// this is not a localized file (independent of a language code)
if (options.errorMode === "strict") {
throw new Error(
`Localized file for URL ${prependedLangCodeUrl} was not found in your project. A non-localized version does exist—are you sure you meant to use the \`${options.filters.url}\` filter for this? You can bypass this error using the \`errorMode\` option in the I18N plugin (current value: "${options.errorMode}").`,
);
}
} else if (options.errorMode === "allow-fallback") {
// Youre linking to a localized file that doesnt exist!
throw new Error(
`Localized file for URL ${prependedLangCodeUrl} was not found in your project! You will need to add it if you want to link to it using the \`${options.filters.url}\` filter. You can bypass this error using the \`errorMode\` option in the I18N plugin (current value: "${options.errorMode}").`,
);
}
return url;
});
// Refactor to use url
// Find the links that are localized alternates to the inputPath argument
eleventyConfig.addFilter(options.filters.links, function (urlOverride) {
let url = urlOverride || this.page?.url;
return (contentMaps.localeUrlsMap[url] || []).filter((entry) => {
return entry.url !== url;
});
});
// Returns a `page`-esque variable for the root default language page
// If paginated, returns first result only
eleventyConfig.addFilter(
"locale_page", // This is not exposed in `options` because it is an Eleventy internals filter (used in get*CollectionItem filters)
function (pageOverride, languageCode) {
// both args here are optional
if (!languageCode) {
languageCode = options.defaultLanguage;
}
let page = pageOverride || this.page;
let url; // new url
if (contentMaps.localeUrlsMap[page.url]) {
for (let entry of contentMaps.localeUrlsMap[page.url]) {
if (entry.lang === languageCode) {
url = entry.url;
}
}
}
let inputPath = LangUtils.swapLanguageCode(page.inputPath, languageCode);
if (
!url ||
!Array.isArray(contentMaps.inputPathToUrl[inputPath]) ||
contentMaps.inputPathToUrl[inputPath].length === 0
) {
// no internationalized pages found
return page;
}
let result = {
// // note that the permalink/slug may be different for the localized file!
url,
inputPath,
filePathStem: LangUtils.swapLanguageCode(page.filePathStem, languageCode),
// outputPath is omitted here, not necessary for GetCollectionItem.js if url is provided
__locale_page_resolved: true,
};
return result;
},
);
}
export { Comparator, LangUtils };
Object.defineProperty(eleventyI18nPlugin, "eleventyPackage", {
value: "@11ty/eleventy/i18n-plugin",
});
Object.defineProperty(eleventyI18nPlugin, "eleventyPluginOptions", {
value: {
unique: true,
},
});
export default eleventyI18nPlugin;

View File

@@ -0,0 +1,103 @@
import matchHelper from "posthtml-match-helper";
import { decodeHTML } from "entities";
import slugifyFilter from "../Filters/Slugify.js";
import MemoizeUtil from "../Util/MemoizeFunction.js";
function getTextNodeContent(node) {
if (node.attrs?.["eleventy:id-ignore"] === "") {
delete node.attrs["eleventy:id-ignore"];
return "";
}
if (!node.content) {
return "";
}
return node.content
.map((entry) => {
if (typeof entry === "string") {
return entry;
}
if (Array.isArray(entry.content)) {
return getTextNodeContent(entry);
}
return "";
})
.join("");
}
function IdAttributePlugin(eleventyConfig, options = {}) {
if (!options.slugify) {
options.slugify = MemoizeUtil(slugifyFilter);
}
if (!options.selector) {
options.selector = "[id],h1,h2,h3,h4,h5,h6";
}
options.decodeEntities = options.decodeEntities ?? true;
options.checkDuplicates = options.checkDuplicates ?? "error";
eleventyConfig.htmlTransformer.addPosthtmlPlugin(
"html",
function (pluginOptions = {}) {
if (typeof options.filter === "function") {
if (options.filter(pluginOptions) === false) {
return function () {};
}
}
return function (tree) {
// One per page
let conflictCheck = {};
// Cache heading nodes for conflict resolution
let headingNodes = {};
tree.match(matchHelper(options.selector), function (node) {
if (node.attrs?.id) {
let id = node.attrs?.id;
if (conflictCheck[id]) {
conflictCheck[id]++;
if (headingNodes[id]) {
// Rename conflicting assigned heading id
let newId = `${id}-${conflictCheck[id]}`;
headingNodes[newId] = headingNodes[id];
headingNodes[newId].attrs.id = newId;
delete headingNodes[id];
} else if (options.checkDuplicates === "error") {
// Existing `id` conflicts with assigned heading id, throw error
throw new Error(
"Duplicate `id` attribute (" +
id +
") in markup on " +
pluginOptions.page.inputPath,
);
}
} else {
conflictCheck[id] = 1;
}
} else if (!node.attrs?.id && node.content) {
node.attrs = node.attrs || {};
let textContent = getTextNodeContent(node);
if (options.decodeEntities) {
textContent = decodeHTML(textContent);
}
let id = options.slugify(textContent);
if (conflictCheck[id]) {
conflictCheck[id]++;
id = `${id}-${conflictCheck[id]}`;
} else {
conflictCheck[id] = 1;
}
headingNodes[id] = node;
node.attrs.id = id;
}
return node;
});
};
} /* , {} // pluginOptions */,
);
}
export { IdAttributePlugin };

View File

@@ -0,0 +1,177 @@
import path from "node:path";
import { TemplatePath } from "@11ty/eleventy-utils";
import isValidUrl from "../Util/ValidUrl.js";
function getValidPath(contentMap, testPath) {
let normalized = TemplatePath.addLeadingDotSlash(testPath);
// it must exist in the content map to be valid
if (contentMap[normalized]) {
return normalized;
}
}
function normalizeInputPath(targetInputPath, inputDir, sourceInputPath, contentMap) {
// inputDir is optional at the beginning of the developer supplied-path
// Input directory already on the input path
if (TemplatePath.join(targetInputPath).startsWith(TemplatePath.join(inputDir))) {
let absolutePath = getValidPath(contentMap, targetInputPath);
if (absolutePath) {
return absolutePath;
}
}
// Relative to project input directory
let relativeToInputDir = getValidPath(contentMap, TemplatePath.join(inputDir, targetInputPath));
if (relativeToInputDir) {
return relativeToInputDir;
}
if (targetInputPath && !path.isAbsolute(targetInputPath)) {
// Relative to source files input path
let sourceInputDir = TemplatePath.getDirFromFilePath(sourceInputPath);
let relativeToSourceFile = getValidPath(
contentMap,
TemplatePath.join(sourceInputDir, targetInputPath),
);
if (relativeToSourceFile) {
return relativeToSourceFile;
}
}
// the transform may have sent in a URL so we just return it as-is
return targetInputPath;
}
function parseFilePath(filepath) {
try {
/* u: URL {
href: 'file:///tmpl.njk#anchor',
origin: 'null',
protocol: 'file:',
username: '',
password: '',
host: '',
hostname: '',
port: '',
pathname: '/tmpl.njk',
search: '',
searchParams: URLSearchParams {},
hash: '#anchor'
} */
// Note that `node:url` -> pathToFileURL creates an absolute path, which we dont want
// URL(`file:#anchor`) gives back a pathname of `/`
let u = new URL(`file:${filepath}`);
filepath = filepath.replace(u.search, "");
filepath = filepath.replace(u.hash, "");
return [
// search includes ?, hash includes #
u.search + u.hash,
filepath,
];
} catch (e) {
return ["", filepath];
}
}
function FilterPlugin(eleventyConfig) {
let contentMap;
eleventyConfig.on("eleventy.contentMap", function ({ inputPathToUrl }) {
contentMap = inputPathToUrl;
});
eleventyConfig.addFilter("inputPathToUrl", function (targetFilePath) {
if (!contentMap) {
throw new Error("Internal error: contentMap not available for `inputPathToUrl` filter.");
}
if (isValidUrl(targetFilePath)) {
return targetFilePath;
}
let inputDir = eleventyConfig.directories.input;
let suffix = "";
[suffix, targetFilePath] = parseFilePath(targetFilePath);
// @ts-ignore
targetFilePath = normalizeInputPath(targetFilePath, inputDir, this.page.inputPath, contentMap);
let urls = contentMap[targetFilePath];
if (!urls || urls.length === 0) {
throw new Error(
"`inputPathToUrl` filter could not find a matching target for " + targetFilePath,
);
}
return `${urls[0]}${suffix}`;
});
}
function TransformPlugin(eleventyConfig, defaultOptions = {}) {
let opts = Object.assign(
{
extensions: "html",
},
defaultOptions,
);
let contentMap = null;
eleventyConfig.on("eleventy.contentMap", function ({ inputPathToUrl }) {
contentMap = inputPathToUrl;
});
eleventyConfig.htmlTransformer.addUrlTransform(opts.extensions, function (targetFilepathOrUrl) {
if (!contentMap) {
throw new Error("Internal error: contentMap not available for the `pathToUrl` Transform.");
}
if (isValidUrl(targetFilepathOrUrl)) {
return targetFilepathOrUrl;
}
let inputDir = eleventyConfig.directories.input;
let suffix = "";
[suffix, targetFilepathOrUrl] = parseFilePath(targetFilepathOrUrl);
targetFilepathOrUrl = normalizeInputPath(
targetFilepathOrUrl,
inputDir,
// @ts-ignore
this.page.inputPath,
contentMap,
);
let urls = contentMap[targetFilepathOrUrl];
if (!targetFilepathOrUrl || !urls || urls.length === 0) {
// fallback, transforms dont error on missing paths (though the pathToUrl filter does)
return `${targetFilepathOrUrl}${suffix}`;
}
return `${urls[0]}${suffix}`;
});
}
Object.defineProperty(FilterPlugin, "eleventyPackage", {
value: "@11ty/eleventy/inputpath-to-url-filter-plugin",
});
Object.defineProperty(FilterPlugin, "eleventyPluginOptions", {
value: {
unique: true,
},
});
Object.defineProperty(TransformPlugin, "eleventyPackage", {
value: "@11ty/eleventy/inputpath-to-url-transform-plugin",
});
Object.defineProperty(TransformPlugin, "eleventyPluginOptions", {
value: {
unique: true,
},
});
export default TransformPlugin;
export { FilterPlugin, TransformPlugin };

380
node_modules/@11ty/eleventy/src/Plugins/Pagination.js generated vendored Executable file
View File

@@ -0,0 +1,380 @@
import { isPlainObject } from "@11ty/eleventy-utils";
import lodash from "@11ty/lodash-custom";
import { DeepCopy } from "@11ty/eleventy-utils";
import EleventyBaseError from "../Errors/EleventyBaseError.js";
import { ProxyWrap } from "../Util/Objects/ProxyWrap.js";
// import { DeepFreeze } from "../Util/Objects/DeepFreeze.js";
import TemplateData from "../Data/TemplateData.js";
const { set: lodashSet, get: lodashGet, chunk: lodashChunk } = lodash;
class PaginationConfigError extends EleventyBaseError {}
class PaginationError extends EleventyBaseError {}
class Pagination {
constructor(tmpl, data, config) {
if (!config) {
throw new PaginationConfigError("Expected `config` argument to Pagination class.");
}
this.config = config;
this.setTemplate(tmpl);
this.setData(data);
}
get inputPathForErrorMessages() {
if (this.template) {
return ` (${this.template.inputPath})`;
}
return "";
}
static hasPagination(data) {
return "pagination" in data;
}
hasPagination() {
if (!this.data) {
throw new Error(
`Missing \`setData\` call for Pagination object${this.inputPathForErrorMessages}`,
);
}
return Pagination.hasPagination(this.data);
}
circularReferenceCheck(data) {
let key = data.pagination.data;
let includedTags = TemplateData.getIncludedTagNames(data);
for (let tag of includedTags) {
if (`collections.${tag}` === key) {
throw new PaginationError(
`Pagination circular reference${this.inputPathForErrorMessages}, data:\`${key}\` iterates over both the \`${tag}\` collection and also supplies pages to that collection.`,
);
}
}
}
setData(data) {
this.data = data || {};
this.target = [];
if (!this.hasPagination()) {
return;
}
if (!data.pagination) {
throw new Error(
`Misconfigured pagination data in template front matter${this.inputPathForErrorMessages} (YAML front matter precaution: did you use tabs and not spaces for indentation?).`,
);
} else if (!("size" in data.pagination)) {
throw new Error(
`Missing pagination size in front matter data${this.inputPathForErrorMessages}`,
);
}
this.circularReferenceCheck(data);
this.size = data.pagination.size;
this.alias = data.pagination.alias;
this.fullDataSet = this._get(this.data, this._getDataKey());
// this returns an array
this.target = this._resolveItems();
this.chunkedItems = this.pagedItems;
}
setTemplate(tmpl) {
this.template = tmpl;
}
_getDataKey() {
return this.data.pagination.data;
}
shouldResolveDataToObjectValues() {
if ("resolve" in this.data.pagination) {
return this.data.pagination.resolve === "values";
}
return false;
}
isFiltered(value) {
if ("filter" in this.data.pagination) {
let filtered = this.data.pagination.filter;
if (Array.isArray(filtered)) {
return filtered.indexOf(value) > -1;
}
return filtered === value;
}
return false;
}
_has(target, key) {
let notFoundValue = "__NOT_FOUND_ERROR__";
let data = lodashGet(target, key, notFoundValue);
return data !== notFoundValue;
}
_get(target, key) {
let notFoundValue = "__NOT_FOUND_ERROR__";
let data = lodashGet(target, key, notFoundValue);
if (data === notFoundValue) {
throw new Error(
`Could not find pagination data${this.inputPathForErrorMessages}, went looking for: ${key}`,
);
}
return data;
}
_resolveItems() {
let keys;
if (Array.isArray(this.fullDataSet)) {
keys = this.fullDataSet;
this.paginationTargetType = "array";
} else if (isPlainObject(this.fullDataSet)) {
this.paginationTargetType = "object";
if (this.shouldResolveDataToObjectValues()) {
keys = Object.values(this.fullDataSet);
} else {
keys = Object.keys(this.fullDataSet);
}
} else {
throw new Error(
`Unexpected data found in pagination target${this.inputPathForErrorMessages}: expected an Array or an Object.`,
);
}
// keys must be an array
let result = keys.slice();
if (this.data.pagination.before && typeof this.data.pagination.before === "function") {
// we dont need to make a copy of this because we .slice() above to create a new copy
let fns = {};
if (this.config) {
fns = this.config.javascriptFunctions;
}
result = this.data.pagination.before.call(fns, result, this.data);
}
if (this.data.pagination.reverse === true) {
result = result.reverse();
}
if (this.data.pagination.filter) {
result = result.filter((value) => !this.isFiltered(value));
}
return result;
}
get pagedItems() {
if (!this.data) {
throw new Error(
`Missing \`setData\` call for Pagination object${this.inputPathForErrorMessages}`,
);
}
const chunks = lodashChunk(this.target, this.size);
if (this.data.pagination?.generatePageOnEmptyData) {
return chunks.length ? chunks : [[]];
} else {
return chunks;
}
}
getPageCount() {
if (!this.hasPagination()) {
return 0;
}
return this.chunkedItems.length;
}
getNormalizedItems(pageItems) {
return this.size === 1 ? pageItems[0] : pageItems;
}
getOverrideDataPages(items, pageNumber) {
return {
// See Issue #345 for more examples
page: {
previous: pageNumber > 0 ? this.getNormalizedItems(items[pageNumber - 1]) : null,
next: pageNumber < items.length - 1 ? this.getNormalizedItems(items[pageNumber + 1]) : null,
first: items.length ? this.getNormalizedItems(items[0]) : null,
last: items.length ? this.getNormalizedItems(items[items.length - 1]) : null,
},
pageNumber,
};
}
getOverrideDataLinks(pageNumber, templateCount, links) {
let obj = {};
// links are okay but hrefs are better
obj.previousPageLink = pageNumber > 0 ? links[pageNumber - 1] : null;
obj.previous = obj.previousPageLink;
obj.nextPageLink = pageNumber < templateCount - 1 ? links[pageNumber + 1] : null;
obj.next = obj.nextPageLink;
obj.firstPageLink = links.length > 0 ? links[0] : null;
obj.lastPageLink = links.length > 0 ? links[links.length - 1] : null;
obj.links = links;
// todo deprecated, consistency with collections and use links instead
obj.pageLinks = links;
return obj;
}
getOverrideDataHrefs(pageNumber, templateCount, hrefs) {
let obj = {};
// hrefs are better than links
obj.previousPageHref = pageNumber > 0 ? hrefs[pageNumber - 1] : null;
obj.nextPageHref = pageNumber < templateCount - 1 ? hrefs[pageNumber + 1] : null;
obj.firstPageHref = hrefs.length > 0 ? hrefs[0] : null;
obj.lastPageHref = hrefs.length > 0 ? hrefs[hrefs.length - 1] : null;
obj.hrefs = hrefs;
// better names
obj.href = {
previous: obj.previousPageHref,
next: obj.nextPageHref,
first: obj.firstPageHref,
last: obj.lastPageHref,
};
return obj;
}
async getPageTemplates() {
if (!this.data) {
throw new Error(
`Missing \`setData\` call for Pagination object${this.inputPathForErrorMessages}`,
);
}
if (!this.hasPagination()) {
return [];
}
let entries = [];
let items = this.chunkedItems;
let pages = this.size === 1 ? items.map((entry) => entry[0]) : items;
let links = [];
let hrefs = [];
let hasPermalinkField = Boolean(this.data[this.config.keys.permalink]);
let hasComputedPermalinkField = Boolean(
this.data.eleventyComputed && this.data.eleventyComputed[this.config.keys.permalink],
);
// Do *not* pass collections through DeepCopy, well re-add them back in later.
let collections = this.data.collections;
if (collections) {
delete this.data.collections;
}
let parentData = DeepCopy(
{
pagination: {
data: this.data.pagination.data,
size: this.data.pagination.size,
alias: this.alias,
pages,
},
},
this.data,
);
// Restore skipped collections
if (collections) {
this.data.collections = collections;
// Keep the original reference to the collections, no deep copy!!
parentData.collections = collections;
}
// TODO this does work fine but lets wait on enabling it.
// DeepFreeze(parentData, ["collections"]);
// TODO future improvement dea: use a light Template wrapper for paged template clones (PagedTemplate?)
// so that we dont have the memory cost of the full template (and can reuse the parent
// template for some things)
let indices = new Set();
for (let j = 0; j <= items.length - 1; j++) {
indices.add(j);
}
for (let pageNumber of indices) {
let cloned = await this.template.clone();
if (pageNumber > 0 && !hasPermalinkField && !hasComputedPermalinkField) {
cloned.setExtraOutputSubdirectory(pageNumber);
}
let paginationData = {
pagination: {
items: items[pageNumber],
},
page: {},
};
Object.assign(paginationData.pagination, this.getOverrideDataPages(items, pageNumber));
if (this.alias) {
lodashSet(paginationData, this.alias, this.getNormalizedItems(items[pageNumber]));
}
// Do *not* deep merge pagination data! See https://github.com/11ty/eleventy/issues/147#issuecomment-440802454
let clonedData = ProxyWrap(paginationData, parentData);
// Previous method:
// let clonedData = DeepCopy(paginationData, parentData);
let { /*linkInstance,*/ rawPath, path, href } = await cloned.getOutputLocations(clonedData);
// TODO subdirectory to links if the site doesnt live at /
if (rawPath) {
links.push("/" + rawPath);
}
hrefs.push(href);
// page.url and page.outputPath are used to avoid another getOutputLocations call later, see Template->addComputedData
clonedData.page.url = href;
clonedData.page.outputPath = path;
entries.push({
pageNumber,
// This is used by i18n Plugin to allow subgroups of nested pagination to be separate
groupNumber: items[pageNumber]?.[0]?.eleventyPaginationGroupNumber,
template: cloned,
data: clonedData,
});
}
// we loop twice to pass in the appropriate prev/next links (already full generated now)
let index = 0;
for (let pageEntry of entries) {
let linksObj = this.getOverrideDataLinks(index, items.length, links);
Object.assign(pageEntry.data.pagination, linksObj);
let hrefsObj = this.getOverrideDataHrefs(index, items.length, hrefs);
Object.assign(pageEntry.data.pagination, hrefsObj);
index++;
}
return entries;
}
}
export default Pagination;

481
node_modules/@11ty/eleventy/src/Plugins/RenderPlugin.js generated vendored Normal file
View File

@@ -0,0 +1,481 @@
import fs from "graceful-fs";
import { Merge, TemplatePath, isPlainObject } from "@11ty/eleventy-utils";
import { evalToken } from "liquidjs";
// TODO add a first-class Markdown component to expose this using Markdown-only syntax (will need to be synchronous for markdown-it)
import { ProxyWrap } from "../Util/Objects/ProxyWrap.js";
import TemplateDataInitialGlobalData from "../Data/TemplateDataInitialGlobalData.js";
import EleventyBaseError from "../Errors/EleventyBaseError.js";
import TemplateRender from "../TemplateRender.js";
import ProjectDirectories from "../Util/ProjectDirectories.js";
import TemplateConfig from "../TemplateConfig.js";
import Liquid from "../Engines/Liquid.js";
class EleventyNunjucksError extends EleventyBaseError {}
/** @this {object} */
async function compile(content, templateLang, options = {}) {
let { templateConfig, extensionMap } = options;
if (!templateConfig) {
templateConfig = new TemplateConfig(null, false);
templateConfig.setDirectories(new ProjectDirectories());
await templateConfig.init();
}
// Breaking change in 2.0+, previous default was `html` and now we default to the page template syntax
if (!templateLang) {
templateLang = this.page.templateSyntax;
}
let tr = new TemplateRender(templateLang, templateConfig);
tr.extensionMap = extensionMap;
if (templateLang) {
await tr.setEngineOverride(templateLang);
} else {
await tr.init();
}
// TODO tie this to the class, not the extension
if (
tr.engine.name === "11ty.js" ||
tr.engine.name === "11ty.cjs" ||
tr.engine.name === "11ty.mjs"
) {
throw new Error(
"11ty.js is not yet supported as a template engine for `renderTemplate`. Use `renderFile` instead!",
);
}
return tr.getCompiledTemplate(content);
}
// No templateLang default, it should infer from the inputPath.
async function compileFile(inputPath, options = {}, templateLang) {
let { templateConfig, extensionMap, config } = options;
if (!inputPath) {
throw new Error("Missing file path argument passed to the `renderFile` shortcode.");
}
if (!fs.existsSync(TemplatePath.normalizeOperatingSystemFilePath(inputPath))) {
throw new Error(
"Could not find render plugin file for the `renderFile` shortcode, looking for: " + inputPath,
);
}
let wasTemplateConfigMissing = false;
if (!templateConfig) {
templateConfig = new TemplateConfig(null, false);
templateConfig.setDirectories(new ProjectDirectories());
wasTemplateConfigMissing = true;
}
if (config && typeof config === "function") {
await config(templateConfig.userConfig);
}
if (wasTemplateConfigMissing) {
await templateConfig.init();
}
let tr = new TemplateRender(inputPath, templateConfig);
tr.extensionMap = extensionMap;
if (templateLang) {
await tr.setEngineOverride(templateLang);
} else {
await tr.init();
}
if (!tr.engine.needsToReadFileContents()) {
return tr.getCompiledTemplate(null);
}
// TODO we could make this work with full templates (with front matter?)
let content = fs.readFileSync(inputPath, "utf8");
return tr.getCompiledTemplate(content);
}
/** @this {object} */
async function renderShortcodeFn(fn, data) {
if (fn === undefined) {
return;
} else if (typeof fn !== "function") {
throw new Error(`The \`compile\` function did not return a function. Received ${fn}`);
}
// if the user passes a string or other literal, remap to an object.
if (!isPlainObject(data)) {
data = {
_: data,
};
}
if ("data" in this && isPlainObject(this.data)) {
// when options.accessGlobalData is true, this allows the global data
// to be accessed inside of the shortcode as a fallback
data = ProxyWrap(data, this.data);
} else {
// save `page` and `eleventy` for reuse
data.page = this.page;
data.eleventy = this.eleventy;
}
return fn(data);
}
/**
* @module 11ty/eleventy/Plugins/RenderPlugin
*/
/**
* A plugin to add shortcodes to render an Eleventy template
* string (or file) inside of another template. {@link https://v3.11ty.dev/docs/plugins/render/}
*
* @since 1.0.0
* @param {module:11ty/eleventy/UserConfig} eleventyConfig - User-land configuration instance.
* @param {object} options - Plugin options
*/
function eleventyRenderPlugin(eleventyConfig, options = {}) {
/**
* @typedef {object} options
* @property {string} [tagName] - The shortcode name to render a template string.
* @property {string} [tagNameFile] - The shortcode name to render a template file.
* @property {module:11ty/eleventy/TemplateConfig} [templateConfig] - Configuration object
* @property {boolean} [accessGlobalData] - Whether or not the template has access to the pages data.
*/
let defaultOptions = {
tagName: "renderTemplate",
tagNameFile: "renderFile",
filterName: "renderContent",
templateConfig: null,
accessGlobalData: false,
};
let opts = Object.assign(defaultOptions, options);
function liquidTemplateTag(liquidEngine, tagName) {
// via https://github.com/harttle/liquidjs/blob/b5a22fa0910c708fe7881ef170ed44d3594e18f3/src/builtin/tags/raw.ts
return {
parse: function (tagToken, remainTokens) {
this.name = tagToken.name;
if (eleventyConfig.liquid.parameterParsing === "builtin") {
this.orderedArgs = Liquid.parseArgumentsBuiltin(tagToken.args);
// note that Liquid does have a Hash class for name-based argument parsing but offers no easy to support both modes in one class
} else {
this.legacyArgs = tagToken.args;
}
this.tokens = [];
var stream = liquidEngine.parser
.parseStream(remainTokens)
.on("token", (token) => {
if (token.name === "end" + tagName) stream.stop();
else this.tokens.push(token);
})
.on("end", () => {
throw new Error(`tag ${tagToken.getText()} not closed`);
});
stream.start();
},
render: function* (ctx) {
let normalizedContext = {};
if (ctx) {
if (opts.accessGlobalData) {
// parent template data cascade
normalizedContext.data = ctx.getAll();
}
normalizedContext.page = ctx.get(["page"]);
normalizedContext.eleventy = ctx.get(["eleventy"]);
}
let argArray = [];
if (this.legacyArgs) {
let rawArgs = Liquid.parseArguments(null, this.legacyArgs);
for (let arg of rawArgs) {
let b = yield liquidEngine.evalValue(arg, ctx);
argArray.push(b);
}
} else if (this.orderedArgs) {
for (let arg of this.orderedArgs) {
let b = yield evalToken(arg, ctx);
argArray.push(b);
}
}
// plaintext paired shortcode content
let body = this.tokens.map((token) => token.getText()).join("");
let ret = _renderStringShortcodeFn.call(
normalizedContext,
body,
// templateLang, data
...argArray,
);
yield ret;
return ret;
},
};
}
// TODO I dont think this works with whitespace control, e.g. {%- endrenderTemplate %}
function nunjucksTemplateTag(NunjucksLib, tagName) {
return new (function () {
this.tags = [tagName];
this.parse = function (parser, nodes) {
var tok = parser.nextToken();
var args = parser.parseSignature(true, true);
const begun = parser.advanceAfterBlockEnd(tok.value);
// This code was ripped from the Nunjucks parser for `raw`
// https://github.com/mozilla/nunjucks/blob/fd500902d7c88672470c87170796de52fc0f791a/nunjucks/src/parser.js#L655
const endTagName = "end" + tagName;
// Look for upcoming raw blocks (ignore all other kinds of blocks)
const rawBlockRegex = new RegExp(
"([\\s\\S]*?){%\\s*(" + tagName + "|" + endTagName + ")\\s*(?=%})%}",
);
let rawLevel = 1;
let str = "";
let matches = null;
// Exit when there's nothing to match
// or when we've found the matching "endraw" block
while ((matches = parser.tokens._extractRegex(rawBlockRegex)) && rawLevel > 0) {
const all = matches[0];
const pre = matches[1];
const blockName = matches[2];
// Adjust rawlevel
if (blockName === tagName) {
rawLevel += 1;
} else if (blockName === endTagName) {
rawLevel -= 1;
}
// Add to str
if (rawLevel === 0) {
// We want to exclude the last "endraw"
str += pre;
// Move tokenizer to beginning of endraw block
parser.tokens.backN(all.length - pre.length);
} else {
str += all;
}
}
let body = new nodes.Output(begun.lineno, begun.colno, [
new nodes.TemplateData(begun.lineno, begun.colno, str),
]);
return new nodes.CallExtensionAsync(this, "run", args, [body]);
};
this.run = function (...args) {
let resolve = args.pop();
let body = args.pop();
let [context, ...argArray] = args;
let normalizedContext = {};
if (context.ctx?.page) {
normalizedContext.ctx = context.ctx;
// TODO .data
// if(opts.accessGlobalData) {
// normalizedContext.data = context.ctx;
// }
normalizedContext.page = context.ctx.page;
normalizedContext.eleventy = context.ctx.eleventy;
}
body(function (e, bodyContent) {
if (e) {
resolve(
new EleventyNunjucksError(`Error with Nunjucks paired shortcode \`${tagName}\``, e),
);
}
Promise.resolve(
_renderStringShortcodeFn.call(
normalizedContext,
bodyContent,
// templateLang, data
...argArray,
),
).then(
function (returnValue) {
resolve(null, new NunjucksLib.runtime.SafeString(returnValue));
},
function (e) {
resolve(
new EleventyNunjucksError(`Error with Nunjucks paired shortcode \`${tagName}\``, e),
null,
);
},
);
});
};
})();
}
// This will only work on 1.0.0-beta.5+ but is only necessary if you want to reuse your config inside of template shortcodes.
// Just rendering raw templates (without filters, shortcodes, etc. from your config) will work fine on old versions.
let templateConfig;
eleventyConfig.on("eleventy.config", (cfg) => {
templateConfig = cfg;
});
let extensionMap;
eleventyConfig.on("eleventy.extensionmap", (map) => {
extensionMap = map;
});
/** @this {object} */
async function _renderStringShortcodeFn(content, templateLang, data = {}) {
// Default is fn(content, templateLang, data) but we want to support fn(content, data) too
if (typeof templateLang !== "string") {
data = templateLang;
templateLang = false;
}
let fn = await compile.call(this, content, templateLang, {
templateConfig: opts.templateConfig || templateConfig,
extensionMap,
});
return renderShortcodeFn.call(this, fn, data);
}
/** @this {object} */
async function _renderFileShortcodeFn(inputPath, data = {}, templateLang) {
let options = {
templateConfig: opts.templateConfig || templateConfig,
extensionMap,
};
let fn = await compileFile.call(this, inputPath, options, templateLang);
return renderShortcodeFn.call(this, fn, data);
}
// Render strings
if (opts.tagName) {
// use falsy to opt-out
eleventyConfig.addJavaScriptFunction(opts.tagName, _renderStringShortcodeFn);
eleventyConfig.addLiquidTag(opts.tagName, function (liquidEngine) {
return liquidTemplateTag(liquidEngine, opts.tagName);
});
eleventyConfig.addNunjucksTag(opts.tagName, function (nunjucksLib) {
return nunjucksTemplateTag(nunjucksLib, opts.tagName);
});
}
// Filter for rendering strings
if (opts.filterName) {
eleventyConfig.addAsyncFilter(opts.filterName, _renderStringShortcodeFn);
}
// Render File
// use `false` to opt-out
if (opts.tagNameFile) {
eleventyConfig.addAsyncShortcode(opts.tagNameFile, _renderFileShortcodeFn);
}
}
// Will re-use the same configuration instance both at a top level and across any nested renders
class RenderManager {
/** @type {Promise|undefined} */
#hasConfigInitialized;
constructor() {
this.templateConfig = new TemplateConfig(null, false);
this.templateConfig.setDirectories(new ProjectDirectories());
// This is the only plugin running on the Edge
this.templateConfig.userConfig.addPlugin(eleventyRenderPlugin, {
templateConfig: this.templateConfig,
accessGlobalData: true,
});
}
async init() {
if (this.#hasConfigInitialized) {
return this.#hasConfigInitialized;
}
if (this.templateConfig.hasInitialized()) {
return true;
}
this.#hasConfigInitialized = this.templateConfig.init();
await this.#hasConfigInitialized;
return true;
}
// `callback` is async-friendly but requires await upstream
config(callback) {
// run an extra `function(eleventyConfig)` configuration callbacks
if (callback && typeof callback === "function") {
return callback(this.templateConfig.userConfig);
}
}
get initialGlobalData() {
if (!this._data) {
this._data = new TemplateDataInitialGlobalData(this.templateConfig);
}
return this._data;
}
// because we dont have access to the full data cascade—but
// we still want configuration data added via `addGlobalData`
async getData(...data) {
await this.init();
let globalData = await this.initialGlobalData.getData();
let merged = Merge({}, globalData, ...data);
return merged;
}
async compile(content, templateLang, options = {}) {
await this.init();
// Missing here: extensionMap
options.templateConfig = this.templateConfig;
// We dont need `compile.call(this)` here because the Edge always uses "liquid" as the template lang (instead of relying on this.page.templateSyntax)
// returns promise
return compile(content, templateLang, options);
}
async render(fn, edgeData, buildTimeData) {
await this.init();
let mergedData = await this.getData(edgeData);
// Set .data for options.accessGlobalData feature
let context = {
data: mergedData,
};
return renderShortcodeFn.call(context, fn, buildTimeData);
}
}
Object.defineProperty(eleventyRenderPlugin, "eleventyPackage", {
value: "@11ty/eleventy/render-plugin",
});
Object.defineProperty(eleventyRenderPlugin, "eleventyPluginOptions", {
value: {
unique: true,
},
});
export default eleventyRenderPlugin;
export { compileFile as File, compile as String, RenderManager };

1124
node_modules/@11ty/eleventy/src/Template.js generated vendored Executable file

File diff suppressed because it is too large Load Diff

85
node_modules/@11ty/eleventy/src/TemplateBehavior.js generated vendored Normal file
View File

@@ -0,0 +1,85 @@
import { isPlainObject } from "@11ty/eleventy-utils";
class TemplateBehavior {
#isRenderOptional;
constructor(config) {
this.render = true;
this.write = true;
this.outputFormat = null;
if (!config) {
throw new Error("Missing config argument in TemplateBehavior");
}
this.config = config;
}
// Render override set to false
isRenderableDisabled() {
return this.renderableOverride === false;
}
isRenderableOptional() {
return this.#isRenderOptional;
}
// undefined (fallback), true, false
setRenderableOverride(renderableOverride) {
if (renderableOverride === "optional") {
this.#isRenderOptional = true;
this.renderableOverride = undefined;
} else {
this.#isRenderOptional = false;
this.renderableOverride = renderableOverride;
}
}
// permalink *has* a build key or output is json/ndjson
isRenderable() {
return this.renderableOverride ?? (this.render || this.isRenderForced());
}
setOutputFormat(format) {
this.outputFormat = format;
}
isRenderForced() {
return this.outputFormat === "json" || this.outputFormat === "ndjson";
}
isWriteable() {
return this.write;
}
// Duplicate logic with TemplatePermalink constructor
setRenderViaDataCascade(data) {
// render is false *only* if `build` key does not exist in permalink objects (both in data and eleventyComputed)
// (note that permalink: false means it wont write but will still render)
let keys = new Set();
if (isPlainObject(data.permalink)) {
for (let key of Object.keys(data.permalink)) {
keys.add(key);
}
}
let computedKey = this.config.keys.computed;
if (computedKey in data && isPlainObject(data[computedKey]?.permalink)) {
for (let key of Object.keys(data[computedKey].permalink)) {
keys.add(key);
}
}
if (keys.size) {
this.render = keys.has("build");
}
}
setFromPermalink(templatePermalink) {
// this.render is duplicated between TemplatePermalink and `setRenderViaDataCascade` above
this.render = templatePermalink._isRendered;
this.write = templatePermalink._writeToFileSystem;
}
}
export default TemplateBehavior;

104
node_modules/@11ty/eleventy/src/TemplateCache.js generated vendored Normal file
View File

@@ -0,0 +1,104 @@
import { TemplatePath } from "@11ty/eleventy-utils";
import eventBus from "./EventBus.js";
// Note: this is only used for TemplateLayout right now but could be used for more
// Just be careful because right now the TemplateLayout cache keys are not directly mapped to paths
// So you may get collisions if you use this for other things.
class TemplateCache {
constructor() {
this.cache = {};
this.cacheByInputPath = {};
}
clear() {
this.cache = {};
this.cacheByInputPath = {};
}
// alias
removeAll() {
this.clear();
}
size() {
return Object.keys(this.cacheByInputPath).length;
}
add(layoutTemplate) {
let keys = new Set();
if (typeof layoutTemplate === "string") {
throw new Error(
"Invalid argument type passed to TemplateCache->add(). Should be a TemplateLayout.",
);
}
if ("getFullKey" in layoutTemplate) {
keys.add(layoutTemplate.getFullKey());
}
if ("getKey" in layoutTemplate) {
// if `key` was an alias, also set to the pathed layout value too
// e.g. `layout: "default"` and `layout: "default.liquid"` will both map to the same template.
keys.add(layoutTemplate.getKey());
}
for (let key of keys) {
this.cache[key] = layoutTemplate;
}
// also the full template input path for use with eleventy --serve/--watch e.g. `_includes/default.liquid` (see `remove` below)
let fullPath = TemplatePath.stripLeadingDotSlash(layoutTemplate.inputPath);
this.cacheByInputPath[fullPath] = layoutTemplate;
}
has(key) {
return key in this.cache;
}
get(key) {
if (!this.has(key)) {
throw new Error(`Could not find ${key} in TemplateCache.`);
}
return this.cache[key];
}
remove(layoutFilePath) {
layoutFilePath = TemplatePath.stripLeadingDotSlash(layoutFilePath);
if (!this.cacheByInputPath[layoutFilePath]) {
// not a layout file
return;
}
let layoutTemplate = this.cacheByInputPath[layoutFilePath];
layoutTemplate.resetCaches();
let keys = layoutTemplate.getCacheKeys();
for (let key of keys) {
delete this.cache[key];
}
delete this.cacheByInputPath[layoutFilePath];
}
}
let layoutCache = new TemplateCache();
eventBus.on("eleventy.resourceModified", (path, usedBy, metadata = {}) => {
// https://github.com/11ty/eleventy-plugin-bundle/issues/10
if (metadata.viaConfigReset) {
layoutCache.removeAll();
} else if (metadata.relevantLayouts?.length) {
// reset the appropriate layouts relevant to the file.
for (let layoutPath of metadata.relevantLayouts || []) {
layoutCache.remove(layoutPath);
}
} else {
layoutCache.remove(path);
}
});
// singleton
export default layoutCache;

81
node_modules/@11ty/eleventy/src/TemplateCollection.js generated vendored Executable file
View File

@@ -0,0 +1,81 @@
import { TemplatePath } from "@11ty/eleventy-utils";
import TemplateData from "./Data/TemplateData.js";
import Sortable from "./Util/Objects/Sortable.js";
import { isGlobMatch } from "./Util/GlobMatcher.js";
class TemplateCollection extends Sortable {
constructor() {
super();
this._filteredByGlobsCache = new Map();
}
getAll() {
return this.items.slice();
}
getAllSorted() {
return this.sort(Sortable.sortFunctionDateInputPath);
}
getSortedByDate() {
return this.sort(Sortable.sortFunctionDate);
}
getGlobs(globs) {
if (typeof globs === "string") {
globs = [globs];
}
globs = globs.map((glob) => TemplatePath.addLeadingDotSlash(glob));
return globs;
}
getFilteredByGlob(globs) {
globs = this.getGlobs(globs);
let key = globs.join("::");
if (!this._dirty) {
// Try to find a pre-sorted list and clone it.
if (this._filteredByGlobsCache.has(key)) {
return [...this._filteredByGlobsCache.get(key)];
}
} else if (this._filteredByGlobsCache.size) {
// Blow away cache
this._filteredByGlobsCache = new Map();
}
let filtered = this.getAllSorted().filter((item) => {
return isGlobMatch(item.inputPath, globs);
});
this._dirty = false;
this._filteredByGlobsCache.set(key, [...filtered]);
return filtered;
}
getFilteredByTag(tagName) {
return this.getAllSorted().filter((item) => {
if (!tagName || TemplateData.getIncludedTagNames(item.data).includes(tagName)) {
return true;
}
return false;
});
}
getFilteredByTags(...tags) {
return this.getAllSorted().filter((item) => {
let itemTags = TemplateData.getIncludedTagNames(item.data);
return tags.every((requiredTag) => {
if (Array.isArray(itemTags)) {
return itemTags.includes(requiredTag);
} else {
return itemTags === requiredTag;
}
});
});
}
}
export default TemplateCollection;

551
node_modules/@11ty/eleventy/src/TemplateConfig.js generated vendored Normal file
View File

@@ -0,0 +1,551 @@
import fs from "node:fs";
import chalk from "kleur";
import { Merge, TemplatePath, isPlainObject } from "@11ty/eleventy-utils";
import debugUtil from "debug";
import { EleventyImportRaw, EleventyImportRawFromEleventy } from "./Util/Require.js";
import EleventyBaseError from "./Errors/EleventyBaseError.js";
import UserConfig from "./UserConfig.js";
import GlobalDependencyMap from "./GlobalDependencyMap.js";
import ExistsCache from "./Util/ExistsCache.js";
import eventBus from "./EventBus.js";
import ProjectTemplateFormats from "./Util/ProjectTemplateFormats.js";
const debug = debugUtil("Eleventy:TemplateConfig");
const debugDev = debugUtil("Dev:Eleventy:TemplateConfig");
/**
* @module 11ty/eleventy/TemplateConfig
*/
/**
* Config as used by the template.
* @typedef {object} module:11ty/eleventy/TemplateConfig~TemplateConfig~config
* @property {String} [pathPrefix] - The path prefix.
*/
/**
* Errors in eleventy config.
* @ignore
*/
class EleventyConfigError extends EleventyBaseError {}
/**
* Errors in eleventy plugins.
* @ignore
*/
class EleventyPluginError extends EleventyBaseError {}
/**
* Config for a template.
* @ignore
* @param {{}} customRootConfig - tbd.
* @param {String} projectConfigPath - Path to local project config.
*/
class TemplateConfig {
#templateFormats;
#runMode;
#configManuallyDefined = false;
/** @type {UserConfig} */
#userConfig = new UserConfig();
constructor(customRootConfig, projectConfigPath) {
/** @type {object} */
this.overrides = {};
/**
* @type {String}
* @description Path to local project config.
* @default .eleventy.js
*/
if (projectConfigPath !== undefined) {
this.#configManuallyDefined = true;
if (!projectConfigPath) {
// falsy skips config files
this.projectConfigPaths = [];
} else {
this.projectConfigPaths = [projectConfigPath];
}
} else {
this.projectConfigPaths = [
".eleventy.js",
"eleventy.config.js",
"eleventy.config.mjs",
"eleventy.config.cjs",
];
}
if (customRootConfig) {
/**
* @type {object}
* @description Custom root config.
*/
this.customRootConfig = customRootConfig;
debug("Warning: Using custom root config!");
} else {
this.customRootConfig = null;
}
this.hasConfigMerged = false;
this.isEsm = false;
}
get userConfig() {
return this.#userConfig;
}
get aggregateBenchmark() {
return this.userConfig.benchmarks.aggregate;
}
/* Setter for Logger */
setLogger(logger) {
this.logger = logger;
this.userConfig.logger = this.logger;
}
/* Setter for Directories instance */
setDirectories(directories) {
this.directories = directories;
this.userConfig.directories = directories.getUserspaceInstance();
}
/* Setter for TemplateFormats instance */
setTemplateFormats(templateFormats) {
this.#templateFormats = templateFormats;
}
get templateFormats() {
if (!this.#templateFormats) {
this.#templateFormats = new ProjectTemplateFormats();
}
return this.#templateFormats;
}
/* Backwards compat */
get inputDir() {
return this.directories.input;
}
setRunMode(runMode) {
this.#runMode = runMode;
}
shouldSpiderJavaScriptDependencies() {
// not for a standard build
return (
(this.#runMode === "watch" || this.#runMode === "serve") &&
this.userConfig.watchJavaScriptDependencies
);
}
/**
* Normalises local project config file path.
*
* @method
* @returns {String|undefined} - The normalised local project config file path.
*/
getLocalProjectConfigFile() {
let configFiles = this.getLocalProjectConfigFiles();
// Add the configFiles[0] in case of a test, where no file exists on the file system
let configFile = configFiles.find((path) => path && fs.existsSync(path)) || configFiles[0];
if (configFile) {
return configFile;
}
}
getLocalProjectConfigFiles() {
if (this.projectConfigPaths?.length > 0) {
return TemplatePath.addLeadingDotSlashArray(this.projectConfigPaths.filter((path) => path));
}
return [];
}
setProjectUsingEsm(isEsmProject) {
this.isEsm = !!isEsmProject;
this.usesGraph.setIsEsm(isEsmProject);
}
getIsProjectUsingEsm() {
return this.isEsm;
}
/**
* Resets the configuration.
*/
async reset() {
debugDev("Resetting configuration: TemplateConfig and UserConfig.");
this.userConfig.reset();
// await this.initializeRootConfig();
await this.forceReloadConfig();
this.usesGraph.reset();
// Clear the compile cache
eventBus.emit("eleventy.compileCacheReset");
}
/**
* Resets the configuration while in watch mode.
*
* @todo Add implementation.
*/
resetOnWatch() {
// nothing yet
}
hasInitialized() {
return this.hasConfigMerged;
}
/**
* Async-friendly init method
*/
async init(overrides) {
await this.initializeRootConfig();
if (overrides) {
this.appendToRootConfig(overrides);
}
this.config = await this.mergeConfig();
this.hasConfigMerged = true;
}
/**
* Force a reload of the configuration object.
*/
async forceReloadConfig() {
this.hasConfigMerged = false;
await this.init();
}
/**
* Returns the config object.
*
* @returns {{}} - The config object.
*/
getConfig() {
if (!this.hasConfigMerged) {
throw new Error("Invalid call to .getConfig(). Needs an .init() first.");
}
return this.config;
}
/**
* Overwrites the config path.
*
* @param {String} path - The new config path.
*/
async setProjectConfigPath(path) {
this.#configManuallyDefined = true;
if (path !== undefined) {
this.projectConfigPaths = [path];
} else {
this.projectConfigPaths = [];
}
if (this.hasConfigMerged) {
// merge it again
debugDev("Merging in getConfig again after setting the local project config path.");
await this.forceReloadConfig();
}
}
/**
* Overwrites the path prefix.
*
* @param {String} pathPrefix - The new path prefix.
*/
setPathPrefix(pathPrefix) {
if (pathPrefix && pathPrefix !== "/") {
debug("Setting pathPrefix to %o", pathPrefix);
this.overrides.pathPrefix = pathPrefix;
}
}
/**
* Gets the current path prefix denoting the root folder the output will be deployed to
*
* @returns {String} - The path prefix string
*/
getPathPrefix() {
if (this.overrides.pathPrefix) {
return this.overrides.pathPrefix;
}
if (!this.hasConfigMerged) {
throw new Error("Config has not yet merged. Needs `init()`.");
}
return this.config?.pathPrefix;
}
/**
* Bootstraps the config object.
*/
async initializeRootConfig() {
this.rootConfig = this.customRootConfig;
if (!this.rootConfig) {
let { default: cfg } = await EleventyImportRawFromEleventy("./src/defaultConfig.js");
this.rootConfig = cfg;
}
if (typeof this.rootConfig === "function") {
// Not yet using async in defaultConfig.js
this.rootConfig = this.rootConfig.call(this, this.userConfig);
}
debug("Default Eleventy config %o", this.rootConfig);
}
/*
* Add additional overrides to the root config object, used for testing
*
* @param {object} - a subset of the return Object from the users config file.
*/
appendToRootConfig(obj) {
Object.assign(this.rootConfig, obj);
}
/*
* Process the userland plugins from the Config
*
* @param {object} - the return Object from the users config file.
*/
async processPlugins({ dir, pathPrefix }) {
this.userConfig.dir = dir;
this.userConfig.pathPrefix = pathPrefix;
// for Nested addPlugin calls, Issue #1925
this.userConfig._enablePluginExecution();
let storedActiveNamespace = this.userConfig.activeNamespace;
for (let { plugin, options, pluginNamespace } of this.userConfig.plugins) {
try {
this.userConfig.activeNamespace = pluginNamespace;
await this.userConfig._executePlugin(plugin, options);
} catch (e) {
let name = this.userConfig._getPluginName(plugin);
let namespaces = [storedActiveNamespace, pluginNamespace].filter((entry) => !!entry);
let namespaceStr = "";
if (namespaces.length) {
namespaceStr = ` (namespace: ${namespaces.join(".")})`;
}
throw new EleventyPluginError(
`Error processing ${name ? `the \`${name}\`` : "a"} plugin${namespaceStr}`,
e,
);
}
}
this.userConfig.activeNamespace = storedActiveNamespace;
this.userConfig._disablePluginExecution();
}
/**
* Fetches and executes the local configuration file
*
* @returns {Promise<object>} merged - The merged config file object.
*/
async requireLocalConfigFile() {
let localConfig = {};
let exportedConfig = {};
let path = this.projectConfigPaths.filter((path) => path).find((path) => fs.existsSync(path));
if (this.projectConfigPaths.length > 0 && this.#configManuallyDefined && !path) {
throw new EleventyConfigError(
"A configuration file was specified but not found: " + this.projectConfigPaths.join(", "),
);
}
debug(`Merging default config with ${path}`);
if (path) {
try {
let { default: configDefaultReturn, config: exportedConfigObject } =
await EleventyImportRaw(path, this.isEsm ? "esm" : "cjs");
exportedConfig = exportedConfigObject || {};
if (this.directories && Object.keys(exportedConfigObject?.dir || {}).length > 0) {
debug(
"Setting directories via `config.dir` export from config file: %o",
exportedConfigObject.dir,
);
this.directories.setViaConfigObject(exportedConfigObject.dir);
}
if (typeof configDefaultReturn === "function") {
localConfig = await configDefaultReturn(this.userConfig);
} else {
localConfig = configDefaultReturn;
}
// Removed a check for `filters` in 3.0.0-alpha.6 (now using addTransform instead) https://v3.11ty.dev/docs/config/#transforms
} catch (err) {
let isModuleError =
err instanceof Error && (err?.message || "").includes("Cannot find module");
// TODO the error message here is bad and I feel bad (needs more accurate info)
return Promise.reject(
new EleventyConfigError(
`Error in your Eleventy config file '${path}'.` +
(isModuleError ? chalk.cyan(" You may need to run `npm install`.") : ""),
err,
),
);
}
} else {
debug(
"Project config file not found (not an error—skipping). Looked in: %o",
this.projectConfigPaths,
);
}
return {
localConfig,
exportedConfig,
};
}
/**
* Merges different config files together.
*
* @returns {Promise<object>} merged - The merged config file.
*/
async mergeConfig() {
let { localConfig, exportedConfig } = await this.requireLocalConfigFile();
// Merge `export const config = {}` with `return {}` in config callback
if (isPlainObject(exportedConfig)) {
localConfig = Merge(localConfig || {}, exportedConfig);
}
if (this.directories) {
if (Object.keys(this.userConfig.directoryAssignments || {}).length > 0) {
debug(
"Setting directories via set*Directory configuration APIs %o",
this.userConfig.directoryAssignments,
);
this.directories.setViaConfigObject(this.userConfig.directoryAssignments);
}
if (localConfig && Object.keys(localConfig?.dir || {}).length > 0) {
debug(
"Setting directories via `dir` object return from configuration file: %o",
localConfig.dir,
);
this.directories.setViaConfigObject(localConfig.dir);
}
}
// `templateFormats` is an override via `setTemplateFormats`
if (this.userConfig?.templateFormats) {
this.templateFormats.setViaConfig(this.userConfig.templateFormats);
} else if (localConfig?.templateFormats || this.rootConfig?.templateFormats) {
// Local project config or defaultConfig.js
this.templateFormats.setViaConfig(
localConfig.templateFormats || this.rootConfig?.templateFormats,
);
}
// `templateFormatsAdded` is additive via `addTemplateFormats`
if (this.userConfig?.templateFormatsAdded) {
this.templateFormats.addViaConfig(this.userConfig.templateFormatsAdded);
}
let mergedConfig = Merge({}, this.rootConfig, localConfig);
// Setup a few properties for plugins:
// Set frozen templateFormats
mergedConfig.templateFormats = Object.freeze(this.templateFormats.getTemplateFormats());
// Setup pathPrefix set via command line for plugin consumption
if (this.overrides.pathPrefix) {
mergedConfig.pathPrefix = this.overrides.pathPrefix;
}
// Returning a falsy value (e.g. "") from user config should reset to the default value.
if (!mergedConfig.pathPrefix) {
mergedConfig.pathPrefix = this.rootConfig.pathPrefix;
}
// This is not set in UserConfig.js so that getters arent converted to strings
// We want to error if someone attempts to use a setter there.
if (this.directories) {
mergedConfig.directories = this.directories.getUserspaceInstance();
}
// Delay processing plugins until after the result of localConfig is returned
// But BEFORE the rest of the config options are merged
// this way we can pass directories and other template information to plugins
await this.userConfig.events.emit("eleventy.beforeConfig", this.userConfig);
let pluginsBench = this.aggregateBenchmark.get("Processing plugins in config");
pluginsBench.before();
await this.processPlugins(mergedConfig);
pluginsBench.after();
// Template formats added via plugins
if (this.userConfig?.templateFormatsAdded) {
this.templateFormats.addViaConfig(this.userConfig.templateFormatsAdded);
mergedConfig.templateFormats = Object.freeze(this.templateFormats.getTemplateFormats());
}
let eleventyConfigApiMergingObject = this.userConfig.getMergingConfigObject();
if ("templateFormats" in eleventyConfigApiMergingObject) {
throw new Error(
"Internal error: templateFormats should not return from `getMergingConfigObject`",
);
}
// Overrides are only used by pathPrefix
debug("Configuration overrides: %o", this.overrides);
Merge(mergedConfig, eleventyConfigApiMergingObject, this.overrides);
debug("Current configuration: %o", mergedConfig);
// Add to the merged config too
mergedConfig.uses = this.usesGraph;
// this is used for the layouts event
this.usesGraph.setConfig(mergedConfig);
return mergedConfig;
}
get usesGraph() {
if (!this._usesGraph) {
this._usesGraph = new GlobalDependencyMap();
this._usesGraph.setIsEsm(this.isEsm);
this._usesGraph.setTemplateConfig(this);
}
return this._usesGraph;
}
get uses() {
if (!this.usesGraph) {
throw new Error("The Eleventy Global Dependency Graph has not yet been initialized.");
}
return this.usesGraph;
}
get existsCache() {
if (!this._existsCache) {
this._existsCache = new ExistsCache();
this._existsCache.setDirectoryCheck(true);
}
return this._existsCache;
}
}
export default TemplateConfig;

712
node_modules/@11ty/eleventy/src/TemplateContent.js generated vendored Normal file
View File

@@ -0,0 +1,712 @@
import os from "node:os";
import fs from "graceful-fs";
import matter from "gray-matter";
import lodash from "@11ty/lodash-custom";
import { TemplatePath } from "@11ty/eleventy-utils";
import debugUtil from "debug";
import chardet from "chardet";
import EleventyExtensionMap from "./EleventyExtensionMap.js";
import TemplateData from "./Data/TemplateData.js";
import TemplateRender from "./TemplateRender.js";
import EleventyBaseError from "./Errors/EleventyBaseError.js";
import EleventyErrorUtil from "./Errors/EleventyErrorUtil.js";
import eventBus from "./EventBus.js";
const { set: lodashSet } = lodash;
const debug = debugUtil("Eleventy:TemplateContent");
const debugDiagnostic = debugUtil("Eleventy:Diagnostics");
const debugDev = debugUtil("Dev:Eleventy:TemplateContent");
class TemplateContentConfigError extends EleventyBaseError {}
class TemplateContentFrontMatterError extends EleventyBaseError {}
class TemplateContentCompileError extends EleventyBaseError {}
class TemplateContentRenderError extends EleventyBaseError {}
class TemplateContent {
constructor(inputPath, templateConfig) {
if (!templateConfig || templateConfig.constructor.name !== "TemplateConfig") {
throw new TemplateContentConfigError(
"Missing or invalid `templateConfig` argument to TemplateContent",
);
}
this.eleventyConfig = templateConfig;
this.inputPath = inputPath;
}
get dirs() {
return this.eleventyConfig.directories;
}
get inputDir() {
return this.dirs.input;
}
get outputDir() {
return this.dirs.output;
}
getResetTypes(types) {
if (types) {
return Object.assign(
{
data: false,
read: false,
render: false,
},
types,
);
}
return {
data: true,
read: true,
render: true,
};
}
// Called during an incremental build when the template instance is cached but needs to be reset because it has changed
resetCaches(types) {
types = this.getResetTypes(types);
if (types.read) {
delete this.readingPromise;
delete this.inputContent;
delete this._frontMatterDataCache;
}
}
/* Used by tests */
get extensionMap() {
if (!this._extensionMap) {
this._extensionMap = new EleventyExtensionMap(this.eleventyConfig);
this._extensionMap.setFormats([]);
}
return this._extensionMap;
}
set extensionMap(map) {
this._extensionMap = map;
}
set eleventyConfig(config) {
this._config = config;
if (this._config.constructor.name === "TemplateConfig") {
this._configOptions = this._config.getConfig();
} else {
throw new TemplateContentConfigError("Tried to get an TemplateConfig but none was found.");
}
}
get eleventyConfig() {
if (this._config.constructor.name === "TemplateConfig") {
return this._config;
}
throw new TemplateContentConfigError("Tried to get an TemplateConfig but none was found.");
}
get config() {
if (this._config.constructor.name === "TemplateConfig" && !this._configOptions) {
this._configOptions = this._config.getConfig();
}
return this._configOptions;
}
get bench() {
return this.config.benchmarkManager.get("Aggregate");
}
get engine() {
return this.templateRender.engine;
}
get templateRender() {
if (!this.hasTemplateRender()) {
throw new Error(`\`templateRender\` has not yet initialized on ${this.inputPath}`);
}
return this._templateRender;
}
hasTemplateRender() {
return !!this._templateRender;
}
async getTemplateRender() {
if (!this._templateRender) {
this._templateRender = new TemplateRender(this.inputPath, this.eleventyConfig);
this._templateRender.extensionMap = this.extensionMap;
await this._templateRender.init();
}
return this._templateRender;
}
// For monkey patchers
get frontMatter() {
if (this.frontMatterOverride) {
return this.frontMatterOverride;
} else {
throw new Error(
"Unfortunately youre using code that monkey patched some Eleventy internals and it isnt async-friendly. Change your code to use the async `read()` method on the template instead!",
);
}
}
// For monkey patchers
set frontMatter(contentOverride) {
this.frontMatterOverride = contentOverride;
}
getInputPath() {
return this.inputPath;
}
getInputDir() {
return this.inputDir;
}
isVirtualTemplate() {
let def = this.getVirtualTemplateDefinition();
return !!def;
}
getVirtualTemplateDefinition() {
let inputDirRelativeInputPath =
this.eleventyConfig.directories.getInputPathRelativeToInputDirectory(this.inputPath);
return this.config.virtualTemplates[inputDirRelativeInputPath];
}
async #read() {
let content = await this.inputContent;
if (content || content === "") {
if (this.engine.useJavaScriptImport()) {
return {
data: {},
content,
};
}
let options = this.config.frontMatterParsingOptions || {};
let fm;
try {
// Added in 3.0, passed along to front matter engines
options.filePath = this.inputPath;
fm = matter(content, options);
} catch (e) {
throw new TemplateContentFrontMatterError(
`Having trouble reading front matter from template ${this.inputPath}`,
e,
);
}
if (options.excerpt && fm.excerpt) {
let excerptString = fm.excerpt + (options.excerpt_separator || "---");
if (fm.content.startsWith(excerptString + os.EOL)) {
// with an os-specific newline after excerpt separator
fm.content = fm.excerpt.trim() + "\n" + fm.content.slice((excerptString + os.EOL).length);
} else if (fm.content.startsWith(excerptString + "\n")) {
// with a newline (\n) after excerpt separator
// This is necessary for some git configurations on windows
fm.content = fm.excerpt.trim() + "\n" + fm.content.slice((excerptString + 1).length);
} else if (fm.content.startsWith(excerptString)) {
// no newline after excerpt separator
fm.content = fm.excerpt + fm.content.slice(excerptString.length);
}
// alias, defaults to page.excerpt
let alias = options.excerpt_alias || "page.excerpt";
lodashSet(fm.data, alias, fm.excerpt);
}
// For monkey patchers that used `frontMatter` 🤧
// https://github.com/11ty/eleventy/issues/613#issuecomment-999637109
// https://github.com/11ty/eleventy/issues/2710#issuecomment-1373854834
// Removed this._frontMatter monkey patcher help in 3.0.0-alpha.7
return fm;
} else {
return {
data: {},
content: "",
excerpt: "",
};
}
}
async read() {
if (!this.readingPromise) {
if (!this.inputContent) {
// @cachedproperty
this.inputContent = this.getInputContent();
}
// @cachedproperty
this.readingPromise = this.#read();
}
return this.readingPromise;
}
/* Incremental builds cache the Template instances (in TemplateWriter) but
* these template specific caches are important for Pagination */
static cache(path, content) {
this._inputCache.set(TemplatePath.absolutePath(path), content);
}
static getCached(path) {
return this._inputCache.get(TemplatePath.absolutePath(path));
}
static deleteFromInputCache(path) {
this._inputCache.delete(TemplatePath.absolutePath(path));
}
// Used via clone
setInputContent(content) {
this.inputContent = content;
}
async getInputContent() {
let tr = await this.getTemplateRender();
let virtualTemplateDefinition = this.getVirtualTemplateDefinition();
if (virtualTemplateDefinition) {
let { content } = virtualTemplateDefinition;
return content;
}
if (
tr.engine.useJavaScriptImport() &&
typeof tr.engine.getInstanceFromInputPath === "function"
) {
return tr.engine.getInstanceFromInputPath(this.inputPath);
}
if (!tr.engine.needsToReadFileContents()) {
return "";
}
let templateBenchmark = this.bench.get("Template Read");
templateBenchmark.before();
let content;
if (this.config.useTemplateCache) {
content = TemplateContent.getCached(this.inputPath);
}
if (!content && content !== "") {
let contentBuffer = fs.readFileSync(this.inputPath);
if (process.env.DEBUG) {
// Warning: this is slow!!
let encoding = chardet.detect(contentBuffer);
if (encoding.startsWith("UTF-16")) {
debug("Warning: %o encoding detected on %o.", encoding, this.inputPath);
debugDiagnostic(
"%o encoding detected on %o. Please re-save as UTF-8.",
encoding,
this.inputPath,
);
} else {
debug("%o encoding detected on %o.", encoding, this.inputPath);
}
}
content = contentBuffer.toString("utf8");
if (this.config.useTemplateCache) {
TemplateContent.cache(this.inputPath, content);
}
}
templateBenchmark.after();
return content;
}
async _testGetFrontMatter() {
let fm = this.frontMatterOverride ? this.frontMatterOverride : await this.read();
return fm;
}
async getPreRender() {
let fm = this.frontMatterOverride ? this.frontMatterOverride : await this.read();
return fm.content;
}
async #getFrontMatterData() {
let fm = await this.read();
// gray-matter isnt async-friendly but can return a promise from custom front matter
if (fm.data instanceof Promise) {
fm.data = await fm.data;
}
let extraData = await this.engine.getExtraDataFromFile(this.inputPath);
let virtualTemplateDefinition = this.getVirtualTemplateDefinition();
let virtualTemplateData;
if (virtualTemplateDefinition) {
virtualTemplateData = virtualTemplateDefinition.data;
}
let data = TemplateData.mergeDeep(false, fm.data, extraData, virtualTemplateData);
let cleanedData = TemplateData.cleanupData(data);
return {
data: cleanedData,
excerpt: fm.excerpt,
};
}
async getFrontMatterData() {
if (!this._frontMatterDataCache) {
// @cachedproperty
this._frontMatterDataCache = this.#getFrontMatterData();
}
return this._frontMatterDataCache;
}
async getEngineOverride() {
let { data: frontMatterData } = await this.getFrontMatterData();
return frontMatterData[this.config.keys.engineOverride];
}
_getCompileCache(str) {
// Caches used to be bifurcated based on engine name, now theyre based on inputPath
let inputPathMap = TemplateContent._compileCache.get(this.inputPath);
if (!inputPathMap) {
inputPathMap = new Map();
TemplateContent._compileCache.set(this.inputPath, inputPathMap);
}
let cacheable = this.engine.cacheable;
let { useCache, key } = this.engine.getCompileCacheKey(str, this.inputPath);
// We also tie the compile cache key to the UserConfig instance, to alleviate issues with global template cache
// Better to move the cache to the Eleventy instance instead, no?
// (This specifically failed I18nPluginTest cases with filters being cached across tests and not having access to each plugins options)
key = this.eleventyConfig.userConfig._getUniqueId() + key;
return [cacheable, key, inputPathMap, useCache];
}
async compile(str, options = {}) {
let { type, bypassMarkdown, engineOverride } = options;
let tr = await this.getTemplateRender();
if (engineOverride !== undefined) {
debugDev("%o overriding template engine to use %o", this.inputPath, engineOverride);
await tr.setEngineOverride(engineOverride, bypassMarkdown);
} else {
tr.setUseMarkdown(!bypassMarkdown);
}
if (bypassMarkdown && !this.engine.needsCompilation(str)) {
return function () {
return str;
};
}
debugDev("%o compile() using engine: %o", this.inputPath, tr.engineName);
try {
let res;
if (this.config.useTemplateCache) {
let [cacheable, key, cache, useCache] = this._getCompileCache(str);
if (cacheable && key) {
if (useCache && cache.has(key)) {
this.bench.get("(count) Template Compile Cache Hit").incrementCount();
return cache.get(key);
}
this.bench.get("(count) Template Compile Cache Miss").incrementCount();
// Compile cache is cleared when the resource is modified (below)
// Compilation is async, so we eagerly cache a Promise that eventually
// resolves to the compiled function
cache.set(
key,
new Promise((resolve) => {
res = resolve;
}),
);
}
}
let typeStr = type ? ` ${type}` : "";
let templateBenchmark = this.bench.get(`Template Compile${typeStr}`);
let inputPathBenchmark = this.bench.get(`> Compile${typeStr} > ${this.inputPath}`);
templateBenchmark.before();
inputPathBenchmark.before();
let fn = await tr.getCompiledTemplate(str);
inputPathBenchmark.after();
templateBenchmark.after();
debugDev("%o getCompiledTemplate function created", this.inputPath);
if (this.config.useTemplateCache && res) {
res(fn);
}
return fn;
} catch (e) {
let [cacheable, key, cache] = this._getCompileCache(str);
if (cacheable && key) {
cache.delete(key);
}
debug(`Having trouble compiling template ${this.inputPath}: %O`, str);
throw new TemplateContentCompileError(
`Having trouble compiling template ${this.inputPath}`,
e,
);
}
}
getParseForSymbolsFunction(str) {
let engine = this.engine;
// Dont use markdown as the engine to parse for symbols
// TODO pass in engineOverride here
let preprocessorEngine = this.templateRender.getPreprocessorEngine();
if (preprocessorEngine && engine.getName() !== preprocessorEngine) {
let replacementEngine = this.templateRender.getEngineByName(preprocessorEngine);
if (replacementEngine) {
engine = replacementEngine;
}
}
if ("parseForSymbols" in engine) {
return () => {
return engine.parseForSymbols(str);
};
}
}
// used by computed data or for permalink functions
async _renderFunction(fn, ...args) {
let mixins = Object.assign({}, this.config.javascriptFunctions);
let result = await fn.call(mixins, ...args);
// normalize Buffer away if returned from permalink
if (Buffer.isBuffer(result)) {
return result.toString();
}
return result;
}
async renderComputedData(str, data) {
if (typeof str === "function") {
return this._renderFunction(str, data);
}
return this._render(str, data, {
type: "Computed Data",
bypassMarkdown: true,
});
}
async renderPermalink(permalink, data) {
let permalinkCompilation = this.engine.permalinkNeedsCompilation(permalink);
// No string compilation:
// ({ compileOptions: { permalink: "raw" }})
// These mean `permalink: false`, which is no file system writing:
// ({ compileOptions: { permalink: false }})
// ({ compileOptions: { permalink: () => false }})
// ({ compileOptions: { permalink: () => (() = > false) }})
if (permalinkCompilation === false) {
return permalink;
}
/* Custom `compile` function for permalinks, usage:
permalink: function(permalinkString, inputPath) {
return async function(data) {
return "THIS IS MY RENDERED PERMALINK";
}
}
*/
if (permalinkCompilation && typeof permalinkCompilation === "function") {
permalink = await this._renderFunction(permalinkCompilation, permalink, this.inputPath);
}
// Raw permalink function (in the app code data cascade)
if (typeof permalink === "function") {
return this._renderFunction(permalink, data);
}
return this._render(permalink, data, {
type: "Permalink",
bypassMarkdown: true,
});
}
async render(str, data, bypassMarkdown) {
return this._render(str, data, {
bypassMarkdown,
type: "",
});
}
_getPaginationLogSuffix(data) {
let suffix = [];
if ("pagination" in data) {
suffix.push(" (");
if (data.pagination.pages) {
suffix.push(
`${data.pagination.pages.length} page${data.pagination.pages.length !== 1 ? "s" : ""}`,
);
} else {
suffix.push("Pagination");
}
suffix.push(")");
}
return suffix.join("");
}
async _render(str, data, options = {}) {
let { bypassMarkdown, type } = options;
try {
if (bypassMarkdown && !this.engine.needsCompilation(str)) {
return str;
}
let fn = await this.compile(str, {
bypassMarkdown,
engineOverride: data[this.config.keys.engineOverride],
type,
});
if (fn === undefined) {
return;
} else if (typeof fn !== "function") {
throw new Error(`The \`compile\` function did not return a function. Received ${fn}`);
}
// Benchmark
let templateBenchmark = this.bench.get("Render");
let inputPathBenchmark = this.bench.get(
`> Render${type ? ` ${type}` : ""} > ${this.inputPath}${this._getPaginationLogSuffix(data)}`,
);
templateBenchmark.before();
if (inputPathBenchmark) {
inputPathBenchmark.before();
}
let rendered = await fn(data);
if (inputPathBenchmark) {
inputPathBenchmark.after();
}
templateBenchmark.after();
debugDev("%o getCompiledTemplate called, rendered content created", this.inputPath);
return rendered;
} catch (e) {
if (EleventyErrorUtil.isPrematureTemplateContentError(e)) {
return Promise.reject(e);
} else {
let tr = await this.getTemplateRender();
let engine = tr.getReadableEnginesList();
debug(`Having trouble rendering ${engine} template ${this.inputPath}: %O`, str);
return Promise.reject(
new TemplateContentRenderError(
`Having trouble rendering ${engine} template ${this.inputPath}`,
e,
),
);
}
}
}
getExtensionEntries() {
return this.engine.extensionEntries;
}
isFileRelevantToThisTemplate(incrementalFile, metadata = {}) {
// always relevant if incremental file not set (build everything)
if (!incrementalFile) {
return true;
}
let hasDependencies = this.engine.hasDependencies(incrementalFile);
let isRelevant = this.engine.isFileRelevantTo(this.inputPath, incrementalFile);
debug(
"Test dependencies to see if %o is relevant to %o: %o",
this.inputPath,
incrementalFile,
isRelevant,
);
let extensionEntries = this.getExtensionEntries().filter((entry) => !!entry.isIncrementalMatch);
if (extensionEntries.length) {
for (let entry of extensionEntries) {
if (
entry.isIncrementalMatch.call(
{
inputPath: this.inputPath,
isFullTemplate: metadata.isFullTemplate,
isFileRelevantToInputPath: isRelevant,
doesFileHaveDependencies: hasDependencies,
},
incrementalFile,
)
) {
return true;
}
}
return false;
} else {
// Not great way of building all templates if this is a layout, include, JS dependency.
// TODO improve this for default template syntaxes
// This is the fallback way of determining if something is incremental (no isIncrementalMatch available)
// This will be true if the inputPath and incrementalFile are the same
if (isRelevant) {
return true;
}
// only return true here if dependencies are not known
if (!hasDependencies && !metadata.isFullTemplate) {
return true;
}
}
return false;
}
}
TemplateContent._inputCache = new Map();
TemplateContent._compileCache = new Map();
eventBus.on("eleventy.resourceModified", (path) => {
// delete from input cache
TemplateContent.deleteFromInputCache(path);
// delete from compile cache
let normalized = TemplatePath.addLeadingDotSlash(path);
let compileCache = TemplateContent._compileCache.get(normalized);
if (compileCache) {
compileCache.clear();
}
});
// Used when the configuration file reset https://github.com/11ty/eleventy/issues/2147
eventBus.on("eleventy.compileCacheReset", (/*path*/) => {
TemplateContent._compileCache = new Map();
});
export default TemplateContent;

57
node_modules/@11ty/eleventy/src/TemplateFileSlug.js generated vendored Normal file
View File

@@ -0,0 +1,57 @@
import path from "node:path";
import { TemplatePath } from "@11ty/eleventy-utils";
class TemplateFileSlug {
constructor(inputPath, extensionMap, eleventyConfig) {
let inputDir = eleventyConfig.directories.input;
if (inputDir) {
inputPath = TemplatePath.stripLeadingSubPath(inputPath, inputDir);
}
this.inputPath = inputPath;
this.cleanInputPath = inputPath.replace(/^.\//, "");
let dirs = this.cleanInputPath.split("/");
this.dirs = dirs;
this.dirs.pop();
this.parsed = path.parse(inputPath);
this.filenameNoExt = extensionMap.removeTemplateExtension(this.parsed.base);
}
// `page.filePathStem` see https://v3.11ty.dev/docs/data-eleventy-supplied/#page-variable
getFullPathWithoutExtension() {
return "/" + TemplatePath.join(...this.dirs, this._getRawSlug());
}
_getRawSlug() {
let slug = this.filenameNoExt;
return this._stripDateFromSlug(slug);
}
/** Removes dates in the format of YYYY-MM-DD from a given slug string candidate. */
_stripDateFromSlug(slug) {
let reg = slug.match(/\d{4}-\d{2}-\d{2}-(.*)/);
if (reg) {
return reg[1];
}
return slug;
}
// `page.fileSlug` see https://v3.11ty.dev/docs/data-eleventy-supplied/#page-variable
getSlug() {
let rawSlug = this._getRawSlug();
if (rawSlug === "index") {
if (!this.dirs.length) {
return "";
}
let lastDir = this.dirs[this.dirs.length - 1];
return this._stripDateFromSlug(lastDir);
}
return rawSlug;
}
}
export default TemplateFileSlug;

35
node_modules/@11ty/eleventy/src/TemplateGlob.js generated vendored Normal file
View File

@@ -0,0 +1,35 @@
import { TemplatePath } from "@11ty/eleventy-utils";
class TemplateGlob {
static normalizePath(...paths) {
if (paths[0].charAt(0) === "!") {
throw new Error(
`TemplateGlob.normalizePath does not accept ! glob paths like: ${paths.join("")}`,
);
}
return TemplatePath.addLeadingDotSlash(TemplatePath.join(...paths));
}
static normalize(path) {
path = path.trim();
if (path.charAt(0) === "!") {
return "!" + TemplateGlob.normalizePath(path.slice(1));
} else {
return TemplateGlob.normalizePath(path);
}
}
static map(files) {
if (typeof files === "string") {
return TemplateGlob.normalize(files);
} else if (Array.isArray(files)) {
return files.map(function (path) {
return TemplateGlob.normalize(path);
});
} else {
return files;
}
}
}
export default TemplateGlob;

242
node_modules/@11ty/eleventy/src/TemplateLayout.js generated vendored Normal file
View File

@@ -0,0 +1,242 @@
import { TemplatePath } from "@11ty/eleventy-utils";
import debugUtil from "debug";
import TemplateLayoutPathResolver from "./TemplateLayoutPathResolver.js";
import TemplateContent from "./TemplateContent.js";
import TemplateData from "./Data/TemplateData.js";
import templateCache from "./TemplateCache.js";
// const debug = debugUtil("Eleventy:TemplateLayout");
const debugDev = debugUtil("Dev:Eleventy:TemplateLayout");
class TemplateLayout extends TemplateContent {
constructor(key, extensionMap, eleventyConfig) {
if (!eleventyConfig || eleventyConfig.constructor.name !== "TemplateConfig") {
throw new Error("Expected `eleventyConfig` in TemplateLayout constructor.");
}
let resolver = new TemplateLayoutPathResolver(key, extensionMap, eleventyConfig);
let resolvedPath = resolver.getFullPath();
super(resolvedPath, eleventyConfig);
if (!extensionMap) {
throw new Error("Expected `extensionMap` in TemplateLayout constructor.");
}
this.extensionMap = extensionMap;
this.key = resolver.getNormalizedLayoutKey();
this.dataKeyLayoutPath = key;
this.inputPath = resolvedPath;
}
getKey() {
return this.key;
}
getFullKey() {
return TemplateLayout.resolveFullKey(this.dataKeyLayoutPath, this.inputDir);
}
getCacheKeys() {
return new Set([this.dataKeyLayoutPath, this.getFullKey(), this.key]);
}
static resolveFullKey(key, inputDir) {
return TemplatePath.join(inputDir, key);
}
static getTemplate(key, eleventyConfig, extensionMap) {
let config = eleventyConfig.getConfig();
if (!config.useTemplateCache) {
return new TemplateLayout(key, extensionMap, eleventyConfig);
}
let inputDir = eleventyConfig.directories.input;
let fullKey = TemplateLayout.resolveFullKey(key, inputDir);
if (!templateCache.has(fullKey)) {
let layout = new TemplateLayout(key, extensionMap, eleventyConfig);
templateCache.add(layout);
debugDev("Added %o to TemplateCache", key);
return layout;
}
return templateCache.get(fullKey);
}
async getTemplateLayoutMapEntry() {
let { data: frontMatterData } = await this.getFrontMatterData();
return {
// Used by `TemplateLayout.getTemplate()`
key: this.dataKeyLayoutPath,
// used by `this.getData()`
frontMatterData,
};
}
async #getTemplateLayoutMap() {
// For both the eleventy.layouts event and cyclical layout chain checking (e.g., a => b => c => a)
let layoutChain = new Set();
layoutChain.add(this.inputPath);
let cfgKey = this.config.keys.layout;
let map = [];
let mapEntry = await this.getTemplateLayoutMapEntry();
map.push(mapEntry);
while (mapEntry.frontMatterData && cfgKey in mapEntry.frontMatterData) {
// Layout of the current layout
let parentLayoutKey = mapEntry.frontMatterData[cfgKey];
let layout = TemplateLayout.getTemplate(
parentLayoutKey,
this.eleventyConfig,
this.extensionMap,
);
// Abort if a circular layout chain is detected. Otherwise, we'll time out and run out of memory.
if (layoutChain.has(layout.inputPath)) {
throw new Error(
`Your layouts have a circular reference, starting at ${map[0].key}! The layout at ${layout.inputPath} was specified twice in this layout chain.`,
);
}
// Keep track of this layout so we can detect duplicates in subsequent iterations
layoutChain.add(layout.inputPath);
// reassign for next loop
mapEntry = await layout.getTemplateLayoutMapEntry();
map.push(mapEntry);
}
this.layoutChain = Array.from(layoutChain);
return map;
}
async getTemplateLayoutMap() {
if (!this.cachedLayoutMap) {
this.cachedLayoutMap = this.#getTemplateLayoutMap();
}
return this.cachedLayoutMap;
}
async getLayoutChain() {
if (!Array.isArray(this.layoutChain)) {
await this.getTemplateLayoutMap();
}
return this.layoutChain;
}
async #getData() {
let map = await this.getTemplateLayoutMap();
let dataToMerge = [];
for (let j = map.length - 1; j >= 0; j--) {
dataToMerge.push(map[j].frontMatterData);
}
// Deep merge of layout front matter
let data = TemplateData.mergeDeep(this.config.dataDeepMerge, {}, ...dataToMerge);
delete data[this.config.keys.layout];
return data;
}
async getData() {
if (!this.dataCache) {
this.dataCache = this.#getData();
}
return this.dataCache;
}
async #getCachedCompiledLayoutFunction() {
let rawInput = await this.getPreRender();
let renderFunction = await this.compile(rawInput);
return renderFunction;
}
// Do only cache this layouts render function and delegate the rest to the other templates.
async getCachedCompiledLayoutFunction() {
if (!this.cachedCompiledLayoutFunction) {
this.cachedCompiledLayoutFunction = this.#getCachedCompiledLayoutFunction();
}
return this.cachedCompiledLayoutFunction;
}
async getCompiledLayoutFunctions() {
let layoutMap = await this.getTemplateLayoutMap();
let fns = [];
try {
fns.push({
render: await this.getCachedCompiledLayoutFunction(),
});
if (layoutMap.length > 1) {
let [, /*currentLayout*/ parentLayout] = layoutMap;
let { key } = parentLayout;
let layoutTemplate = TemplateLayout.getTemplate(
key,
this.eleventyConfig,
this.extensionMap,
);
// The parent already includes the rest of the layout chain
let upstreamFns = await layoutTemplate.getCompiledLayoutFunctions();
for (let j = 0, k = upstreamFns.length; j < k; j++) {
fns.push(upstreamFns[j]);
}
}
return fns;
} catch (e) {
debugDev("Clearing TemplateCache after error.");
templateCache.clear();
throw e;
}
}
async render() {
throw new Error("Internal error: `render` was removed from TemplateLayout.js in Eleventy 3.0.");
}
// Inefficient? We want to compile all the templatelayouts into a single reusable callback?
// Trouble: layouts may need data variables present downstream/upstream
// This is called from Template->renderPageEntry
async renderPageEntry(pageEntry) {
let templateContent = pageEntry.templateContent;
let compiledFunctions = await this.getCompiledLayoutFunctions();
for (let { render } of compiledFunctions) {
let data = {
content: templateContent,
...pageEntry.data,
};
templateContent = await render(data);
}
// Dont set `templateContent` on pageEntry because collection items should not have layout markup
return templateContent;
}
resetCaches(types) {
super.resetCaches(types);
delete this.dataCache;
delete this.layoutChain;
delete this.cachedLayoutMap;
delete this.cachedCompiledLayoutFunction;
}
}
export default TemplateLayout;

View File

@@ -0,0 +1,136 @@
import fs from "node:fs";
import { TemplatePath } from "@11ty/eleventy-utils";
// import debugUtil from "debug";
// const debug = debugUtil("Eleventy:TemplateLayoutPathResolver");
class TemplateLayoutPathResolver {
constructor(path, extensionMap, eleventyConfig) {
if (!eleventyConfig) {
throw new Error("Expected `eleventyConfig` in TemplateLayoutPathResolver constructor");
}
this.eleventyConfig = eleventyConfig;
this.originalPath = path;
this.originalDisplayPath =
TemplatePath.join(this.layoutsDir, this.originalPath) +
` (via \`layout: ${this.originalPath}\`)`; // for error messaging
this.path = path;
this.aliases = {};
this.extensionMap = extensionMap;
if (!extensionMap) {
throw new Error("Expected `extensionMap` in TemplateLayoutPathResolver constructor.");
}
this.init();
}
getVirtualTemplate(layoutPath) {
let inputDirRelativePath =
this.eleventyConfig.directories.getLayoutPathRelativeToInputDirectory(layoutPath);
return this.config.virtualTemplates[inputDirRelativePath];
}
get dirs() {
return this.eleventyConfig.directories;
}
get inputDir() {
return this.dirs.input;
}
get layoutsDir() {
return this.dirs.layouts || this.dirs.includes;
}
/* Backwards compat */
getLayoutsDir() {
return this.layoutsDir;
}
setAliases() {
this.aliases = Object.assign({}, this.config.layoutAliases, this.aliases);
}
// for testing
set config(cfg) {
this._config = cfg;
this.init();
}
get config() {
if (this.eleventyConfig) {
return this.eleventyConfig.getConfig();
} else {
throw new Error("Missing this.eleventyConfig");
}
}
exists(layoutPath) {
if (this.getVirtualTemplate(layoutPath)) {
return true;
}
let fullPath = this.eleventyConfig.directories.getLayoutPath(layoutPath);
if (fs.existsSync(fullPath)) {
return true;
}
return false;
}
init() {
// we might be able to move this into the constructor?
this.aliases = Object.assign({}, this.config.layoutAliases, this.aliases);
if (this.aliases[this.path]) {
this.path = this.aliases[this.path];
}
let useLayoutResolution = this.config.layoutResolution;
if (this.path.split(".").length > 0 && this.exists(this.path)) {
this.filename = this.path;
this.fullPath = this.eleventyConfig.directories.getLayoutPath(this.path);
} else if (useLayoutResolution) {
this.filename = this.findFileName();
this.fullPath = this.eleventyConfig.directories.getLayoutPath(this.filename || "");
}
}
addLayoutAlias(from, to) {
this.aliases[from] = to;
}
getFileName() {
if (!this.filename) {
throw new Error(
`Youre trying to use a layout that does not exist: ${this.originalDisplayPath}`,
);
}
return this.filename;
}
getFullPath() {
if (!this.filename) {
throw new Error(
`Youre trying to use a layout that does not exist: ${this.originalDisplayPath}`,
);
}
return this.fullPath;
}
findFileName() {
for (let filename of this.extensionMap.getFileList(this.path)) {
if (this.exists(filename)) {
return filename;
}
}
}
getNormalizedLayoutKey() {
return TemplatePath.stripLeadingSubPath(this.fullPath, this.layoutsDir);
}
}
export default TemplateLayoutPathResolver;

828
node_modules/@11ty/eleventy/src/TemplateMap.js generated vendored Normal file
View File

@@ -0,0 +1,828 @@
import { DepGraph as DependencyGraph } from "dependency-graph";
import { isPlainObject, TemplatePath } from "@11ty/eleventy-utils";
import debugUtil from "debug";
import TemplateCollection from "./TemplateCollection.js";
import EleventyErrorUtil from "./Errors/EleventyErrorUtil.js";
import UsingCircularTemplateContentReferenceError from "./Errors/UsingCircularTemplateContentReferenceError.js";
import EleventyBaseError from "./Errors/EleventyBaseError.js";
import DuplicatePermalinkOutputError from "./Errors/DuplicatePermalinkOutputError.js";
import TemplateData from "./Data/TemplateData.js";
const debug = debugUtil("Eleventy:TemplateMap");
const debugDev = debugUtil("Dev:Eleventy:TemplateMap");
class TemplateMapConfigError extends EleventyBaseError {}
class EleventyDataSchemaError extends EleventyBaseError {}
// These template URL filenames are allowed to exclude file extensions
const EXTENSIONLESS_URL_ALLOWLIST = [
"/_redirects", // Netlify specific
"/.htaccess", // Apache
"/_headers", // Cloudflare
];
class TemplateMap {
constructor(eleventyConfig) {
if (!eleventyConfig) {
throw new TemplateMapConfigError("Missing config argument.");
}
this.eleventyConfig = eleventyConfig;
this.map = [];
this.collectionsData = null;
this.cached = false;
this.verboseOutput = true;
this.collection = new TemplateCollection();
}
set userConfig(config) {
this._userConfig = config;
}
get userConfig() {
if (!this._userConfig) {
// TODO use this.config for this, need to add collections to mergeable props in userconfig
this._userConfig = this.eleventyConfig.userConfig;
}
return this._userConfig;
}
get config() {
if (!this._config) {
this._config = this.eleventyConfig.getConfig();
}
return this._config;
}
static get tagPrefix() {
return "___TAG___";
}
async add(template) {
if (!template) {
return;
}
let data = await template.getData();
let entries = await template.getTemplateMapEntries(data);
for (let map of entries) {
this.map.push(map);
}
}
getMap() {
return this.map;
}
getTagTarget(str) {
if (str.startsWith("collections.")) {
return str.slice("collections.".length);
}
// Fixes #2851
if (str.startsWith("collections['") || str.startsWith('collections["')) {
return str.slice("collections['".length, -2);
}
}
/* ---
* pagination:
* data: collections
* ---
*/
isPaginationOverAllCollections(entry) {
if (entry.data.pagination?.data) {
return (
entry.data.pagination.data === "collections" ||
entry.data.pagination.data === "collections.all"
);
}
}
getPaginationTagTarget(entry) {
if (entry.data.pagination?.data) {
return this.getTagTarget(entry.data.pagination.data);
}
}
addTagsToGraph(graph, inputPath, tags) {
if (!Array.isArray(tags)) {
return;
}
for (let tag of tags) {
let tagWithPrefix = TemplateMap.tagPrefix + tag;
if (!graph.hasNode(tagWithPrefix)) {
graph.addNode(tagWithPrefix);
}
// Populates to collections.tagName
// Dependency from tag to inputPath
graph.addDependency(tagWithPrefix, inputPath);
}
}
addDeclaredDependenciesToGraph(graph, inputPath, deps) {
if (!Array.isArray(deps)) {
return;
}
for (let tag of deps) {
let tagWithPrefix = TemplateMap.tagPrefix + tag;
if (!graph.hasNode(tagWithPrefix)) {
graph.addNode(tagWithPrefix);
}
// Dependency from inputPath to collection/tag
graph.addDependency(inputPath, tagWithPrefix);
}
}
// Exclude: Pagination templates consuming `collections` or `collections.all`
// Exclude: Pagination templates that consume config API collections
// Include: Pagination templates that dont consume config API collections
// Include: Templates that dont use Pagination
getMappedDependencies() {
let graph = new DependencyGraph();
let tagPrefix = TemplateMap.tagPrefix;
graph.addNode(tagPrefix + "all");
for (let entry of this.map) {
if (this.isPaginationOverAllCollections(entry)) {
continue;
}
// using Pagination (but not targeting a user config collection)
let paginationTagTarget = this.getPaginationTagTarget(entry);
if (paginationTagTarget) {
if (this.isUserConfigCollectionName(paginationTagTarget)) {
// delay this one to the second stage
continue;
} else {
// using pagination but over a tagged collection
graph.addNode(entry.inputPath);
if (!graph.hasNode(tagPrefix + paginationTagTarget)) {
graph.addNode(tagPrefix + paginationTagTarget);
}
graph.addDependency(entry.inputPath, tagPrefix + paginationTagTarget);
}
} else {
// not using pagination
graph.addNode(entry.inputPath);
}
let collections = TemplateData.getIncludedCollectionNames(entry.data);
this.addTagsToGraph(graph, entry.inputPath, collections);
this.addDeclaredDependenciesToGraph(
graph,
entry.inputPath,
entry.data.eleventyImport?.collections,
);
}
return graph;
}
// Exclude: Pagination templates consuming `collections` or `collections.all`
// Include: Pagination templates that consume config API collections
getDelayedMappedDependencies() {
let graph = new DependencyGraph();
let tagPrefix = TemplateMap.tagPrefix;
graph.addNode(tagPrefix + "all");
let userConfigCollections = this.getUserConfigCollectionNames();
// Add tags from named user config collections
for (let tag of userConfigCollections) {
graph.addNode(tagPrefix + tag);
}
for (let entry of this.map) {
if (this.isPaginationOverAllCollections(entry)) {
continue;
}
let paginationTagTarget = this.getPaginationTagTarget(entry);
if (paginationTagTarget && this.isUserConfigCollectionName(paginationTagTarget)) {
if (!graph.hasNode(entry.inputPath)) {
graph.addNode(entry.inputPath);
}
graph.addDependency(entry.inputPath, tagPrefix + paginationTagTarget);
let collections = TemplateData.getIncludedCollectionNames(entry.data);
this.addTagsToGraph(graph, entry.inputPath, collections);
this.addDeclaredDependenciesToGraph(
graph,
entry.inputPath,
entry.data.eleventyImport?.collections,
);
}
}
return graph;
}
// Exclude: Pagination templates consuming `collections.all`
// Include: Pagination templates consuming `collections`
getPaginatedOverCollectionsMappedDependencies() {
let graph = new DependencyGraph();
let tagPrefix = TemplateMap.tagPrefix;
let allNodeAdded = false;
for (let entry of this.map) {
if (this.isPaginationOverAllCollections(entry) && !this.getPaginationTagTarget(entry)) {
if (!allNodeAdded) {
graph.addNode(tagPrefix + "all");
allNodeAdded = true;
}
if (!graph.hasNode(entry.inputPath)) {
graph.addNode(entry.inputPath);
}
let collectionNames = TemplateData.getIncludedCollectionNames(entry.data);
if (collectionNames.includes("all")) {
// collections.all
graph.addDependency(tagPrefix + "all", entry.inputPath);
// Note that `tags` are otherwise ignored here
}
this.addDeclaredDependenciesToGraph(
graph,
entry.inputPath,
entry.data.eleventyImport?.collections,
);
}
}
return graph;
}
// Include: Pagination templates consuming `collections.all`
getPaginatedOverAllCollectionMappedDependencies() {
let graph = new DependencyGraph();
let tagPrefix = TemplateMap.tagPrefix;
let allNodeAdded = false;
for (let entry of this.map) {
if (
this.isPaginationOverAllCollections(entry) &&
this.getPaginationTagTarget(entry) === "all"
) {
if (!allNodeAdded) {
graph.addNode(tagPrefix + "all");
allNodeAdded = true;
}
if (!graph.hasNode(entry.inputPath)) {
graph.addNode(entry.inputPath);
}
let collectionNames = TemplateData.getIncludedCollectionNames(entry.data);
if (collectionNames.includes("all")) {
// Populates into collections.all
// This is circular!
graph.addDependency(tagPrefix + "all", entry.inputPath);
// Note that `tags` are otherwise ignored here
}
this.addDeclaredDependenciesToGraph(
graph,
entry.inputPath,
entry.data.eleventyImport?.collections,
);
}
}
return graph;
}
getTemplateMapDependencyGraph() {
return [
this.getMappedDependencies(),
this.getDelayedMappedDependencies(),
this.getPaginatedOverCollectionsMappedDependencies(),
this.getPaginatedOverAllCollectionMappedDependencies(),
];
}
getFullTemplateMapOrder() {
// convert dependency graphs to ordered arrays
return this.getTemplateMapDependencyGraph().map((entry) => entry.overallOrder());
}
#addEntryToGlobalDependencyGraph(entry) {
let paginationTagTarget = this.getPaginationTagTarget(entry);
if (paginationTagTarget) {
this.config.uses.addDependencyConsumesCollection(entry.inputPath, paginationTagTarget);
}
let collectionNames = TemplateData.getIncludedCollectionNames(entry.data);
for (let name of collectionNames) {
this.config.uses.addDependencyPublishesToCollection(entry.inputPath, name);
}
if (Array.isArray(entry.data.eleventyImport?.collections)) {
for (let tag of entry.data.eleventyImport.collections) {
this.config.uses.addDependencyConsumesCollection(entry.inputPath, tag);
}
}
}
addAllToGlobalDependencyGraph() {
for (let entry of this.map) {
this.#addEntryToGlobalDependencyGraph(entry);
}
}
async setCollectionByTagName(tagName) {
if (this.isUserConfigCollectionName(tagName)) {
// async
this.collectionsData[tagName] = await this.getUserConfigCollection(tagName);
} else {
this.collectionsData[tagName] = this.getTaggedCollection(tagName);
}
let precompiled = this.config.precompiledCollections;
if (precompiled?.[tagName]) {
if (
tagName === "all" ||
!Array.isArray(this.collectionsData[tagName]) ||
this.collectionsData[tagName].length === 0
) {
this.collectionsData[tagName] = precompiled[tagName];
}
}
}
// TODO(slightlyoff): major bottleneck
async initDependencyMap(dependencyMap) {
let tagPrefix = TemplateMap.tagPrefix;
for (let depEntry of dependencyMap) {
if (depEntry.startsWith(tagPrefix)) {
// is a tag (collection) entry
let tagName = depEntry.slice(tagPrefix.length);
await this.setCollectionByTagName(tagName);
} else {
// is a template entry
let map = this.getMapEntryForInputPath(depEntry);
map._pages = await map.template.getTemplates(map.data);
if (map._pages.length === 0) {
// Reminder: a serverless code path was removed here.
} else {
let counter = 0;
for (let page of map._pages) {
// Copy outputPath to map entry
// This is no longer used internally, just for backwards compatibility
// Error added in v3 for https://github.com/11ty/eleventy/issues/3183
if (map.data.pagination) {
if (!Object.prototype.hasOwnProperty.call(map, "outputPath")) {
Object.defineProperty(map, "outputPath", {
get() {
throw new Error(
"Internal error: `.outputPath` on a paginated map entry is not consistent. Use `_pages[…].outputPath` instead.",
);
},
});
}
} else if (!map.outputPath) {
map.outputPath = page.outputPath;
}
if (counter === 0 || map.data.pagination?.addAllPagesToCollections) {
if (map.data.eleventyExcludeFromCollections !== true) {
// is in *some* collections
this.collection.add(page);
}
}
counter++;
}
}
}
}
}
async cache() {
debug("Caching collections objects.");
this.collectionsData = {};
for (let entry of this.map) {
entry.data.collections = this.collectionsData;
}
let [dependencyMap, delayedDependencyMap, firstPaginatedDepMap, secondPaginatedDepMap] =
this.getFullTemplateMapOrder();
await this.initDependencyMap(dependencyMap);
await this.initDependencyMap(delayedDependencyMap);
await this.initDependencyMap(firstPaginatedDepMap);
await this.initDependencyMap(secondPaginatedDepMap);
await this.resolveRemainingComputedData();
let orderedPaths = this.getOrderedInputPaths(
dependencyMap,
delayedDependencyMap,
firstPaginatedDepMap,
secondPaginatedDepMap,
);
let orderedMap = orderedPaths.map((inputPath) => {
return this.getMapEntryForInputPath(inputPath);
});
await this.config.events.emitLazy("eleventy.contentMap", () => {
return {
inputPathToUrl: this.generateInputUrlContentMap(orderedMap),
urlToInputPath: this.generateUrlMap(orderedMap),
};
});
await this.runDataSchemas(orderedMap);
await this.populateContentDataInMap(orderedMap);
this.populateCollectionsWithContent();
this.cached = true;
this.checkForDuplicatePermalinks();
this.checkForMissingFileExtensions();
await this.config.events.emitLazy("eleventy.layouts", () => this.generateLayoutsMap());
}
generateInputUrlContentMap(orderedMap) {
let entries = {};
for (let entry of orderedMap) {
entries[entry.inputPath] = entry._pages.map((entry) => entry.url);
}
return entries;
}
generateUrlMap(orderedMap) {
let entries = {};
for (let entry of orderedMap) {
for (let page of entry._pages) {
// duplicate urls throw an error, so we can return non array here
entries[page.url] = {
inputPath: entry.inputPath,
groupNumber: page.groupNumber,
};
}
}
return entries;
}
// TODO(slightlyoff): hot inner loop?
getMapEntryForInputPath(inputPath) {
for (let map of this.map) {
if (map.inputPath === inputPath) {
return map;
}
}
}
// Filter out any tag nodes
getOrderedInputPaths(...maps) {
let orderedMap = [];
let tagPrefix = TemplateMap.tagPrefix;
for (let map of maps) {
for (let dep of map) {
if (!dep.startsWith(tagPrefix)) {
orderedMap.push(dep);
}
}
}
return orderedMap;
}
async runDataSchemas(orderedMap) {
for (let map of orderedMap) {
if (!map._pages) {
continue;
}
for (let pageEntry of map._pages) {
// Data Schema callback #879
if (typeof pageEntry.data[this.config.keys.dataSchema] === "function") {
try {
await pageEntry.data[this.config.keys.dataSchema](pageEntry.data);
} catch (e) {
throw new EleventyDataSchemaError(
`Error in the data schema for: ${map.inputPath} (via \`eleventyDataSchema\`)`,
e,
);
}
}
}
}
}
async populateContentDataInMap(orderedMap) {
let usedTemplateContentTooEarlyMap = [];
// Note that empty pagination templates will be skipped here as not renderable
let filteredMap = orderedMap.filter((entry) => entry.template.isRenderable());
for (let map of filteredMap) {
if (!map._pages) {
throw new Error(`Internal error: _pages not found for ${map.inputPath}`);
}
// IMPORTANT: this is where template content is rendered
try {
for (let pageEntry of map._pages) {
pageEntry.templateContent =
await pageEntry.template.renderPageEntryWithoutLayout(pageEntry);
}
} catch (e) {
if (EleventyErrorUtil.isPrematureTemplateContentError(e)) {
usedTemplateContentTooEarlyMap.push(map);
// Reset cached render promise
for (let pageEntry of map._pages) {
pageEntry.template.resetCaches({ render: true });
}
} else {
throw e;
}
}
debugDev("Added this.map[...].templateContent, outputPath, et al for one map entry");
}
for (let map of usedTemplateContentTooEarlyMap) {
try {
for (let pageEntry of map._pages) {
pageEntry.templateContent =
await pageEntry.template.renderPageEntryWithoutLayout(pageEntry);
}
} catch (e) {
if (EleventyErrorUtil.isPrematureTemplateContentError(e)) {
throw new UsingCircularTemplateContentReferenceError(
`${map.inputPath} contains a circular reference (using collections) to its own templateContent.`,
);
} else {
// rethrow?
throw e;
}
}
}
}
getTaggedCollection(tag) {
let result;
if (!tag || tag === "all") {
result = this.collection.getAllSorted();
} else {
result = this.collection.getFilteredByTag(tag);
}
debug(`Collection: collections.${tag || "all"} size: ${result.length}`);
return result;
}
/* 3.0.0-alpha.1: setUserConfigCollections method removed (was only used for testing) */
isUserConfigCollectionName(name) {
let collections = this.userConfig.getCollections();
return name && !!collections[name];
}
getUserConfigCollectionNames() {
return Object.keys(this.userConfig.getCollections());
}
async getUserConfigCollection(name) {
let configCollections = this.userConfig.getCollections();
// This works with async now
let result = await configCollections[name](this.collection);
debug(`Collection: collections.${name} size: ${result.length}`);
return result;
}
populateCollectionsWithContent() {
for (let collectionName in this.collectionsData) {
// skip custom collections set in configuration files that have arbitrary types
if (!Array.isArray(this.collectionsData[collectionName])) {
continue;
}
for (let item of this.collectionsData[collectionName]) {
// skip custom collections set in configuration files that have arbitrary types
if (!isPlainObject(item) || !("inputPath" in item)) {
continue;
}
let entry = this.getMapEntryForInputPath(item.inputPath);
// This check skips precompiled collections
if (entry) {
let index = item.pageNumber || 0;
let content = entry._pages[index]._templateContent;
if (content !== undefined) {
item.templateContent = content;
}
}
}
}
}
async resolveRemainingComputedData() {
let promises = [];
for (let entry of this.map) {
for (let pageEntry of entry._pages) {
if (this.config.keys.computed in pageEntry.data) {
promises.push(await pageEntry.template.resolveRemainingComputedData(pageEntry.data));
}
}
}
return Promise.all(promises);
}
async generateLayoutsMap() {
let layouts = {};
for (let entry of this.map) {
for (let page of entry._pages) {
let tmpl = page.template;
let layoutKey = page.data[this.config.keys.layout];
if (layoutKey) {
let layout = tmpl.getLayout(layoutKey);
let layoutChain = await layout.getLayoutChain();
let priors = [];
for (let filepath of layoutChain) {
if (!layouts[filepath]) {
layouts[filepath] = new Set();
}
layouts[filepath].add(page.inputPath);
for (let prior of priors) {
layouts[filepath].add(prior);
}
priors.push(filepath);
}
}
}
}
for (let key in layouts) {
layouts[key] = Array.from(layouts[key]);
}
return layouts;
}
#onEachPage(callback) {
for (let template of this.map) {
for (let page of template._pages) {
callback(page, template);
}
}
}
checkForDuplicatePermalinks() {
let inputs = {};
let permalinks = {};
let warnings = {};
this.#onEachPage((page, template) => {
if (page.outputPath === false || page.url === false) {
// do nothing (also serverless)
} else {
// Make sure output doesnt overwrite input (e.g. --input=. --output=.)
// Related to https://github.com/11ty/eleventy/issues/3327
if (page.outputPath === page.inputPath) {
throw new DuplicatePermalinkOutputError(
`The template at "${page.inputPath}" attempted to overwrite itself.`,
);
} else if (inputs[page.outputPath]) {
throw new DuplicatePermalinkOutputError(
`The template at "${page.inputPath}" attempted to overwrite an existing template at "${page.outputPath}".`,
);
}
inputs[page.inputPath] = true;
if (!permalinks[page.outputPath]) {
permalinks[page.outputPath] = [template.inputPath];
} else {
warnings[page.outputPath] = `Output conflict: multiple input files are writing to \`${
page.outputPath
}\`. Use distinct \`permalink\` values to resolve this conflict.
1. ${template.inputPath}
${permalinks[page.outputPath]
.map(function (inputPath, index) {
return ` ${index + 2}. ${inputPath}\n`;
})
.join("")}
`;
permalinks[page.outputPath].push(template.inputPath);
}
}
});
let warningList = Object.values(warnings);
if (warningList.length) {
// throw one at a time
throw new DuplicatePermalinkOutputError(warningList[0]);
}
}
checkForMissingFileExtensions() {
// disabled in config
if (this.userConfig?.errorReporting?.allowMissingExtensions === true) {
return;
}
this.#onEachPage((page) => {
if (
page.outputPath === false ||
page.url === false ||
page.data.eleventyAllowMissingExtension ||
EXTENSIONLESS_URL_ALLOWLIST.some((url) => page.url.endsWith(url))
) {
// do nothing (also serverless)
} else {
if (TemplatePath.getExtension(page.outputPath) === "") {
let e =
new Error(`The template at '${page.inputPath}' attempted to write to '${page.outputPath}'${page.data.permalink ? ` (via \`permalink\` value: '${page.data.permalink}')` : ""}, which is a target on the file system that does not include a file extension.
You *probably* want to add a file extension to your permalink so that hosts will know how to correctly serve this file to web browsers. Without a file extension, this file may not be reliably deployed without additional hosting configuration (it wont have a mime type) and may also cause local development issues if you later attempt to write to a subdirectory of the same name.
Learn more: https://v3.11ty.dev/docs/permalinks/#trailing-slashes
This is usually but not *always* an error so if youd like to disable this error message, add \`eleventyAllowMissingExtension: true\` somewhere in the data cascade for this template or use \`eleventyConfig.configureErrorReporting({ allowMissingExtensions: true });\` to disable this feature globally.`);
e.skipOriginalStack = true;
throw e;
}
}
});
}
// TODO move these into TemplateMapTest.js
_testGetAllTags() {
let allTags = {};
for (let map of this.map) {
let tags = map.data.tags;
if (Array.isArray(tags)) {
for (let tag of tags) {
allTags[tag] = true;
}
}
}
return Object.keys(allTags);
}
async _testGetUserConfigCollectionsData() {
let collections = {};
let configCollections = this.userConfig.getCollections();
for (let name in configCollections) {
collections[name] = configCollections[name](this.collection);
debug(`Collection: collections.${name} size: ${collections[name].length}`);
}
return collections;
}
async _testGetTaggedCollectionsData() {
let collections = {};
collections.all = this.collection.getAllSorted();
debug(`Collection: collections.all size: ${collections.all.length}`);
let tags = this._testGetAllTags();
for (let tag of tags) {
collections[tag] = this.collection.getFilteredByTag(tag);
debug(`Collection: collections.${tag} size: ${collections[tag].length}`);
}
return collections;
}
async _testGetAllCollectionsData() {
let collections = {};
let taggedCollections = await this._testGetTaggedCollectionsData();
Object.assign(collections, taggedCollections);
let userConfigCollections = await this._testGetUserConfigCollectionsData();
Object.assign(collections, userConfigCollections);
return collections;
}
async _testGetCollectionsData() {
if (!this.cached) {
await this.cache();
}
return this.collectionsData;
}
}
export default TemplateMap;

339
node_modules/@11ty/eleventy/src/TemplatePassthrough.js generated vendored Normal file
View File

@@ -0,0 +1,339 @@
import util from "node:util";
import path from "node:path";
import fs from "graceful-fs";
import isGlob from "is-glob";
import copy from "@11ty/recursive-copy";
import { TemplatePath } from "@11ty/eleventy-utils";
import debugUtil from "debug";
import EleventyBaseError from "./Errors/EleventyBaseError.js";
import checkPassthroughCopyBehavior from "./Util/PassthroughCopyBehaviorCheck.js";
import ProjectDirectories from "./Util/ProjectDirectories.js";
const fsExists = util.promisify(fs.exists);
const debug = debugUtil("Eleventy:TemplatePassthrough");
class TemplatePassthroughError extends EleventyBaseError {}
class TemplatePassthrough {
#isExistsCache = {};
#isDirectoryCache = {};
constructor(path, eleventyConfig) {
if (!eleventyConfig || eleventyConfig.constructor.name !== "TemplateConfig") {
throw new TemplatePassthroughError(
"Missing `eleventyConfig` or was not an instance of `TemplateConfig`.",
);
}
this.eleventyConfig = eleventyConfig;
this.benchmarks = {
aggregate: this.config.benchmarkManager.get("Aggregate"),
};
this.rawPath = path;
// inputPath is relative to the root of your project and not your Eleventy input directory.
// TODO normalize these with forward slashes
this.inputPath = this.normalizeDirectory(path.inputPath);
this.isInputPathGlob = isGlob(this.inputPath);
this.outputPath = path.outputPath;
this.copyOptions = path.copyOptions; // custom options for recursive-copy
this.isDryRun = false;
this.isIncremental = false;
}
get config() {
return this.eleventyConfig.getConfig();
}
get dirs() {
return this.eleventyConfig.directories;
}
// inputDir is used when stripping from output path in `getOutputPath`
get inputDir() {
return this.dirs.input;
}
get outputDir() {
return this.dirs.output;
}
/* { inputPath, outputPath } though outputPath is *not* the full path: just the output directory */
getPath() {
return this.rawPath;
}
async getOutputPath(inputFileFromGlob) {
let { inputDir, outputDir, outputPath, inputPath } = this;
if (outputPath === true) {
// no explicit target, implied target
if (this.isDirectory(inputPath)) {
let inputRelativePath = TemplatePath.stripLeadingSubPath(
inputFileFromGlob || inputPath,
inputDir,
);
return ProjectDirectories.normalizeDirectory(
TemplatePath.join(outputDir, inputRelativePath),
);
}
return TemplatePath.normalize(
TemplatePath.join(
outputDir,
TemplatePath.stripLeadingSubPath(inputFileFromGlob || inputPath, inputDir),
),
);
}
if (inputFileFromGlob) {
return this.getOutputPathForGlobFile(inputFileFromGlob);
}
// Has explicit target
// Bug when copying incremental file overwriting output directory (and making it a file)
// e.g. public/test.css -> _site
// https://github.com/11ty/eleventy/issues/2278
let fullOutputPath = TemplatePath.normalize(TemplatePath.join(outputDir, outputPath));
if (outputPath === "" || this.isDirectory(inputPath)) {
fullOutputPath = ProjectDirectories.normalizeDirectory(fullOutputPath);
}
// TODO room for improvement here:
if (
!this.isInputPathGlob &&
(await fsExists(inputPath)) &&
!this.isDirectory(inputPath) &&
this.isDirectory(fullOutputPath)
) {
let filename = path.parse(inputPath).base;
return TemplatePath.normalize(TemplatePath.join(fullOutputPath, filename));
}
return fullOutputPath;
}
async getOutputPathForGlobFile(inputFileFromGlob) {
return TemplatePath.join(
await this.getOutputPath(),
TemplatePath.getLastPathSegment(inputFileFromGlob),
);
}
setDryRun(isDryRun) {
this.isDryRun = !!isDryRun;
}
setRunMode(runMode) {
this.runMode = runMode;
}
setIsIncremental(isIncremental) {
this.isIncremental = isIncremental;
}
setFileSystemSearch(fileSystemSearch) {
this.fileSystemSearch = fileSystemSearch;
}
async getFiles(glob) {
debug("Searching for: %o", glob);
let b = this.benchmarks.aggregate.get("Searching the file system (passthrough)");
b.before();
let files = TemplatePath.addLeadingDotSlashArray(
await this.fileSystemSearch.search("passthrough", glob),
);
b.after();
return files;
}
isExists(dir) {
if (this.#isExistsCache[dir] === undefined) {
this.#isExistsCache[dir] = fs.existsSync(dir);
}
return this.#isExistsCache[dir];
}
isDirectory(dir) {
if (this.#isDirectoryCache[dir] === undefined) {
if (isGlob(this.inputPath)) {
this.#isDirectoryCache[dir] = false;
} else if (!this.isExists(dir)) {
this.#isDirectoryCache[dir] = false;
} else if (fs.statSync(dir).isDirectory()) {
this.#isDirectoryCache[dir] = true;
} else {
this.#isDirectoryCache[dir] = false;
}
}
return this.#isDirectoryCache[dir];
}
// dir is guaranteed to exist by context
// dir may not be a directory
normalizeDirectory(dir) {
if (dir && typeof dir === "string") {
if (dir.endsWith(path.sep) || dir.endsWith("/")) {
return dir;
}
// When inputPath is a directory, make sure it has a slash for passthrough copy aliasing
// https://github.com/11ty/eleventy/issues/2709
if (this.isDirectory(dir)) {
return `${dir}/`;
}
}
return dir;
}
// maps input paths to output paths
async getFileMap() {
// TODO VirtualFileSystem candidate
if (!isGlob(this.inputPath) && this.isExists(this.inputPath)) {
return [
{
inputPath: this.inputPath,
outputPath: await this.getOutputPath(),
},
];
}
let paths = [];
// If not directory or file, attempt to get globs
let files = await this.getFiles(this.inputPath);
for (let filePathFromGlob of files) {
paths.push({
inputPath: filePathFromGlob,
outputPath: await this.getOutputPath(filePathFromGlob),
});
}
return paths;
}
/* Types:
* 1. via glob, individual files found
* 2. directory, triggers an event for each file
* 3. individual file
*/
async copy(src, dest, copyOptions) {
if (
!TemplatePath.stripLeadingDotSlash(dest).startsWith(
TemplatePath.stripLeadingDotSlash(this.outputDir),
)
) {
return Promise.reject(
new TemplatePassthroughError(
"Destination is not in the site output directory. Check your passthrough paths.",
),
);
}
let fileCopyCount = 0;
let fileSizeCount = 0;
let map = {};
let b = this.benchmarks.aggregate.get("Passthrough Copy File");
// returns a promise
return copy(src, dest, copyOptions)
.on(copy.events.COPY_FILE_START, (copyOp) => {
// Access to individual files at `copyOp.src`
debug("Copying individual file %o", copyOp.src);
map[copyOp.src] = copyOp.dest;
b.before();
})
.on(copy.events.COPY_FILE_COMPLETE, (copyOp) => {
fileCopyCount++;
fileSizeCount += copyOp.stats.size;
b.after();
})
.then(() => {
return {
count: fileCopyCount,
size: fileSizeCount,
map,
};
});
}
async write() {
if (this.isDryRun) {
return Promise.resolve({
count: 0,
map: {},
});
}
debug("Copying %o", this.inputPath);
let fileMap = await this.getFileMap();
// default options for recursive-copy
// see https://www.npmjs.com/package/recursive-copy#arguments
let copyOptionsDefault = {
overwrite: true, // overwrite output. fails when input is directory (mkdir) and output is file
dot: true, // copy dotfiles
junk: false, // copy cache files like Thumbs.db
results: false,
expand: false, // follow symlinks (matches recursive-copy default)
debug: false, // (matches recursive-copy default)
// Note: `filter` callback function only passes in a relative path, which is unreliable
// See https://github.com/timkendrick/recursive-copy/blob/4c9a8b8a4bf573285e9c4a649a30a2b59ccf441c/lib/copy.js#L59
// e.g. `{ filePaths: [ './img/coolkid.jpg' ], relativePaths: [ '' ] }`
};
let copyOptions = Object.assign(copyOptionsDefault, this.copyOptions);
let promises = fileMap.map((entry) => {
// For-free passthrough copy
if (checkPassthroughCopyBehavior(this.config, this.runMode)) {
let aliasMap = {};
aliasMap[entry.inputPath] = entry.outputPath;
return Promise.resolve({
count: 0,
map: aliasMap,
});
}
// Copy the files (only in build mode)
return this.copy(entry.inputPath, entry.outputPath, copyOptions);
});
// IMPORTANT: this returns an array of promises, does not await for promise to finish
return Promise.all(promises).then(
(results) => {
// collate the count and input/output map results from the array.
let count = 0;
let size = 0;
let map = {};
for (let result of results) {
count += result.count;
size += result.size;
Object.assign(map, result.map);
}
return {
count,
size,
map,
};
},
(err) => {
throw new TemplatePassthroughError(`Error copying passthrough files: ${err.message}`, err);
},
);
}
}
export default TemplatePassthrough;

View File

@@ -0,0 +1,311 @@
import isGlob from "is-glob";
import { TemplatePath } from "@11ty/eleventy-utils";
import debugUtil from "debug";
import EleventyExtensionMap from "./EleventyExtensionMap.js";
import EleventyBaseError from "./Errors/EleventyBaseError.js";
import TemplatePassthrough from "./TemplatePassthrough.js";
import checkPassthroughCopyBehavior from "./Util/PassthroughCopyBehaviorCheck.js";
import { isGlobMatch } from "./Util/GlobMatcher.js";
const debug = debugUtil("Eleventy:TemplatePassthroughManager");
const debugDev = debugUtil("Dev:Eleventy:TemplatePassthroughManager");
class TemplatePassthroughManagerConfigError extends EleventyBaseError {}
class TemplatePassthroughManagerCopyError extends EleventyBaseError {}
class TemplatePassthroughManager {
constructor(eleventyConfig) {
if (!eleventyConfig || eleventyConfig.constructor.name !== "TemplateConfig") {
throw new TemplatePassthroughManagerConfigError("Missing or invalid `config` argument.");
}
this.eleventyConfig = eleventyConfig;
this.config = eleventyConfig.getConfig();
this.reset();
}
reset() {
this.count = 0;
this.size = 0;
this.conflictMap = {};
this.incrementalFile = null;
debug("Resetting counts to 0");
}
set extensionMap(extensionMap) {
this._extensionMap = extensionMap;
}
get extensionMap() {
if (!this._extensionMap) {
this._extensionMap = new EleventyExtensionMap(this.eleventyConfig);
this._extensionMap.setFormats([]);
}
return this._extensionMap;
}
get dirs() {
return this.eleventyConfig.directories;
}
get inputDir() {
return this.dirs.input;
}
get outputDir() {
return this.dirs.output;
}
setDryRun(isDryRun) {
this.isDryRun = !!isDryRun;
}
setRunMode(runMode) {
this.runMode = runMode;
}
setIncrementalFile(path) {
if (path) {
this.incrementalFile = path;
}
}
_normalizePaths(path, outputPath, copyOptions = {}) {
return {
inputPath: TemplatePath.addLeadingDotSlash(path),
outputPath: outputPath ? TemplatePath.stripLeadingDotSlash(outputPath) : true,
copyOptions,
};
}
getConfigPaths() {
let paths = [];
let pathsRaw = this.config.passthroughCopies || {};
debug("`addPassthroughCopy` config API paths: %o", pathsRaw);
for (let [inputPath, { outputPath, copyOptions }] of Object.entries(pathsRaw)) {
paths.push(this._normalizePaths(inputPath, outputPath, copyOptions));
}
debug("`addPassthroughCopy` config API normalized paths: %o", paths);
return paths;
}
getConfigPathGlobs() {
return this.getConfigPaths().map((path) => {
return TemplatePath.convertToRecursiveGlobSync(path.inputPath);
});
}
getNonTemplatePaths(paths) {
let matches = [];
for (let path of paths) {
if (!this.extensionMap.hasEngine(path)) {
matches.push(path);
}
}
return matches;
}
getCopyCount() {
return this.count;
}
getCopySize() {
return this.size;
}
setFileSystemSearch(fileSystemSearch) {
this.fileSystemSearch = fileSystemSearch;
}
getTemplatePassthroughForPath(path, isIncremental = false) {
let inst = new TemplatePassthrough(path, this.eleventyConfig);
inst.setFileSystemSearch(this.fileSystemSearch);
inst.setIsIncremental(isIncremental);
inst.setDryRun(this.isDryRun);
inst.setRunMode(this.runMode);
return inst;
}
async copyPassthrough(pass) {
if (!(pass instanceof TemplatePassthrough)) {
throw new TemplatePassthroughManagerCopyError(
"copyPassthrough expects an instance of TemplatePassthrough",
);
}
let { inputPath } = pass.getPath();
// TODO https://github.com/11ty/eleventy/issues/2452
// De-dupe both the input and output paired together to avoid the case
// where an input/output pair has been added via multiple passthrough methods (glob, file suffix, etc)
// Probably start with the `filter` callback in recursive-copy but it only passes relative paths
// See the note in TemplatePassthrough.js->write()
// Also note that `recursive-copy` handles repeated overwrite copy to the same destination just fine.
// e.g. `for(let j=0, k=1000; j<k; j++) { copy("coolkid.jpg", "_site/coolkid.jpg"); }`
// Eventually well want to move all of this to use Nodes fs.cp, which is experimental and only on Node 16+
return pass.write().then(
({ size, count, map }) => {
for (let src in map) {
let dest = map[src];
if (this.conflictMap[dest]) {
if (src !== this.conflictMap[dest]) {
throw new TemplatePassthroughManagerCopyError(
`Multiple passthrough copy files are trying to write to the same output file (${dest}). ${src} and ${this.conflictMap[dest]}`,
);
} else {
// Multiple entries from the same source
debug(
"A passthrough copy entry (%o) caused the same file (%o) to be copied more than once to the output (%o). This is atomically safe but a waste of build resources.",
inputPath,
src,
dest,
);
}
}
debugDev("Adding %o to passthrough copy conflict map, from %o", dest, src);
this.conflictMap[dest] = src;
}
if (pass.isDryRun) {
// We dont count the skipped files as we need to iterate over them
debug(
"Skipped %o (either from --dryrun or --incremental or for-free passthrough copy)",
inputPath,
);
} else {
if (count) {
this.count += count;
this.size += size;
debug("Copied %o (%d files, %d size)", inputPath, count || 0, size || 0);
} else {
debug("Skipped copying %o (emulated passthrough copy)", inputPath);
}
}
return {
count,
map,
};
},
function (e) {
return Promise.reject(
new TemplatePassthroughManagerCopyError(`Having trouble copying '${inputPath}'`, e),
);
},
);
}
isPassthroughCopyFile(paths, changedFile) {
if (!changedFile) {
return false;
}
// passthrough copy by non-matching engine extension (via templateFormats)
for (let path of paths) {
if (path === changedFile && !this.extensionMap.hasEngine(path)) {
return true;
}
}
for (let path of this.getConfigPaths()) {
if (TemplatePath.startsWithSubPath(changedFile, path.inputPath)) {
return path;
}
if (changedFile && isGlob(path.inputPath) && isGlobMatch(changedFile, [path.inputPath])) {
return path;
}
}
return false;
}
getAllNormalizedPaths(paths) {
if (this.incrementalFile) {
let isPassthrough = this.isPassthroughCopyFile(paths, this.incrementalFile);
if (isPassthrough) {
if (isPassthrough.outputPath) {
return [this._normalizePaths(this.incrementalFile, isPassthrough.outputPath)];
}
return [this._normalizePaths(this.incrementalFile)];
}
// Fixes https://github.com/11ty/eleventy/issues/2491
if (!checkPassthroughCopyBehavior(this.config, this.runMode)) {
return [];
}
}
let normalizedPaths = this.getConfigPaths();
if (debug.enabled) {
for (let path of normalizedPaths) {
debug("TemplatePassthrough copying from config: %o", path);
}
}
if (paths?.length) {
let passthroughPaths = this.getNonTemplatePaths(paths);
for (let path of passthroughPaths) {
let normalizedPath = this._normalizePaths(path);
debug(
`TemplatePassthrough copying from non-matching file extension: ${normalizedPath.inputPath}`,
);
normalizedPaths.push(normalizedPath);
}
}
return normalizedPaths;
}
// keys: output
// values: input
getAliasesFromPassthroughResults(result) {
let entries = {};
for (let entry of result) {
for (let src in entry.map) {
let dest = TemplatePath.stripLeadingSubPath(entry.map[src], this.outputDir);
entries["/" + encodeURI(dest)] = src;
}
}
return entries;
}
// Performance note: these can actually take a fair bit of time, but arent a
// bottleneck to eleventy. The copies are performed asynchronously and dont affect eleventy
// write times in a significant way.
async copyAll(templateExtensionPaths) {
debug("TemplatePassthrough copy started.");
let normalizedPaths = this.getAllNormalizedPaths(templateExtensionPaths);
let passthroughs = normalizedPaths.map((path) => {
// if incrementalFile is set but it isnt a passthrough copy, normalizedPaths will be an empty array
let isIncremental = !!this.incrementalFile;
return this.getTemplatePassthroughForPath(path, isIncremental);
});
let promises = passthroughs.map((pass) => this.copyPassthrough(pass));
return Promise.all(promises).then(async (results) => {
let aliases = this.getAliasesFromPassthroughResults(results);
await this.config.events.emit("eleventy.passthrough", {
map: aliases,
});
debug(`TemplatePassthrough copy finished. Current count: ${this.count} (size: ${this.size})`);
return results;
});
}
}
export default TemplatePassthroughManager;

189
node_modules/@11ty/eleventy/src/TemplatePermalink.js generated vendored Normal file
View File

@@ -0,0 +1,189 @@
import path from "node:path";
import normalize from "normalize-path";
import { TemplatePath, isPlainObject } from "@11ty/eleventy-utils";
class TemplatePermalink {
// `link` with template syntax should have already been rendered in Template.js
constructor(link, extraSubdir) {
let isLinkAnObject = isPlainObject(link);
this._isRendered = true;
this._writeToFileSystem = true;
let buildLink;
if (isLinkAnObject) {
if ("build" in link) {
buildLink = link.build;
}
// find the first string key
for (let key in link) {
if (typeof key !== "string") {
continue;
}
break;
}
} else {
buildLink = link;
}
// permalink: false and permalink: build: false
if (typeof buildLink === "boolean") {
if (buildLink === false) {
this._writeToFileSystem = false;
} else {
throw new Error(
`\`permalink: ${
isLinkAnObject ? "build: " : ""
}true\` is not a supported feature in Eleventy. Did you mean \`permalink: ${
isLinkAnObject ? "build: " : ""
}false\`?`,
);
}
} else if (buildLink) {
this.buildLink = buildLink;
}
if (isLinkAnObject) {
// default if permalink is an Object but does not have a `build` prop
if (!("build" in link)) {
this._writeToFileSystem = false;
this._isRendered = false;
}
}
this.extraPaginationSubdir = extraSubdir || "";
}
setUrlTransforms(transforms) {
this._urlTransforms = transforms;
}
get urlTransforms() {
return this._urlTransforms || [];
}
_addDefaultLinkFilename(link) {
return link + (link.slice(-1) === "/" ? "index.html" : "");
}
toOutputPath() {
if (!this.buildLink) {
// empty or false
return false;
}
let cleanLink = this._addDefaultLinkFilename(this.buildLink);
let parsed = path.parse(cleanLink);
return TemplatePath.join(parsed.dir, this.extraPaginationSubdir, parsed.base);
}
// Used in url transforms feature
static getUrlStem(original) {
let subject = original;
if (original.endsWith(".html")) {
subject = original.slice(0, -1 * ".html".length);
}
return TemplatePermalink.normalizePathToUrl(subject);
}
static normalizePathToUrl(original) {
let compare = original || "";
let needleHtml = "/index.html";
let needleBareTrailingSlash = "/index/";
let needleBare = "/index";
if (compare.endsWith(needleHtml)) {
return compare.slice(0, compare.length - needleHtml.length) + "/";
} else if (compare.endsWith(needleBareTrailingSlash)) {
return compare.slice(0, compare.length - needleBareTrailingSlash.length) + "/";
} else if (compare.endsWith(needleBare)) {
return compare.slice(0, compare.length - needleBare.length) + "/";
}
return original;
}
// This method is used to generate the `page.url` variable.
// remove all index.htmls from links
// index.html becomes /
// test/index.html becomes test/
toHref() {
if (!this.buildLink) {
// empty or false
return false;
}
let transformedLink = this.toOutputPath();
let original = (transformedLink.charAt(0) !== "/" ? "/" : "") + transformedLink;
let normalized = TemplatePermalink.normalizePathToUrl(original) || "";
for (let transform of this.urlTransforms) {
original =
transform({
url: normalized,
urlStem: TemplatePermalink.getUrlStem(original),
}) ?? original;
}
return TemplatePermalink.normalizePathToUrl(original);
}
toPath(outputDir) {
if (!this.buildLink) {
return false;
}
let uri = this.toOutputPath();
if (uri === false) {
return false;
}
return normalize(outputDir + "/" + uri);
}
toPathFromRoot() {
if (!this.buildLink) {
return false;
}
let uri = this.toOutputPath();
if (uri === false) {
return false;
}
return normalize(uri);
}
static _hasDuplicateFolder(dir, base) {
let folders = dir.split("/");
if (!folders[folders.length - 1]) {
folders.pop();
}
return folders[folders.length - 1] === base;
}
static generate(dir, filenameNoExt, extraSubdir, fileExtension = "html") {
let path;
if (fileExtension === "html") {
let hasDupeFolder = TemplatePermalink._hasDuplicateFolder(dir, filenameNoExt);
path =
(dir ? dir + "/" : "") +
(filenameNoExt !== "index" && !hasDupeFolder ? filenameNoExt + "/" : "") +
"index" +
".html";
} else {
path = (dir ? dir + "/" : "") + filenameNoExt + "." + fileExtension;
}
return new TemplatePermalink(path, extraSubdir);
}
}
export default TemplatePermalink;

294
node_modules/@11ty/eleventy/src/TemplateRender.js generated vendored Normal file
View File

@@ -0,0 +1,294 @@
import EleventyBaseError from "./Errors/EleventyBaseError.js";
import EleventyExtensionMap from "./EleventyExtensionMap.js";
import TemplateEngineManager from "./Engines/TemplateEngineManager.js";
import CustomEngine from "./Engines/Custom.js";
// import debugUtil from "debug";
// const debug = debugUtil("Eleventy:TemplateRender");
class TemplateRenderConfigError extends EleventyBaseError {}
class TemplateRenderUnknownEngineError extends EleventyBaseError {}
// works with full path names or short engine name
class TemplateRender {
constructor(tmplPath, config) {
if (!tmplPath) {
throw new Error(`TemplateRender requires a tmplPath argument, instead of ${tmplPath}`);
}
if (!config) {
throw new TemplateRenderConfigError("Missing `config` argument.");
}
if (config.constructor.name === "TemplateConfig") {
this.eleventyConfig = config;
this.config = config.getConfig();
} else {
throw new Error("Third argument to TemplateRender must be a TemplateConfig instance.");
}
this.engineNameOrPath = tmplPath;
this.parseMarkdownWith = this.config.markdownTemplateEngine;
this.parseHtmlWith = this.config.htmlTemplateEngine;
}
get dirs() {
return this.eleventyConfig.directories;
}
get inputDir() {
return this.dirs.input;
}
get includesDir() {
return this.dirs.includes;
}
/* Backwards compat */
getIncludesDir() {
return this.includesDir;
}
get config() {
return this._config;
}
set config(config) {
this._config = config;
}
set extensionMap(extensionMap) {
this._extensionMap = extensionMap;
}
get extensionMap() {
if (!this._extensionMap) {
this._extensionMap = new EleventyExtensionMap(this.eleventyConfig);
this._extensionMap.setFormats([]);
}
return this._extensionMap;
}
async getEngineByName(name) {
let engine = await this.extensionMap.engineManager.getEngine(name, this.extensionMap);
engine.eleventyConfig = this.eleventyConfig;
return engine;
}
// Runs once per template
async init(engineNameOrPath) {
let name = engineNameOrPath || this.engineNameOrPath;
this.extensionMap.config = this.eleventyConfig;
let extensionEntry = this.extensionMap.getExtensionEntry(name);
let engineName = extensionEntry?.aliasKey || extensionEntry?.key;
if (TemplateEngineManager.isSimpleAlias(extensionEntry)) {
engineName = extensionEntry?.key;
}
this._engineName = engineName;
if (!extensionEntry || !this._engineName) {
throw new TemplateRenderUnknownEngineError(
`Unknown engine for ${name} (supported extensions: ${this.extensionMap.getReadableFileExtensions()})`,
);
}
this._engine = await this.getEngineByName(this._engineName);
if (this.useMarkdown === undefined) {
this.setUseMarkdown(this._engineName === "md");
}
}
get engineName() {
if (!this._engineName) {
throw new Error("TemplateRender needs a call to the init() method.");
}
return this._engineName;
}
get engine() {
if (!this._engine) {
throw new Error("TemplateRender needs a call to the init() method.");
}
return this._engine;
}
static parseEngineOverrides(engineName) {
if (typeof (engineName || "") !== "string") {
throw new Error("Expected String passed to parseEngineOverrides. Received: " + engineName);
}
let overlappingEngineWarningCount = 0;
let engines = [];
let uniqueLookup = {};
let usingMarkdown = false;
(engineName || "")
.split(",")
.map((name) => {
return name.toLowerCase().trim();
})
.forEach((name) => {
// html is assumed (treated as plaintext by the system)
if (!name || name === "html") {
return;
}
if (name === "md") {
usingMarkdown = true;
return;
}
if (!uniqueLookup[name]) {
engines.push(name);
uniqueLookup[name] = true;
// we already short circuit md and html types above
overlappingEngineWarningCount++;
}
});
if (overlappingEngineWarningCount > 1) {
throw new Error(
`Dont mix multiple templating engines in your front matter overrides (exceptions for HTML and Markdown). You used: ${engineName}`,
);
}
// markdown should always be first
if (usingMarkdown) {
engines.unshift("md");
}
return engines;
}
// used for error logging and console output.
getReadableEnginesList() {
return this.getReadableEnginesListDifferingFromFileExtension() || this.engineName;
}
getReadableEnginesListDifferingFromFileExtension() {
let keyFromFilename = this.extensionMap.getKey(this.engineNameOrPath);
if (this.engine instanceof CustomEngine) {
if (
this.engine.entry &&
this.engine.entry.name &&
keyFromFilename !== this.engine.entry.name
) {
return this.engine.entry.name;
} else {
// We dont have a name for it so we return nothing so we dont misreport (per #2386)
return;
}
}
if (this.engineName === "md" && this.useMarkdown && this.parseMarkdownWith) {
return this.parseMarkdownWith;
}
if (this.engineName === "html" && this.parseHtmlWith) {
return this.parseHtmlWith;
}
// templateEngineOverride in play and template language differs from file extension
if (keyFromFilename !== this.engineName) {
return this.engineName;
}
}
// TODO templateEngineOverride
getPreprocessorEngine() {
if (this.engineName === "md" && this.parseMarkdownWith) {
return this.parseMarkdownWith;
}
if (this.engineName === "html" && this.parseHtmlWith) {
return this.parseHtmlWith;
}
return this.extensionMap.getKey(this.engineNameOrPath);
}
// We pass in templateEngineOverride here because it isnt yet applied to templateRender
getEnginesList(engineOverride) {
if (engineOverride) {
let engines = TemplateRender.parseEngineOverrides(engineOverride).reverse();
return engines.join(",");
}
if (this.engineName === "md" && this.useMarkdown && this.parseMarkdownWith) {
return `${this.parseMarkdownWith},md`;
}
if (this.engineName === "html" && this.parseHtmlWith) {
return this.parseHtmlWith;
}
// templateEngineOverride in play
return this.extensionMap.getKey(this.engineNameOrPath);
}
async setEngineOverride(engineName, bypassMarkdown) {
let engines = TemplateRender.parseEngineOverrides(engineName);
// when overriding, Template Engines with HTML will instead use the Template Engine as primary and output HTML
// So any HTML engine usage here will never use a preprocessor templating engine.
this.setHtmlEngine(false);
if (!engines.length) {
await this.init("html");
return;
}
await this.init(engines[0]);
let usingMarkdown = engines[0] === "md" && !bypassMarkdown;
this.setUseMarkdown(usingMarkdown);
if (usingMarkdown) {
// false means only parse markdown and not with a preprocessor template engine
this.setMarkdownEngine(engines.length > 1 ? engines[1] : false);
}
}
getEngineName() {
return this.engineName;
}
isEngine(engine) {
return this.engineName === engine;
}
setUseMarkdown(useMarkdown) {
this.useMarkdown = !!useMarkdown;
}
// this is only called for templateEngineOverride
setMarkdownEngine(markdownEngine) {
this.parseMarkdownWith = markdownEngine;
}
// this is only called for templateEngineOverride
setHtmlEngine(htmlEngineName) {
this.parseHtmlWith = htmlEngineName;
}
async _testRender(str, data) {
return this.engine._testRender(str, data);
}
async getCompiledTemplate(str) {
// TODO refactor better, move into TemplateEngine logic
if (this.engineName === "md") {
return this.engine.compile(
str,
this.engineNameOrPath,
this.parseMarkdownWith,
!this.useMarkdown,
);
} else if (this.engineName === "html") {
return this.engine.compile(str, this.engineNameOrPath, this.parseHtmlWith);
} else {
return this.engine.compile(str, this.engineNameOrPath);
}
}
}
export default TemplateRender;

518
node_modules/@11ty/eleventy/src/TemplateWriter.js generated vendored Executable file
View File

@@ -0,0 +1,518 @@
import { TemplatePath } from "@11ty/eleventy-utils";
import debugUtil from "debug";
import Template from "./Template.js";
import TemplateMap from "./TemplateMap.js";
import EleventyFiles from "./EleventyFiles.js";
import EleventyExtensionMap from "./EleventyExtensionMap.js";
import EleventyBaseError from "./Errors/EleventyBaseError.js";
import { EleventyErrorHandler } from "./Errors/EleventyErrorHandler.js";
import EleventyErrorUtil from "./Errors/EleventyErrorUtil.js";
import FileSystemSearch from "./FileSystemSearch.js";
import ConsoleLogger from "./Util/ConsoleLogger.js";
const debug = debugUtil("Eleventy:TemplateWriter");
class TemplateWriterMissingConfigArgError extends EleventyBaseError {}
class EleventyPassthroughCopyError extends EleventyBaseError {}
class EleventyTemplateError extends EleventyBaseError {}
class TemplateWriter {
#eleventyFiles;
constructor(
templateFormats, // TODO remove this, see `get eleventyFiles` first
templateData,
eleventyConfig,
) {
if (!eleventyConfig) {
throw new TemplateWriterMissingConfigArgError("Missing config argument.");
}
this.eleventyConfig = eleventyConfig;
this.config = eleventyConfig.getConfig();
this.userConfig = eleventyConfig.userConfig;
this.templateFormats = templateFormats;
this.templateData = templateData;
this.isVerbose = true;
this.isDryRun = false;
this.writeCount = 0;
this.renderCount = 0;
this.skippedCount = 0;
this.isRunInitialBuild = true;
this._templatePathCache = new Map();
}
get dirs() {
return this.eleventyConfig.directories;
}
get inputDir() {
return this.dirs.input;
}
get outputDir() {
return this.dirs.output;
}
get templateFormats() {
return this._templateFormats;
}
set templateFormats(value) {
this._templateFormats = value;
}
/* Getter for error handler */
get errorHandler() {
if (!this._errorHandler) {
this._errorHandler = new EleventyErrorHandler();
this._errorHandler.isVerbose = this.verboseMode;
this._errorHandler.logger = this.logger;
}
return this._errorHandler;
}
/* Getter for Logger */
get logger() {
if (!this._logger) {
this._logger = new ConsoleLogger();
this._logger.isVerbose = this.verboseMode;
}
return this._logger;
}
/* Setter for Logger */
set logger(logger) {
this._logger = logger;
}
/* For testing */
overrideConfig(config) {
this.config = config;
}
restart() {
this.writeCount = 0;
this.renderCount = 0;
this.skippedCount = 0;
}
set extensionMap(extensionMap) {
this._extensionMap = extensionMap;
}
get extensionMap() {
if (!this._extensionMap) {
this._extensionMap = new EleventyExtensionMap(this.eleventyConfig);
this._extensionMap.setFormats(this.templateFormats);
}
return this._extensionMap;
}
setEleventyFiles(eleventyFiles) {
this.#eleventyFiles = eleventyFiles;
}
set eleventyFiles(eleventyFiles) {
this.#eleventyFiles = eleventyFiles;
}
get eleventyFiles() {
// usually Eleventy.js will setEleventyFiles with the EleventyFiles manager
if (!this.#eleventyFiles) {
// if not, we can create one (used only by tests)
this.#eleventyFiles = new EleventyFiles(this.templateFormats, this.eleventyConfig);
this.#eleventyFiles.setFileSystemSearch(new FileSystemSearch());
this.#eleventyFiles.init();
}
return this.#eleventyFiles;
}
async _getAllPaths() {
// this is now cached upstream by FileSystemSearch
return this.eleventyFiles.getFiles();
}
_createTemplate(path, to = "fs") {
let tmpl = this._templatePathCache.get(path);
let wasCached = false;
if (tmpl) {
wasCached = true;
// Update config for https://github.com/11ty/eleventy/issues/3468
tmpl.eleventyConfig = this.eleventyConfig;
// TODO reset other constructor things here like inputDir/outputDir/extensionMap/
tmpl.setTemplateData(this.templateData);
} else {
tmpl = new Template(path, this.templateData, this.extensionMap, this.eleventyConfig);
tmpl.setOutputFormat(to);
tmpl.logger = this.logger;
this._templatePathCache.set(path, tmpl);
/*
* Sample filter: arg str, return pretty HTML string
* function(str) {
* return pretty(str, { ocd: true });
* }
*/
tmpl.setTransforms(this.config.transforms);
for (let linterName in this.config.linters) {
let linter = this.config.linters[linterName];
if (typeof linter === "function") {
tmpl.addLinter(linter);
}
}
}
tmpl.setDryRun(this.isDryRun);
tmpl.setIsVerbose(this.isVerbose);
tmpl.reset();
return {
template: tmpl,
wasCached,
};
}
// incrementalFileShape is `template` or `copy` (for passthrough file copy)
async _addToTemplateMapIncrementalBuild(incrementalFileShape, paths, to = "fs") {
// Render overrides are only used when `--ignore-initial` is in play and an initial build is not run
let ignoreInitialBuild = !this.isRunInitialBuild;
let secondOrderRelevantLookup = {};
let templates = [];
let promises = [];
for (let path of paths) {
let { template: tmpl } = this._createTemplate(path, to);
// Note: removed a fix here to fetch missing templateRender instances
// that was tested as no longer needed (Issue #3170).
templates.push(tmpl);
// This must happen before data is generated for the incremental file only
if (incrementalFileShape === "template" && tmpl.inputPath === this.incrementalFile) {
tmpl.resetCaches();
}
// IMPORTANT: This is where the data is first generated for the template
promises.push(this.templateMap.add(tmpl));
}
// Important to set up template dependency relationships first
await Promise.all(promises);
// Delete incremental file from the dependency graph so we get fresh entries!
// This _must_ happen before any additions, the other ones are in Custom.js and GlobalDependencyMap.js (from the eleventy.layouts Event)
this.config.uses.resetNode(this.incrementalFile);
// write new template relationships to the global dependency graph for next time
this.templateMap.addAllToGlobalDependencyGraph();
// Always disable render for --ignore-initial
if (ignoreInitialBuild) {
for (let tmpl of templates) {
tmpl.setRenderableOverride(false); // disable render
}
return;
}
for (let tmpl of templates) {
if (incrementalFileShape === "template" && tmpl.inputPath === this.incrementalFile) {
tmpl.setRenderableOverride(undefined); // unset, probably render
} else if (
tmpl.isFileRelevantToThisTemplate(this.incrementalFile, {
isFullTemplate: incrementalFileShape === "template",
})
) {
// changed file is used by template
// template uses the changed file
tmpl.setRenderableOverride(undefined); // unset, probably render
secondOrderRelevantLookup[tmpl.inputPath] = true;
} else if (this.config.uses.isFileUsedBy(this.incrementalFile, tmpl.inputPath)) {
// changed file uses this template
tmpl.setRenderableOverride("optional");
} else {
// For incremental, always disable render on irrelevant templates
tmpl.setRenderableOverride(false); // disable render
}
}
let secondOrderRelevantArray = this.config.uses
.getTemplatesRelevantToTemplateList(Object.keys(secondOrderRelevantLookup))
.map((entry) => TemplatePath.addLeadingDotSlash(entry));
let secondOrderTemplates = Object.fromEntries(
Object.entries(secondOrderRelevantArray).map(([index, value]) => [value, true]),
);
for (let tmpl of templates) {
// second order templates must also be rendered if not yet already rendered at least once and available in cache.
if (secondOrderTemplates[tmpl.inputPath]) {
if (tmpl.isRenderableDisabled()) {
tmpl.setRenderableOverride("optional");
}
}
}
// Order of templates does not matter here, theyre reordered later based on dependencies in TemplateMap.js
for (let tmpl of templates) {
if (incrementalFileShape === "template" && tmpl.inputPath === this.incrementalFile) {
// Cache is reset above (to invalidate data cache at the right time)
tmpl.setDryRunViaIncremental(false);
} else if (!tmpl.isRenderableDisabled() && !tmpl.isRenderableOptional()) {
// Related to the template but not the template (reset the render cache, not the read cache)
tmpl.resetCaches({
data: true,
render: true,
});
tmpl.setDryRunViaIncremental(false);
} else {
// During incremental we only reset the data cache for non-matching templates, see https://github.com/11ty/eleventy/issues/2710
// Keep caches for read/render
tmpl.resetCaches({
data: true,
});
tmpl.setDryRunViaIncremental(true);
this.skippedCount++;
}
}
}
_addToTemplateMapFullBuild(paths, to = "fs") {
if (this.incrementalFile) {
return [];
}
let ignoreInitialBuild = !this.isRunInitialBuild;
let promises = [];
for (let path of paths) {
let { template: tmpl, wasCached } = this._createTemplate(path, to);
// Render overrides are only used when `--ignore-initial` is in play and an initial build is not run
if (ignoreInitialBuild) {
tmpl.setRenderableOverride(false); // disable render
} else {
tmpl.setRenderableOverride(undefined); // unset, render
}
if (wasCached) {
tmpl.resetCaches();
}
// IMPORTANT: This is where the data is first generated for the template
promises.push(this.templateMap.add(tmpl));
}
return Promise.all(promises);
}
async _addToTemplateMap(paths, to = "fs") {
let incrementalFileShape = this.eleventyFiles.getFileShape(paths, this.incrementalFile);
// Filter out passthrough copy files
paths = paths.filter((path) => {
if (!this.extensionMap.hasEngine(path)) {
return false;
}
if (incrementalFileShape === "copy") {
this.skippedCount++;
// Filters out templates if the incremental file is a passthrough copy file
return false;
}
return true;
});
// Full Build
if (!this.incrementalFile) {
let ret = await this._addToTemplateMapFullBuild(paths, to);
// write new template relationships to the global dependency graph for next time
this.templateMap.addAllToGlobalDependencyGraph();
return ret;
}
// Top level async to get at the promises returned.
return await this._addToTemplateMapIncrementalBuild(incrementalFileShape, paths, to);
}
async _createTemplateMap(paths, to) {
this.templateMap = new TemplateMap(this.eleventyConfig);
await this._addToTemplateMap(paths, to);
await this.templateMap.cache();
return this.templateMap;
}
async _generateTemplate(mapEntry, to) {
let tmpl = mapEntry.template;
return tmpl.generateMapEntry(mapEntry, to).then((pages) => {
this.renderCount += tmpl.getRenderCount();
this.writeCount += tmpl.getWriteCount();
return pages;
});
}
async writePassthroughCopy(templateExtensionPaths) {
let passthroughManager = this.eleventyFiles.getPassthroughManager();
passthroughManager.setIncrementalFile(this.incrementalFile);
return passthroughManager.copyAll(templateExtensionPaths).catch((e) => {
this.errorHandler.warn(e, "Error with passthrough copy");
return Promise.reject(new EleventyPassthroughCopyError("Having trouble copying", e));
});
}
async generateTemplates(paths, to = "fs") {
let promises = [];
// console.time("generateTemplates:_createTemplateMap");
// TODO optimize await here
await this._createTemplateMap(paths, to);
// console.timeEnd("generateTemplates:_createTemplateMap");
debug("Template map created.");
let usedTemplateContentTooEarlyMap = [];
for (let mapEntry of this.templateMap.getMap()) {
promises.push(
this._generateTemplate(mapEntry, to).catch(function (e) {
// Premature templateContent in layout render, this also happens in
// TemplateMap.populateContentDataInMap for non-layout content
if (EleventyErrorUtil.isPrematureTemplateContentError(e)) {
usedTemplateContentTooEarlyMap.push(mapEntry);
} else {
let outputPaths = `"${mapEntry._pages.map((page) => page.outputPath).join(`", "`)}"`;
return Promise.reject(
new EleventyTemplateError(
`Having trouble writing to ${outputPaths} from "${mapEntry.inputPath}"`,
e,
),
);
}
}),
);
}
for (let mapEntry of usedTemplateContentTooEarlyMap) {
promises.push(
this._generateTemplate(mapEntry, to).catch(function (e) {
return Promise.reject(
new EleventyTemplateError(
`Having trouble writing to (second pass) "${mapEntry.outputPath}" from "${mapEntry.inputPath}"`,
e,
),
);
}),
);
}
return promises;
}
async write() {
let paths = await this._getAllPaths();
let promises = [];
// The ordering here is important to destructuring in Eleventy->_watch
promises.push(this.writePassthroughCopy(paths));
promises.push(...(await this.generateTemplates(paths)));
return Promise.all(promises).then(
([passthroughCopyResults, ...templateResults]) => {
return {
passthroughCopy: passthroughCopyResults,
// New in 3.0: flatten and filter out falsy templates
templates: templateResults.flat().filter(Boolean),
};
},
(e) => {
return Promise.reject(e);
},
);
}
// Passthrough copy not supported in JSON output.
// --incremental not supported in JSON output.
async getJSON(to = "json") {
let paths = await this._getAllPaths();
let promises = await this.generateTemplates(paths, to);
return Promise.all(promises).then(
(templateResults) => {
return {
// New in 3.0: flatten and filter out falsy templates
templates: templateResults.flat().filter(Boolean),
};
},
(e) => {
return Promise.reject(e);
},
);
}
setVerboseOutput(isVerbose) {
this.isVerbose = isVerbose;
this.errorHandler.isVerbose = isVerbose;
}
setDryRun(isDryRun) {
this.isDryRun = !!isDryRun;
this.eleventyFiles.getPassthroughManager().setDryRun(this.isDryRun);
}
setRunInitialBuild(runInitialBuild) {
this.isRunInitialBuild = runInitialBuild;
}
setIncrementalBuild(isIncremental) {
this.isIncremental = isIncremental;
}
setIncrementalFile(incrementalFile) {
this.incrementalFile = incrementalFile;
}
resetIncrementalFile() {
this.incrementalFile = null;
}
getCopyCount() {
return this.eleventyFiles.getPassthroughManager().getCopyCount();
}
getCopySize() {
return this.eleventyFiles.getPassthroughManager().getCopySize();
}
getRenderCount() {
return this.renderCount;
}
getWriteCount() {
return this.writeCount;
}
getSkippedCount() {
return this.skippedCount;
}
get caches() {
return ["_templatePathCache"];
}
}
export default TemplateWriter;

1292
node_modules/@11ty/eleventy/src/UserConfig.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,75 @@
import { EventEmitter } from "node:events";
/**
* This class emits events asynchronously.
* It can be used for time measurements during a build.
*/
class AsyncEventEmitter extends EventEmitter {
#handlerMode = "parallel";
// TypeScript slop
constructor(...args) {
super(...args);
}
/**
* @param {string} type - The event name to emit.
* @param {...*} args - Additional arguments that get passed to listeners.
* @returns {Promise} - Promise resolves once all listeners were invoked
*/
/** @ts-expect-error */
async emit(type, ...args) {
let listeners = this.listeners(type);
if (listeners.length === 0) {
return [];
}
if (this.#handlerMode == "sequential") {
const result = [];
for (const listener of listeners) {
const returnValue = await listener.apply(this, args);
result.push(returnValue);
}
return result;
} else {
return Promise.all(
listeners.map((listener) => {
return listener.apply(this, args);
}),
);
}
}
/**
* @param {string} type - The event name to emit.
* @param {...*} args - Additional lazy-executed function arguments that get passed to listeners.
* @returns {Promise} - Promise resolves once all listeners were invoked
*/
async emitLazy(type, ...args) {
let listeners = this.listeners(type);
if (listeners.length === 0) {
return [];
}
let argsMap = [];
for (let arg of args) {
if (typeof arg === "function") {
let r = arg();
if (r instanceof Promise) {
r = await r;
}
argsMap.push(r);
} else {
argsMap.push(arg);
}
}
return this.emit.call(this, type, ...argsMap);
}
setHandlerMode(mode) {
this.#handlerMode = mode;
}
}
export default AsyncEventEmitter;

55
node_modules/@11ty/eleventy/src/Util/Compatibility.js generated vendored Normal file
View File

@@ -0,0 +1,55 @@
import semver from "semver";
import debugUtil from "debug";
import { getEleventyPackageJson, getWorkingProjectPackageJson } from "./ImportJsonSync.js";
const pkg = getEleventyPackageJson();
const debug = debugUtil("Eleventy:Compatibility");
// Used in user config versionCheck method.
class Compatibility {
static NORMALIZE_PRERELEASE_REGEX = /-canary\b/g;
constructor(compatibleRange) {
this.compatibleRange = Compatibility.getCompatibilityValue(compatibleRange);
}
static normalizeIdentifier(identifier) {
return identifier.replace(Compatibility.NORMALIZE_PRERELEASE_REGEX, "-alpha");
}
static getCompatibilityValue(compatibleRange) {
if (compatibleRange) {
return compatibleRange;
}
try {
// fetch from projects package.json
let projectPackageJson = getWorkingProjectPackageJson();
return projectPackageJson["11ty"]?.compatibility;
} catch (e) {
debug("Could not find a project package.json for compatibility version check: %O", e);
return; // do nothing, no compatibility information to check
}
}
isCompatible() {
return Compatibility.satisfies(pkg.version, this.compatibleRange);
}
static satisfies(version, compatibleRange) {
return semver.satisfies(
Compatibility.normalizeIdentifier(version),
Compatibility.normalizeIdentifier(compatibleRange),
{
includePrerelease: true,
},
);
}
getErrorMessage() {
return `We found Eleventy version '${pkg.version}' which does not meet the required version range: '${this.compatibleRange}'. Use \`npm install @11ty/eleventy\` to upgrade your local project to the latest Eleventy version (or \`npm install @11ty/eleventy -g\` to upgrade the globally installed version).`;
}
}
export default Compatibility;

126
node_modules/@11ty/eleventy/src/Util/ConsoleLogger.js generated vendored Normal file
View File

@@ -0,0 +1,126 @@
import { Readable } from "node:stream";
import chalk from "kleur";
import debugUtil from "debug";
const debug = debugUtil("Eleventy:Logger");
/**
* Logger implementation that logs to STDOUT.
* @typedef {'error'|'log'|'warn'|'info'} LogType
*/
class ConsoleLogger {
/** @type {boolean} */
#isVerbose = true;
/** @type {boolean} */
#isChalkEnabled = true;
/** @type {object|boolean|undefined} */
#logger;
constructor() {
this.outputStream = new Readable({
read() {},
});
}
get isVerbose() {
return this.#isVerbose;
}
set isVerbose(verbose) {
this.#isVerbose = !!verbose;
}
get isChalkEnabled() {
return this.#isChalkEnabled;
}
set isChalkEnabled(enabled) {
this.#isChalkEnabled = !!enabled;
}
overrideLogger(logger) {
this.#logger = logger;
}
get logger() {
return this.#logger || console;
}
/** @param {string} msg */
log(msg) {
this.message(msg);
}
/**
* @typedef LogOptions
* @property {string} message
* @property {string=} prefix
* @property {LogType=} type
* @property {string=} color
* @property {boolean=} force
* @param {LogOptions} options
*/
logWithOptions({ message, type, prefix, color, force }) {
this.message(message, type, color, force, prefix);
}
/** @param {string} msg */
forceLog(msg) {
this.message(msg, undefined, undefined, true);
}
/** @param {string} msg */
info(msg) {
this.message(msg, "warn", "blue");
}
/** @param {string} msg */
warn(msg) {
this.message(msg, "warn", "yellow");
}
/** @param {string} msg */
error(msg) {
this.message(msg, "error", "red");
}
/** @param {string} msg */
toStream(msg) {
this.outputStream.push(msg);
}
closeStream() {
this.outputStream.push(null);
return this.outputStream;
}
/**
* Formats the message to log.
*
* @param {string} message - The raw message to log.
* @param {LogType} [type='log'] - The error level to log.
* @param {string|undefined} [chalkColor=undefined] - Color name or falsy to disable
* @param {boolean} [forceToConsole=false] - Enforce a log on console instead of specified target.
*/
message(
message,
type = "log",
chalkColor = undefined,
forceToConsole = false,
prefix = "[11ty]",
) {
if (!forceToConsole && (!this.isVerbose || process.env.DEBUG)) {
debug(message);
} else if (this.#logger !== false) {
message = `${chalk.gray(prefix)} ${message.split("\n").join(`\n${chalk.gray(prefix)} `)}`;
if (chalkColor && this.isChalkEnabled) {
this.logger[type](chalk[chalkColor](message));
} else {
this.logger[type](message);
}
}
}
}
export default ConsoleLogger;

View File

@@ -0,0 +1,24 @@
import spawn from "cross-spawn";
function getGitFirstAddedTimeStamp(filePath) {
return (
parseInt(
spawn
.sync(
"git",
// Formats https://www.git-scm.com/docs/git-log#_pretty_formats
// %at author date, UNIX timestamp
["log", "--diff-filter=A", "--follow", "-1", "--format=%at", filePath],
)
.stdout.toString("utf-8"),
) * 1000
);
}
// return a Date
export default function (inputPath) {
let timestamp = getGitFirstAddedTimeStamp(inputPath);
if (timestamp) {
return new Date(timestamp);
}
}

View File

@@ -0,0 +1,28 @@
import spawn from "cross-spawn";
/* Thank you to Vuepress!
* https://github.com/vuejs/vuepress/blob/89440ce552675859189ed4ab254ce19c4bba5447/packages/%40vuepress/plugin-last-updated/index.js
* MIT licensed: https://github.com/vuejs/vuepress/blob/89440ce552675859189ed4ab254ce19c4bba5447/LICENSE
*/
function getGitLastUpdatedTimeStamp(filePath) {
return (
parseInt(
spawn
.sync(
"git",
// Formats https://www.git-scm.com/docs/git-log#_pretty_formats
// %at author date, UNIX timestamp
["log", "-1", "--format=%at", filePath],
)
.stdout.toString("utf-8"),
) * 1000
);
}
// return a Date
export default function (inputPath) {
let timestamp = getGitLastUpdatedTimeStamp(inputPath);
if (timestamp) {
return new Date(timestamp);
}
}

9
node_modules/@11ty/eleventy/src/Util/DirContains.js generated vendored Normal file
View File

@@ -0,0 +1,9 @@
import path from "node:path";
// Returns true if subfolder is in parent (accepts absolute or relative paths for both)
export default function (parent, subfolder) {
if (path.resolve(subfolder).startsWith(path.resolve(parent))) {
return true;
}
return false;
}

51
node_modules/@11ty/eleventy/src/Util/EsmResolver.js generated vendored Normal file
View File

@@ -0,0 +1,51 @@
import debugUtil from "debug";
const debug = debugUtil("Eleventy:EsmResolver");
let lastModifiedPaths = new Map();
export async function initialize({ port }) {
// From `eleventy.importCacheReset` event in Require.js
port.on("message", ({ path, newDate }) => {
lastModifiedPaths.set(path, newDate);
});
}
// Fixes issue https://github.com/11ty/eleventy/issues/3270
// Docs: https://nodejs.org/docs/latest/api/module.html#resolvespecifier-context-nextresolve
export async function resolve(specifier, context, nextResolve) {
try {
// Not a relative import and not a file import
// Or from node_modules (perhaps better to check if the specifier is in the project directory instead)
if (
(!specifier.startsWith("../") &&
!specifier.startsWith("./") &&
!specifier.startsWith("file:")) ||
context.parentURL.includes("/node_modules/")
) {
return nextResolve(specifier);
}
let fileUrl = new URL(specifier, context.parentURL);
if (fileUrl.searchParams.has("_cache_bust")) {
// already is cache busted outside resolver (wider compat, url was changed prior to import, probably in Require.js)
return nextResolve(specifier);
}
let absolutePath = fileUrl.pathname;
// Bust the import cache if this is a recently modified file
if (lastModifiedPaths.has(absolutePath)) {
fileUrl.search = ""; // delete existing searchparams
fileUrl.searchParams.set("_cache_bust", lastModifiedPaths.get(absolutePath));
debug("Cache busting %o to %o", specifier, fileUrl.toString());
return nextResolve(fileUrl.toString());
}
} catch (e) {
debug("EsmResolver Error parsing specifier (%o): %o", specifier, e);
}
return nextResolve(specifier);
}
// export async function load(url, context, nextLoad) {
// }

26
node_modules/@11ty/eleventy/src/Util/EventBusUtil.js generated vendored Normal file
View File

@@ -0,0 +1,26 @@
import eventBus from "../EventBus.js";
import debugUtil from "debug";
const debug = debugUtil("Eleventy:EventBus");
class EventBusUtil {
// Used for non-global subscriptions that will blow away the previous listener
static soloOn(name, callback) {
eventBus.off(name, callback);
eventBus.on(name, callback);
}
static resetForConfig() {
this.debug();
debug("Config reset (removing eleventy.templateModified listeners).");
eventBus.removeAllListeners("eleventy.templateModified");
}
static debug() {
for (let name of eventBus.eventNames()) {
debug("Listeners for %o: %o", name, eventBus.listenerCount(name));
}
}
}
export default EventBusUtil;

82
node_modules/@11ty/eleventy/src/Util/ExistsCache.js generated vendored Normal file
View File

@@ -0,0 +1,82 @@
import fs from "graceful-fs";
import PathNormalizer from "./PathNormalizer.js";
// Checks both files and directories
class ExistsCache {
constructor() {
this._cache = new Map();
this.lookupCount = 0;
}
setDirectoryCheck(check) {
this.cacheDirectories = !!check;
}
get size() {
return this._cache.size;
}
parentsDoNotExist(path) {
if (!this.cacheDirectories) {
return false;
}
let allPaths = PathNormalizer.getAllPaths(path).filter((entry) => entry !== path);
for (let parentPath of allPaths) {
if (this._cache.has(parentPath)) {
if (this._cache.get(parentPath) === false) {
return true; // we know this parent doesnt exist
}
}
}
// if youve made it here: we dont know if the parents exist or not
return false;
}
has(path) {
return this._cache.has(path);
}
exists(path) {
path = PathNormalizer.fullNormalization(path);
let exists = this._cache.get(path);
if (this.parentsDoNotExist(path)) {
// we dont need to check if a parent directory does not exist
exists = false;
} else if (!this.has(path)) {
exists = fs.existsSync(path);
this.markExistsWithParentDirectories(path, exists);
this.lookupCount++;
}
return exists;
}
// if a file exists, we can mark the parent directories as existing also
// if a file does not exist, we dont know if the parent directories exist or not (yet)
markExistsWithParentDirectories(path, exists = true) {
path = PathNormalizer.fullNormalization(path);
if (!this.cacheDirectories || !exists) {
this.markExists(path, false, true);
return;
}
let paths = PathNormalizer.getAllPaths(path);
for (let fullpath of paths) {
this.markExists(fullpath, true, true);
}
}
markExists(path, exists = true, alreadyNormalized = false) {
if (!alreadyNormalized) {
path = PathNormalizer.fullNormalization(path);
}
this._cache.set(path, !!exists);
}
}
export default ExistsCache;

19
node_modules/@11ty/eleventy/src/Util/FilePathUtil.js generated vendored Normal file
View File

@@ -0,0 +1,19 @@
class FilePathUtil {
static isMatchingExtension(filepath, fileExtension) {
if (!fileExtension) {
return false;
}
if (!(fileExtension || "").startsWith(".")) {
fileExtension = "." + fileExtension;
}
return filepath.endsWith(fileExtension);
}
static getFileExtension(filepath) {
return (filepath || "").split(".").pop();
}
}
export { FilePathUtil };

View File

@@ -0,0 +1,30 @@
import EleventyBaseError from "../Errors/EleventyBaseError.js";
class JavaScriptInvalidDataFormatError extends EleventyBaseError {}
export default async function (inst, inputPath, key = "data", options = {}) {
let { mixins, isObjectRequired } = Object.assign(
{
mixins: {},
isObjectRequired: true,
},
options,
);
if (inst && key in inst) {
// get extra data from `data` method,
// either as a function or getter or object literal
let result = await (typeof inst[key] === "function"
? Object.keys(mixins).length > 0
? inst[key].call(mixins)
: inst[key]()
: inst[key]);
if (isObjectRequired && typeof result !== "object") {
throw new JavaScriptInvalidDataFormatError(
`Invalid data format returned from ${inputPath}: typeof ${typeof result}`,
);
}
return result;
}
}

21
node_modules/@11ty/eleventy/src/Util/GlobMatcher.js generated vendored Normal file
View File

@@ -0,0 +1,21 @@
import micromatch from "micromatch";
import { TemplatePath } from "@11ty/eleventy-utils";
function isGlobMatch(filepath, globs = [], options = undefined) {
if (!filepath || !Array.isArray(globs) || globs.length === 0) {
return false;
}
let inputPath = TemplatePath.stripLeadingDotSlash(filepath);
let opts = Object.assign(
{
dot: true,
nocase: true, // insensitive
},
options,
);
return micromatch.isMatch(inputPath, globs, opts);
}
export { isGlobMatch };

158
node_modules/@11ty/eleventy/src/Util/HtmlTransformer.js generated vendored Normal file
View File

@@ -0,0 +1,158 @@
import posthtml from "posthtml";
import urls from "@11ty/posthtml-urls";
import { FilePathUtil } from "./FilePathUtil.js";
class HtmlTransformer {
// feature test for Eleventy Bundle Plugin
static SUPPORTS_PLUGINS_ENABLED_CALLBACK = true;
constructor() {
// execution order is important (not order of addition/object key order)
this.callbacks = {};
this.posthtmlProcessOptions = {};
this.plugins = {};
}
get aggregateBench() {
if (!this.userConfig) {
throw new Error("Internal error: Missing `userConfig` in HtmlTransformer.");
}
return this.userConfig.benchmarkManager.get("Aggregate");
}
setUserConfig(config) {
this.userConfig = config;
}
static prioritySort(a, b) {
if (b.priority > a.priority) {
return 1;
}
if (a.priority > b.priority) {
return -1;
}
return 0;
}
// context is important as it is used in html base plugin for page specific URL
static _getPosthtmlInstance(callbacks = [], plugins = [], context = {}) {
let inst = posthtml();
// already sorted by priority when added
for (let { fn: plugin, options } of plugins) {
inst.use(plugin(Object.assign({}, context, options)));
}
// Run the built-ins last
if (callbacks.length > 0) {
inst.use(
urls({
eachURL: (url) => {
// and: attrName, tagName
for (let { fn: callback } of callbacks) {
// already sorted by priority when added
url = callback.call(context, url);
}
return url;
},
}),
);
}
return inst;
}
_add(extensions, addType, value, options = {}) {
options = Object.assign(
{
priority: 0,
},
options,
);
let extensionsArray = (extensions || "").split(",");
for (let ext of extensionsArray) {
let target = this[addType];
if (!target[ext]) {
target[ext] = [];
}
target[ext].push({
fn: value, // callback or plugin
priority: options.priority,
enabled: options.enabled || (() => true),
options: options.pluginOptions,
});
target[ext].sort(HtmlTransformer.prioritySort);
}
}
addPosthtmlPlugin(extensions, plugin, options = {}) {
this._add(extensions, "plugins", plugin, options);
}
addUrlTransform(extensions, callback, options = {}) {
this._add(extensions, "callbacks", callback, options);
}
setPosthtmlProcessOptions(options) {
Object.assign(this.posthtmlProcessOptions, options);
}
isTransformable(extension, context) {
return (
this.getCallbacks(extension, context).length > 0 || this.getPlugins(extension).length > 0
);
}
getCallbacks(extension, context) {
let callbacks = this.callbacks[extension] || [];
return callbacks.filter(({ enabled }) => {
if (!enabled || typeof enabled !== "function") {
return true;
}
return enabled(context);
});
}
getPlugins(extension) {
let plugins = this.plugins[extension] || [];
return plugins.filter(({ enabled }) => {
if (!enabled || typeof enabled !== "function") {
return true;
}
return enabled();
});
}
static async transformStandalone(content, callback, posthtmlProcessOptions = {}) {
let posthtmlInstance = this._getPosthtmlInstance([
{
fn: callback,
enabled: () => true,
},
]);
let result = await posthtmlInstance.process(content, posthtmlProcessOptions);
return result.html;
}
async transformContent(outputPath, content, context) {
let extension = FilePathUtil.getFileExtension(outputPath);
if (!this.isTransformable(extension, context)) {
return content;
}
let bench = this.aggregateBench.get(`Transforming \`${extension}\` with posthtml`);
bench.before();
let callbacks = this.getCallbacks(extension, context);
let plugins = this.getPlugins(extension);
let posthtmlInstance = HtmlTransformer._getPosthtmlInstance(callbacks, plugins, context);
let result = await posthtmlInstance.process(content, this.posthtmlProcessOptions);
bench.after();
return result.html;
}
}
export { HtmlTransformer };

64
node_modules/@11ty/eleventy/src/Util/ImportJsonSync.js generated vendored Normal file
View File

@@ -0,0 +1,64 @@
import fs from "node:fs";
import { createRequire } from "node:module";
import debugUtil from "debug";
import { TemplatePath } from "@11ty/eleventy-utils";
import { normalizeFilePathInEleventyPackage } from "./Require.js";
const debug = debugUtil("Eleventy:ImportJsonSync");
const require = createRequire(import.meta.url);
function findFilePathInParentDirs(dir, filename) {
// `package.json` searches look in parent dirs:
// https://docs.npmjs.com/cli/v7/configuring-npm/folders#more-information
// Fixes issue #3178, limited to working dir paths only
let workingDir = TemplatePath.getWorkingDir();
let allDirs = TemplatePath.getAllDirs(dir).filter((entry) => entry.startsWith(workingDir));
for (let dir of allDirs) {
let newPath = TemplatePath.join(dir, filename);
if (fs.existsSync(newPath)) {
debug("Found %o searching parent directories at: %o", filename, dir);
return newPath;
}
}
}
function importJsonSync(filePath) {
if (!filePath.endsWith(".json")) {
throw new Error(`importJsonSync expects a .json file extension (received: ${filePath})`);
}
try {
// TODO clear require.cache when these files change
return require(filePath);
} catch (e) {
debug("Attempted to import %o, received this error: %o", filePath, e);
// if file does not exist, return nothing
}
}
function getEleventyPackageJson() {
let filePath = normalizeFilePathInEleventyPackage("package.json");
return importJsonSync(filePath);
}
function getModulePackageJson(dir) {
let filePath = findFilePathInParentDirs(TemplatePath.absolutePath(dir), "package.json");
return importJsonSync(filePath);
}
function getWorkingProjectPackageJson() {
let dir = TemplatePath.absolutePath(TemplatePath.getWorkingDir());
let filePath = findFilePathInParentDirs(dir, "package.json");
return importJsonSync(filePath);
}
export {
importJsonSync,
findFilePathInParentDirs,
getEleventyPackageJson,
getModulePackageJson,
getWorkingProjectPackageJson,
};

View File

@@ -0,0 +1,5 @@
const ComparisonAsyncFunction = (async () => {}).constructor;
export default function isAsyncFunction(fn) {
return fn instanceof ComparisonAsyncFunction;
}

View File

@@ -0,0 +1,55 @@
import dependencyTree from "@11ty/dependency-tree";
import { find } from "@11ty/dependency-tree-esm";
import { TemplatePath } from "@11ty/eleventy-utils";
import EleventyBaseError from "../Errors/EleventyBaseError.js";
class JavaScriptDependencies {
static getErrorMessage(file, type) {
return `A problem was encountered looking for JavaScript dependencies in ${type} file: ${file}. This only affects --watch and --serve behavior and does not affect your build.`;
}
static async getDependencies(inputFiles, isProjectUsingEsm) {
let depSet = new Set();
// TODO does this need to work with aliasing? what other JS extensions will have deps?
let commonJsFiles = inputFiles.filter(
(file) => (!isProjectUsingEsm && file.endsWith(".js")) || file.endsWith(".cjs"),
);
for (let file of commonJsFiles) {
try {
let modules = dependencyTree(file, {
nodeModuleNames: "exclude",
allowNotFound: true,
}).map((dependency) => {
return TemplatePath.addLeadingDotSlash(TemplatePath.relativePath(dependency));
});
for (let dep of modules) {
depSet.add(dep);
}
} catch (e) {
throw new EleventyBaseError(this.getErrorMessage(file, "CommonJS"), e);
}
}
let esmFiles = inputFiles.filter(
(file) => (isProjectUsingEsm && file.endsWith(".js")) || file.endsWith(".mjs"),
);
for (let file of esmFiles) {
try {
let modules = await find(file);
for (let dep of modules) {
depSet.add(dep);
}
} catch (e) {
throw new EleventyBaseError(this.getErrorMessage(file, "ESM"), e);
}
}
return Array.from(depSet).sort();
}
}
export default JavaScriptDependencies;

View File

@@ -0,0 +1,26 @@
export default function (callback, options = {}) {
let { bench, name } = options;
let cache = new Map();
return (...args) => {
// Only supports single-arg functions for now.
if (args.filter(Boolean).length > 1) {
bench?.get(`(count) ${name} Not valid for memoize`).incrementCount();
return callback(...args);
}
let [cacheKey] = args;
if (!cache.has(cacheKey)) {
cache.set(cacheKey, callback(...args));
bench?.get(`(count) ${name} memoize miss`).incrementCount();
return cache.get(cacheKey);
}
bench?.get(`(count) ${name} memoize hit`).incrementCount();
return cache.get(cacheKey);
};
}

View File

@@ -0,0 +1,20 @@
import { isPlainObject } from "@11ty/eleventy-utils";
// via https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
function DeepFreeze(obj, topLevelExceptions) {
for (let name of Reflect.ownKeys(obj)) {
if ((topLevelExceptions || []).find((key) => key === name)) {
continue;
}
const value = obj[name];
if (isPlainObject(value)) {
DeepFreeze(value);
}
}
return Object.freeze(obj);
}
export { DeepFreeze };

View File

@@ -0,0 +1,9 @@
export default function objectFilter(obj, callback) {
let newObject = {};
for (let [key, value] of Object.entries(obj || {})) {
if (callback(value, key)) {
newObject[key] = value;
}
}
return newObject;
}

View File

@@ -0,0 +1,111 @@
import types from "node:util/types";
import debugUtil from "debug";
import { isPlainObject } from "@11ty/eleventy-utils";
const debug = debugUtil("Dev:Eleventy:Proxy");
function wrapObject(target, fallback) {
if (Object.isFrozen(target)) {
return target;
}
return new Proxy(target, {
getOwnPropertyDescriptor(target, prop) {
let ret;
if (Reflect.has(target, prop)) {
ret = Reflect.getOwnPropertyDescriptor(target, prop);
} else if (Reflect.has(fallback, prop)) {
ret = Reflect.getOwnPropertyDescriptor(fallback, prop);
}
return ret;
},
has(target, prop) {
if (Reflect.has(target, prop)) {
return true;
}
return Reflect.has(fallback, prop);
},
ownKeys(target) {
let s = new Set();
for (let k of Reflect.ownKeys(target)) {
s.add(k);
}
if (isPlainObject(fallback)) {
for (let k of Reflect.ownKeys(fallback)) {
s.add(k);
}
}
return Array.from(s);
},
get(target, prop) {
debug("handler:get", prop);
let value = Reflect.get(target, prop);
if (Reflect.has(target, prop)) {
// Already proxied
if (types.isProxy(value)) {
return value;
}
if (isPlainObject(value) && Reflect.has(fallback, prop)) {
if (Object.isFrozen(value)) {
return value;
}
let ret = wrapObject(value, Reflect.get(fallback, prop));
debug("handler:get (primary, object)", prop);
return ret;
}
debug("handler:get (primary)", prop);
return value;
}
// Does not exist in primary
if (Reflect.has(fallback, prop)) {
// fallback has prop
let fallbackValue = Reflect.get(fallback, prop);
if (isPlainObject(fallbackValue)) {
if (Object.isFrozen(fallbackValue)) {
return fallbackValue;
}
debug("handler:get (fallback, object)", prop);
// set empty object on primary
let emptyObject = {};
Reflect.set(target, prop, emptyObject);
return wrapObject(emptyObject, fallbackValue);
}
debug("handler:get (fallback)", prop);
return fallbackValue;
}
// primary *and* fallback do _not_ have prop
debug("handler:get (not on primary or fallback)", prop);
return value;
},
set(target, prop, value) {
debug("handler:set", prop);
return Reflect.set(target, prop, value);
},
});
}
function ProxyWrap(target, fallback) {
if (!isPlainObject(target) || !isPlainObject(fallback)) {
throw new Error("ProxyWrap expects objects for both the target and fallback");
}
return wrapObject(target, fallback);
}
export { ProxyWrap };

View File

@@ -0,0 +1 @@
export default {};

View File

@@ -0,0 +1,136 @@
class Sortable {
constructor() {
this.isSortAscending = true;
this.isSortNumeric = false;
this.items = [];
this._dirty = true;
this.sortFunctionStringMap = {
"A-Z": "sortFunctionAscending",
"Z-A": "sortFunctionDescending",
"0-9": "sortFunctionNumericAscending",
"9-0": "sortFunctionNumericDescending",
};
}
get length() {
return this.items.length;
}
add(item) {
this._dirty = true;
this.items.push(item);
}
sort(sortFunction) {
if (!sortFunction) {
sortFunction = this.getSortFunction();
} else if (typeof sortFunction === "string") {
let key = sortFunction;
let name;
if (key in this.sortFunctionStringMap) {
name = this.sortFunctionStringMap[key];
}
if (Sortable[name]) {
sortFunction = Sortable[name];
} else {
throw new Error(
`Invalid String argument for sort(). Received \`${key}\`. Valid values: ${Object.keys(
this.sortFunctionStringMap,
)}`,
);
}
}
return this.items.slice().sort(sortFunction);
}
sortAscending() {
return this.sort(this.getSortFunctionAscending());
}
sortDescending() {
return this.sort(this.getSortFunctionDescending());
}
setSortDescending(isDescending = true) {
this.isSortAscending = !isDescending;
}
setSortAscending(isAscending = true) {
this.isSortAscending = isAscending;
}
setSortNumeric(isNumeric) {
this.isSortNumeric = isNumeric;
}
/* Sort functions */
static sortFunctionNumericAscending(a, b) {
return a - b;
}
static sortFunctionNumericDescending(a, b) {
return b - a;
}
static sortFunctionAscending(a, b) {
if (a > b) {
return 1;
} else if (a < b) {
return -1;
}
return 0;
}
static sortFunctionDescending(a, b) {
return Sortable.sortFunctionAscending(b, a);
}
static sortFunctionAlphabeticAscending(a, b) {
return Sortable.sortFunctionAscending(a, b);
}
static sortFunctionAlphabeticDescending(a, b) {
return Sortable.sortFunctionAscending(b, a);
}
static sortFunctionDate(mapA, mapB) {
return Sortable.sortFunctionNumericAscending(mapA.date.getTime(), mapB.date.getTime());
}
static sortFunctionDateInputPath(mapA, mapB) {
let sortDate = Sortable.sortFunctionNumericAscending(mapA.date.getTime(), mapB.date.getTime());
if (sortDate === 0) {
return Sortable.sortFunctionAlphabeticAscending(mapA.inputPath, mapB.inputPath);
}
return sortDate;
}
/* End sort functions */
getSortFunction() {
if (this.isSortAscending) {
return this.getSortFunctionAscending();
} else {
return this.getSortFunctionDescending();
}
}
getSortFunctionAscending() {
if (this.isSortNumeric) {
return Sortable.sortFunctionNumericAscending;
} else {
return Sortable.sortFunctionAlphabeticAscending;
}
}
getSortFunctionDescending() {
if (this.isSortNumeric) {
return Sortable.sortFunctionNumericDescending;
} else {
return Sortable.sortFunctionAlphabeticDescending;
}
}
}
export default Sortable;

View File

@@ -0,0 +1,3 @@
export default function Unique(arr) {
return Array.from(new Set(arr));
}

View File

@@ -0,0 +1,16 @@
function isUsingEleventyDevServer(config) {
return (
!config.serverOptions.module || config.serverOptions.module === "@11ty/eleventy-dev-server"
);
}
// Config opt-out via serverPassthroughCopyBehavior
// False when other server is used
// False when runMode is "build" or "watch"
export default function (config, runMode) {
return (
config.serverPassthroughCopyBehavior === "passthrough" &&
isUsingEleventyDevServer(config) &&
runMode === "serve"
);
}

60
node_modules/@11ty/eleventy/src/Util/PathNormalizer.js generated vendored Normal file
View File

@@ -0,0 +1,60 @@
import path from "node:path";
import { fileURLToPath } from "node:url";
import { TemplatePath } from "@11ty/eleventy-utils";
class PathNormalizer {
static getParts(inputPath) {
if (!inputPath) {
return [];
}
let separator = "/";
if (inputPath.includes(path.sep)) {
separator = path.sep;
}
return inputPath.split(separator).filter((entry) => entry !== ".");
}
// order is important here: the top-most directory returns first
// array of file and all parent directories
static getAllPaths(inputPath) {
let parts = this.getParts(inputPath);
let allPaths = [];
let fullpath = "";
for (let part of parts) {
fullpath += (fullpath.length > 0 ? "/" : "") + part;
allPaths.push(fullpath);
}
return allPaths;
}
static normalizeSeperator(inputPath) {
if (!inputPath) {
return inputPath;
}
return inputPath.split(path.sep).join("/");
}
static fullNormalization(inputPath) {
if (typeof inputPath !== "string") {
return inputPath;
}
// Fix file:///Users/ or file:///C:/ paths passed in
if (inputPath.startsWith("file://")) {
inputPath = fileURLToPath(inputPath);
}
// Paths should not be absolute (we convert absolute paths to relative)
// Paths should not have a leading dot slash
// Paths should always be `/` independent of OS path separator
return TemplatePath.stripLeadingDotSlash(
this.normalizeSeperator(TemplatePath.relativePath(inputPath)),
);
}
}
export default PathNormalizer;

21
node_modules/@11ty/eleventy/src/Util/PathPrefixer.js generated vendored Normal file
View File

@@ -0,0 +1,21 @@
import path from "node:path";
import PathNormalizer from "./PathNormalizer.js";
class PathPrefixer {
static normalizePathPrefix(pathPrefix) {
if (pathPrefix) {
// add leading / (for browsersync), see #1454
// path.join uses \\ for Windows so we split and rejoin
return PathPrefixer.joinUrlParts("/", pathPrefix);
}
return "/";
}
static joinUrlParts(...parts) {
return PathNormalizer.normalizeSeperator(path.join(...parts));
}
}
export default PathPrefixer;

3
node_modules/@11ty/eleventy/src/Util/Pluralize.js generated vendored Normal file
View File

@@ -0,0 +1,3 @@
export default function (count, singleWord, pluralWord) {
return count === 1 ? singleWord : pluralWord;
}

View File

@@ -0,0 +1,344 @@
import fs from "node:fs";
import path from "node:path";
import { TemplatePath } from "@11ty/eleventy-utils";
import isGlob from "is-glob";
/* Directories internally should always use *nix forward slashes */
class ProjectDirectories {
static defaults = {
input: "./",
data: "./_data/", // Relative to input directory
includes: "./_includes/", // Relative to input directory
layouts: "./_layouts/", // Relative to input directory
output: "./_site/",
};
// no updates allowed, input/output set via CLI
#frozen = false;
#raw = {};
#dirs = {};
inputFile = undefined;
inputGlob = undefined;
// Add leading dot slash
// Use forward slashes
static normalizePath(fileOrDir) {
return TemplatePath.standardizeFilePath(fileOrDir);
}
// Must be a directory
// Always include a trailing slash
static normalizeDirectory(dir) {
return this.addTrailingSlash(this.normalizePath(dir));
}
normalizeDirectoryPathRelativeToInputDirectory(filePath) {
return ProjectDirectories.normalizeDirectory(path.join(this.input, filePath));
}
static addTrailingSlash(path) {
if (path.slice(-1) === "/") {
return path;
}
return path + "/";
}
// If input/output are set via CLI, they take precedence over all other configuration values.
freeze() {
this.#frozen = true;
}
setViaConfigObject(configDirs = {}) {
// input must come last
let inputChanged = false;
if (
configDirs.input &&
ProjectDirectories.normalizeDirectory(configDirs.input) !== this.input
) {
this.#setInputRaw(configDirs.input);
inputChanged = true;
}
// If falsy or an empty string, the current directory is used.
if (configDirs.output !== undefined) {
if (ProjectDirectories.normalizeDirectory(configDirs.output) !== this.output) {
this.setOutput(configDirs.output);
}
}
// Input relative directory, if falsy or an empty string, inputDir is used!
// Always set if input changed, e.g. input is `src` and data is `../_data` (resulting in `./_data`) we still want to set data to this new value
if (configDirs.data !== undefined) {
if (
inputChanged ||
this.normalizeDirectoryPathRelativeToInputDirectory(configDirs.data || "") !== this.data
) {
this.setData(configDirs.data);
}
}
// Input relative directory, if falsy or an empty string, inputDir is used!
if (configDirs.includes !== undefined) {
if (
inputChanged ||
this.normalizeDirectoryPathRelativeToInputDirectory(configDirs.includes || "") !==
this.includes
) {
this.setIncludes(configDirs.includes);
}
}
// Input relative directory, if falsy or an empty string, inputDir is used!
if (configDirs.layouts !== undefined) {
if (
inputChanged ||
this.normalizeDirectoryPathRelativeToInputDirectory(configDirs.layouts || "") !==
this.layouts
) {
this.setLayouts(configDirs.layouts);
}
}
if (inputChanged) {
this.updateInputDependencies();
}
}
updateInputDependencies() {
// raw first, fall back to Eleventy defaults if not yet set
this.setData(this.#raw.data ?? ProjectDirectories.defaults.data);
this.setIncludes(this.#raw.includes ?? ProjectDirectories.defaults.includes);
// Should not include this if not explicitly opted-in
if (this.#raw.layouts !== undefined) {
this.setLayouts(this.#raw.layouts ?? ProjectDirectories.defaults.layouts);
}
}
/* Relative to project root, must exist */
#setInputRaw(dirOrFile, inputDir = undefined) {
// is frozen and was defined previously
if (this.#frozen && this.#raw.input !== undefined) {
return;
}
this.#raw.input = dirOrFile;
if (!dirOrFile) {
// input must exist if inputDir is not set.
return;
}
// Input has to exist (assumed glob if it does not exist)
let inputExists = fs.existsSync(dirOrFile);
let inputExistsAndIsDirectory = inputExists && fs.statSync(dirOrFile).isDirectory();
if (inputExistsAndIsDirectory) {
// is not a file or glob
this.#dirs.input = ProjectDirectories.normalizeDirectory(dirOrFile);
} else {
if (inputExists) {
this.inputFile = ProjectDirectories.normalizePath(dirOrFile);
} else {
if (!isGlob(dirOrFile)) {
throw new Error(
`The "${dirOrFile}" \`input\` parameter (directory or file path) must exist on the file system (unless detected as a glob by the \`is-glob\` package)`,
);
}
this.inputGlob = dirOrFile;
}
// Explicit Eleventy option for inputDir
if (inputDir) {
// Changed in 3.0: must exist
if (!fs.existsSync(inputDir)) {
throw new Error("Directory must exist (via inputDir option to Eleventy constructor).");
}
this.#dirs.input = ProjectDirectories.normalizeDirectory(inputDir);
} else {
// the input directory is implied to be the parent directory of the
// file, unless inputDir is explicitly specified (via Eleventy constructor `options`)
this.#dirs.input = ProjectDirectories.normalizeDirectory(
TemplatePath.getDirFromFilePath(dirOrFile), // works with globs
);
}
}
}
setInput(dirOrFile, inputDir = undefined) {
this.#setInputRaw(dirOrFile, inputDir); // does not update
this.updateInputDependencies();
}
/* Relative to input dir */
setIncludes(dir) {
if (dir !== undefined) {
// falsy or an empty string is valid (falls back to input dir)
this.#raw.includes = dir;
this.#dirs.includes = ProjectDirectories.normalizeDirectory(
TemplatePath.join(this.input, dir || ""),
);
}
}
/* Relative to input dir */
/* Optional */
setLayouts(dir) {
if (dir !== undefined) {
// falsy or an empty string is valid (falls back to input dir)
this.#raw.layouts = dir;
this.#dirs.layouts = ProjectDirectories.normalizeDirectory(
TemplatePath.join(this.input, dir || ""),
);
}
}
/* Relative to input dir */
setData(dir) {
if (dir !== undefined) {
// falsy or an empty string is valid (falls back to input dir)
// TODO must exist if specified
this.#raw.data = dir;
this.#dirs.data = ProjectDirectories.normalizeDirectory(
TemplatePath.join(this.input, dir || ""),
);
}
}
/* Relative to project root */
setOutput(dir) {
// is frozen and was defined previously
if (this.#frozen && this.#raw.output !== undefined) {
return;
}
if (dir !== undefined) {
this.#raw.output = dir;
this.#dirs.output = ProjectDirectories.normalizeDirectory(dir || "");
}
}
get input() {
return this.#dirs.input || ProjectDirectories.defaults.input;
}
get data() {
return this.#dirs.data || ProjectDirectories.defaults.data;
}
get includes() {
return this.#dirs.includes || ProjectDirectories.defaults.includes;
}
get layouts() {
// explicit opt-in, no fallback.
return this.#dirs.layouts;
}
get output() {
return this.#dirs.output || ProjectDirectories.defaults.output;
}
isTemplateFile(filePath) {
let inputPath = this.getInputPath(filePath);
if (this.layouts && inputPath.startsWith(this.layouts)) {
return false;
}
if (inputPath.startsWith(this.includes)) {
return false;
}
return inputPath.startsWith(this.input);
}
// for a hypothetical template file
getInputPath(filePathRelativeToInputDir) {
// TODO change ~/ to project root dir
return TemplatePath.addLeadingDotSlash(
TemplatePath.join(this.input, TemplatePath.standardizeFilePath(filePathRelativeToInputDir)),
);
}
// Inverse of getInputPath
// Removes input dir from path
getInputPathRelativeToInputDirectory(filePathRelativeToInputDir) {
let inputDir = TemplatePath.addLeadingDotSlash(TemplatePath.join(this.input));
// No leading dot slash
return TemplatePath.stripLeadingSubPath(filePathRelativeToInputDir, inputDir);
}
// for a hypothetical Eleventy layout file
getLayoutPath(filePathRelativeToLayoutDir) {
return TemplatePath.addLeadingDotSlash(
TemplatePath.join(
this.layouts || this.includes,
TemplatePath.standardizeFilePath(filePathRelativeToLayoutDir),
),
);
}
// Removes layout dir from path
getLayoutPathRelativeToInputDirectory(filePathRelativeToLayoutDir) {
let layoutPath = this.getLayoutPath(filePathRelativeToLayoutDir);
let inputDir = TemplatePath.addLeadingDotSlash(TemplatePath.join(this.input));
// No leading dot slash
return TemplatePath.stripLeadingSubPath(layoutPath, inputDir);
}
getProjectPath(filePath) {
return TemplatePath.addLeadingDotSlash(
TemplatePath.join(".", TemplatePath.standardizeFilePath(filePath)),
);
}
// Access the data without being able to set the data.
getUserspaceInstance() {
let d = this;
return {
get input() {
return d.input;
},
get inputFile() {
return d.inputFile;
},
get inputGlob() {
return d.inputGlob;
},
get data() {
return d.data;
},
get includes() {
return d.includes;
},
get layouts() {
return d.layouts;
},
get output() {
return d.output;
},
};
}
toString() {
return {
input: this.input,
inputFile: this.inputFile,
inputGlob: this.inputGlob,
data: this.data,
includes: this.includes,
layouts: this.layouts,
output: this.output,
};
}
}
export default ProjectDirectories;

View File

@@ -0,0 +1,134 @@
import debugUtil from "debug";
const debug = debugUtil("Eleventy:Util:ProjectTemplateFormats");
class ProjectTemplateFormats {
#useAll = {};
#raw = {};
#values = {}; // Set objects
static union(...sets) {
let s = new Set();
for (let set of sets) {
if (!set || typeof set[Symbol.iterator] !== "function") {
continue;
}
for (let v of set) {
s.add(v);
}
}
return s;
}
#normalize(formats) {
if (Array.isArray(formats)) {
formats = "" + formats.join(",");
}
if (typeof formats !== "string") {
throw new Error(
`Invalid formats (expect String, Array) passed to ProjectTemplateFormats->normalize: ${formats}`,
);
}
let final = new Set();
for (let format of formats.split(",")) {
format = format.trim();
if (format && format !== "*") {
final.add(format);
}
}
return final;
}
isWildcard() {
return this.#useAll.cli || this.#useAll.config || false;
}
/** @returns {boolean} */
#isUseAll(rawFormats) {
if (rawFormats === "") {
return false;
}
if (typeof rawFormats === "string") {
rawFormats = rawFormats.split(",");
}
if (Array.isArray(rawFormats)) {
return rawFormats.find((entry) => entry === "*") !== undefined;
}
return false;
}
// 3.x Breaking: "" now means no formats. In 2.x and prior it meant "*"
setViaCommandLine(formats) {
if (formats === undefined) {
return;
}
this.#useAll.cli = this.#isUseAll(formats);
this.#raw.cli = formats;
this.#values.cli = this.#normalize(formats);
}
// 3.x Breaking: "" now means no formats—in 2.x and prior it meant "*"
// 3.x Adds support for comma separated string—in 2.x this required an Array
setViaConfig(formats) {
if (formats === undefined) {
return;
}
// "*" is supported
this.#useAll.config = this.#isUseAll(formats);
this.#raw.config = formats;
this.#values.config = this.#normalize(formats);
}
addViaConfig(formats) {
if (!formats) {
return;
}
if (this.#isUseAll(formats)) {
throw new Error(
`\`addTemplateFormats("*")\` is not supported for project template syntaxes.`,
);
}
// "*" not supported here
this.#raw.configAdd = formats;
this.#values.configAdd = this.#normalize(formats);
}
getAllTemplateFormats() {
return Array.from(ProjectTemplateFormats.union(this.#values.config, this.#values.configAdd));
}
getTemplateFormats() {
if (this.#useAll.cli) {
let v = this.getAllTemplateFormats();
debug("Using CLI --formats='*': %o", v);
return v;
}
if (this.#raw.cli !== undefined) {
let v = Array.from(this.#values.cli);
debug("Using CLI --formats: %o", v);
return v;
}
let v = this.getAllTemplateFormats();
debug(
"Using configuration `templateFormats`, `setTemplateFormats()`, `addTemplateFormats()`: %o",
v,
);
return v;
}
}
export default ProjectTemplateFormats;

206
node_modules/@11ty/eleventy/src/Util/Require.js generated vendored Normal file
View File

@@ -0,0 +1,206 @@
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import module from "node:module";
import { MessageChannel } from "node:worker_threads";
import { TemplatePath } from "@11ty/eleventy-utils";
import eventBus from "../EventBus.js";
const { port1, port2 } = new MessageChannel();
// ESM Cache Buster is an enhancement that works in Node 18.19+
// https://nodejs.org/docs/latest/api/module.html#moduleregisterspecifier-parenturl-options
// Fixes https://github.com/11ty/eleventy/issues/3270
// ENV variable for https://github.com/11ty/eleventy/issues/3371
if ("register" in module && !process?.env?.ELEVENTY_SKIP_ESM_RESOLVER) {
module.register("./EsmResolver.js", import.meta.url, {
parentURL: import.meta.url,
data: {
port: port2,
},
transferList: [port2],
});
}
// important to clear the require.cache in CJS projects
const require = module.createRequire(import.meta.url);
const requestPromiseCache = new Map();
// Used for JSON imports, suffering from Node warning that import assertions experimental but also
// throwing an error if you try to import() a JSON file without an import assertion.
/**
*
* @returns {string|undefined}
*/
function loadContents(path, options = {}) {
let rawInput;
/** @type {string} */
let encoding = "utf8"; // JSON is utf8
if (options?.encoding || options?.encoding === null) {
encoding = options.encoding;
}
try {
// @ts-expect-error This is an error in the upstream types
rawInput = fs.readFileSync(path, encoding);
} catch (e) {
// if file does not exist, return nothing
}
// Can return a buffer, string, etc
if (typeof rawInput === "string") {
rawInput = rawInput.trim();
}
return rawInput;
}
let lastModifiedPaths = new Map();
eventBus.on("eleventy.importCacheReset", (fileQueue) => {
for (let filePath of fileQueue) {
let absolutePath = TemplatePath.absolutePath(filePath);
let newDate = Date.now();
lastModifiedPaths.set(absolutePath, newDate);
// post to EsmResolver worker thread
if (port1) {
port1.postMessage({ path: absolutePath, newDate });
}
// ESM Eleventy when using `import()` on a CJS project file still adds to require.cache
if (absolutePath in (require?.cache || {})) {
delete require.cache[absolutePath];
}
}
});
async function dynamicImportAbsolutePath(absolutePath, type, returnRaw = false) {
if (absolutePath.endsWith(".json") || type === "json") {
// https://v8.dev/features/import-assertions#dynamic-import() is still experimental in Node 20
let rawInput = loadContents(absolutePath);
if (!rawInput) {
return;
}
return JSON.parse(rawInput);
}
let urlPath;
try {
let u = new URL(`file:${absolutePath}`);
// Bust the import cache if this is the last modified file
if (lastModifiedPaths.has(absolutePath)) {
u.searchParams.set("_cache_bust", lastModifiedPaths.get(absolutePath));
}
urlPath = u.toString();
} catch (e) {
urlPath = absolutePath;
}
let promise;
if (requestPromiseCache.has(urlPath)) {
promise = requestPromiseCache.get(urlPath);
} else {
promise = import(urlPath);
requestPromiseCache.set(urlPath, promise);
}
if (returnRaw) {
return promise;
}
return promise.then((target) => {
// If the only export is `default`, elevate to top (for ESM and CJS)
if (Object.keys(target).length === 1 && "default" in target) {
return target.default;
}
// When using import() on a CommonJS file that exports an object sometimes it
// returns duplicated values in `default` key, e.g. `{ default: {key: value}, key: value }`
// A few examples:
// module.exports = { key: false };
// returns `{ default: {key: false}, key: false }` as not expected.
// module.exports = { key: true };
// module.exports = { key: null };
// module.exports = { key: undefined };
// module.exports = { key: class {} };
// A few examples where it does not duplicate:
// module.exports = { key: 1 };
// returns `{ default: {key: 1} }` as expected.
// module.exports = { key: "value" };
// module.exports = { key: {} };
// module.exports = { key: [] };
if (type === "cjs" && "default" in target) {
let match = true;
for (let key in target) {
if (key === "default") {
continue;
}
if (target[key] !== target.default[key]) {
match = false;
}
}
if (match) {
return target.default;
}
}
// Otherwise return { default: value, named: value }
// Object.assign here so we can add things to it in JavaScript.js
return Object.assign({}, target);
});
}
function normalizeFilePathInEleventyPackage(file) {
// Back up relative paths from ./src/Util/Require.js
return path.resolve(fileURLToPath(import.meta.url), "../../../", file);
}
async function dynamicImportFromEleventyPackage(file) {
// points to files relative to the top level Eleventy directory
let filePath = normalizeFilePathInEleventyPackage(file);
// Returns promise
return dynamicImportAbsolutePath(filePath, "esm");
}
async function dynamicImport(localPath, type) {
let absolutePath = TemplatePath.absolutePath(localPath);
// Returns promise
return dynamicImportAbsolutePath(absolutePath, type);
}
/* Used to import default Eleventy configuration file, raw means we dont normalize away the `default` export */
async function dynamicImportRawFromEleventyPackage(file) {
// points to files relative to the top level Eleventy directory
let filePath = normalizeFilePathInEleventyPackage(file);
// Returns promise
return dynamicImportAbsolutePath(filePath, "esm", true);
}
/* Used to import project configuration files, raw means we dont normalize away the `default` export */
async function dynamicImportRaw(localPath, type) {
let absolutePath = TemplatePath.absolutePath(localPath);
// Returns promise
return dynamicImportAbsolutePath(absolutePath, type, true);
}
export {
loadContents as EleventyLoadContent,
dynamicImport as EleventyImport,
dynamicImportRaw as EleventyImportRaw,
dynamicImportFromEleventyPackage as EleventyImportFromEleventy,
dynamicImportRawFromEleventyPackage as EleventyImportRawFromEleventy,
normalizeFilePathInEleventyPackage,
};

69
node_modules/@11ty/eleventy/src/Util/ReservedData.js generated vendored Normal file
View File

@@ -0,0 +1,69 @@
class EleventyReservedDataError extends TypeError {}
class ReservedData {
static properties = [
// "pkg", // Object.freezed upstream
// "eleventy", // Object.freezed upstream
// "page" is only frozen for specific subproperties below
"content",
"collections",
];
static pageProperties = [
"date",
"inputPath",
"fileSlug",
"filePathStem",
"outputFileExtension",
"templateSyntax",
"url",
"outputPath",
// not yet `excerpt` or `lang` set via front matter and computed data
];
// Check in the data cascade for reserved data properties.
static getReservedKeys(data) {
if (!data) {
return [];
}
let keys = this.properties.filter((key) => {
return key in data;
});
if ("page" in data) {
if (typeof data.page === "object") {
for (let key of this.pageProperties) {
if (key in data.page) {
keys.push(`page.${key}`);
}
}
} else {
// fail `page` when set to non-object values.
keys.push("page");
}
}
return keys;
}
static check(data) {
let reserved = ReservedData.getReservedKeys(data);
if (reserved.length === 0) {
return;
}
let error = new EleventyReservedDataError(
`Cannot override reserved Eleventy properties: ${reserved.join(", ")}`,
);
error.reservedNames = reserved;
throw error;
}
static isReservedDataError(e) {
return e instanceof EleventyReservedDataError;
}
}
export default ReservedData;

11
node_modules/@11ty/eleventy/src/Util/SetUnion.js generated vendored Normal file
View File

@@ -0,0 +1,11 @@
function setUnion(...sets) {
let root = new Set();
for (let set of sets) {
for (let entry of set) {
root.add(entry);
}
}
return root;
}
export { setUnion };

70
node_modules/@11ty/eleventy/src/Util/TransformsUtil.js generated vendored Normal file
View File

@@ -0,0 +1,70 @@
import EleventyBaseError from "../Errors/EleventyBaseError.js";
import { isPlainObject } from "@11ty/eleventy-utils";
import debugUtil from "debug";
const debug = debugUtil("Eleventy:Transforms");
class EleventyTransformError extends EleventyBaseError {}
class TransformsUtil {
static changeTransformsToArray(transformsObj) {
let transforms = [];
for (let name in transformsObj) {
transforms.push({
name: name,
callback: transformsObj[name],
});
}
return transforms;
}
static async runAll(content, pageData, transforms = {}, options = {}) {
let { baseHrefOverride, logger } = options;
let { inputPath, outputPath, url } = pageData;
if (!isPlainObject(transforms)) {
throw new Error("Object of transforms expected.");
}
let transformsArray = this.changeTransformsToArray(transforms);
for (let { callback, name } of transformsArray) {
debug("Running %o transform on %o: %o", name, inputPath, outputPath);
try {
let hadContentBefore = !!content;
content = await callback.call(
{
inputPath,
outputPath,
url,
page: pageData,
baseHref: baseHrefOverride,
},
content,
outputPath,
);
if (hadContentBefore && !content) {
if (!logger || !logger.warn) {
throw new Error("Internal error: missing `logger` instance.");
}
logger.warn(
`Warning: Transform \`${name}\` returned empty when writing ${outputPath} from ${inputPath}.`,
);
}
} catch (e) {
throw new EleventyTransformError(
`Transform \`${name}\` encountered an error when transforming ${inputPath}.`,
e,
);
}
}
return content;
}
}
export default TransformsUtil;

9
node_modules/@11ty/eleventy/src/Util/ValidUrl.js generated vendored Normal file
View File

@@ -0,0 +1,9 @@
export default function isValidUrl(url) {
try {
new URL(url);
return true;
} catch (e) {
// invalid url OR local path
return false;
}
}

168
node_modules/@11ty/eleventy/src/defaultConfig.js generated vendored Normal file
View File

@@ -0,0 +1,168 @@
import bundlePlugin from "@11ty/eleventy-plugin-bundle";
import urlFilter from "./Filters/Url.js";
import slugFilter from "./Filters/Slug.js";
import slugifyFilter from "./Filters/Slugify.js";
import getLocaleCollectionItem from "./Filters/GetLocaleCollectionItem.js";
import getCollectionItemIndex from "./Filters/GetCollectionItemIndex.js";
import { FilterPlugin as InputPathToUrlFilterPlugin } from "./Plugins/InputPathToUrl.js";
import { HtmlTransformer } from "./Util/HtmlTransformer.js";
import TransformsUtil from "./Util/TransformsUtil.js";
import MemoizeUtil from "./Util/MemoizeFunction.js";
/**
* @module 11ty/eleventy/defaultConfig
*/
/**
* @callback addFilter - Register a global filter.
* @param {string} name - Register a template filter by this name.
* @param {function} callback - The filter logic.
*/
/**
* @typedef {object} config
* @property {addFilter} addFilter - Register a new global filter.
*/
/**
* @typedef {object} defaultConfig
* @property {Array<string>} templateFormats - An array of accepted template formats.
* @property {string} [pathPrefix='/'] - The directory under which all output files should be written to.
* @property {string} [markdownTemplateEngine='liquid'] - Template engine to process markdown files with.
* @property {string} [htmlTemplateEngine='liquid'] - Template engine to process html files with.
* @property {boolean} [dataTemplateEngine=false] - Changed in v1.0
* @property {string} [jsDataFileSuffix='.11tydata'] - File suffix for jsData files.
* @property {object} keys
* @property {string} [keys.package='pkg'] - Global data property for package.json data
* @property {string} [keys.layout='layout']
* @property {string} [keys.permalink='permalink']
* @property {string} [keys.permalinkRoot='permalinkBypassOutputDir']
* @property {string} [keys.engineOverride='templateEngineOverride']
* @property {string} [keys.computed='eleventyComputed']
* @property {object} dir
* @property {string} [dir.input='.']
* @property {string} [dir.includes='_includes']
* @property {string} [dir.data='_data']
* @property {string} [dir.output='_site']
* @deprecated handlebarsHelpers
* @deprecated nunjucksFilters
*/
/**
* Default configuration object factory.
*
* @param {config} config - Eleventy configuration object.
* @returns {defaultConfig}
*/
export default function (config) {
let templateConfig = this;
// Used for the HTML <base>, InputPathToUrl, Image transform plugins
let ut = new HtmlTransformer();
ut.setUserConfig(config);
// This needs to be assigned before bundlePlugin is added below.
config.htmlTransformer = ut;
config.addPlugin(bundlePlugin, {
bundles: false, // no default bundles included—must be opt-in.
immediate: true,
});
// Filter: Maps an input path to output URL
config.addPlugin(InputPathToUrlFilterPlugin, {
immediate: true,
});
let memoizeBench = config.benchmarkManager.get("Configuration");
config.addFilter("slug", MemoizeUtil(slugFilter, { name: "slug", bench: memoizeBench }));
config.addFilter("slugify", MemoizeUtil(slugifyFilter, { name: "slugify", bench: memoizeBench }));
// Deprecated, use HtmlBasePlugin instead.
// Adds a pathPrefix manually to a URL string
config.addFilter("url", function addPathPrefixFilter(url, pathPrefixOverride) {
let pathPrefix;
if (pathPrefixOverride && typeof pathPrefixOverride === "string") {
pathPrefix = pathPrefixOverride;
} else {
pathPrefix = templateConfig.getPathPrefix();
}
return urlFilter.call(this, url, pathPrefix);
});
config.addFilter("log", (input, ...messages) => {
console.log(input, ...messages);
return input;
});
config.addFilter("getCollectionItemIndex", function (collection, pageOverride) {
return getCollectionItemIndex.call(this, collection, pageOverride);
});
config.addFilter("getCollectionItem", function (collection, pageOverride, langCode) {
return getLocaleCollectionItem.call(this, config, collection, pageOverride, langCode, 0);
});
config.addFilter("getPreviousCollectionItem", function (collection, pageOverride, langCode) {
return getLocaleCollectionItem.call(this, config, collection, pageOverride, langCode, -1);
});
config.addFilter("getNextCollectionItem", function (collection, pageOverride, langCode) {
return getLocaleCollectionItem.call(this, config, collection, pageOverride, langCode, 1);
});
// Process arbitrary content with transforms
config.addFilter(
"renderTransforms",
async function transformsFilter(content, pageEntryOverride, baseHrefOverride) {
return TransformsUtil.runAll(content, pageEntryOverride || this.page, config.transforms, {
baseHrefOverride,
logger: config.logger,
});
},
);
// Run the `htmlTransformer` transform
config.addTransform("@11ty/eleventy/html-transformer", async function (content) {
return ut.transformContent(this.outputPath, content, this);
});
return {
templateFormats: ["liquid", "md", "njk", "html", "11ty.js"],
// if your site deploys to a subdirectory, change this
pathPrefix: "/",
markdownTemplateEngine: "liquid",
htmlTemplateEngine: "liquid",
// Renamed from `jsDataFileSuffix` in 2.0 (and swapped to an Array)
// If you remove "" we wont look for dir/dir.json or file.json
dataFileSuffixes: [".11tydata", ""],
// "index" will look for `directory/index.*` directory data files instead of `directory/directory.*`
dataFileDirBaseNameOverride: false,
keys: {
// TODO breaking: use `false` by default
package: "pkg", // supports `false`
layout: "layout",
permalink: "permalink",
permalinkRoot: "permalinkBypassOutputDir",
engineOverride: "templateEngineOverride",
computed: "eleventyComputed",
dataSchema: "eleventyDataSchema",
},
// Deprecated, define using `export const directories = {}` instead.
// Reference values using `eleventyConfig.directories` instead.
dir: {
// These values here arent used internally either (except by a few tests), instead were using `ProjectDirectories.defaults`.
// These are kept in place for backwards compat with `eleventyConfig.dir` references in project config code and plugins.
input: ".",
includes: "_includes",
data: "_data",
output: "_site",
},
// deprecated, use config.addNunjucksFilter
nunjucksFilters: {},
};
}