summaryrefslogtreecommitdiff
path: root/node_modules/@11ty/eleventy/src/Engines
diff options
context:
space:
mode:
authorShipwreckt <me@shipwreckt.co.uk>2025-10-31 20:02:14 +0000
committerShipwreckt <me@shipwreckt.co.uk>2025-10-31 20:02:14 +0000
commit7a52ddeba2a68388b544f529d2d92104420f77b0 (patch)
tree15ddd47457a2cb4a96060747437d36474e4f6b4e /node_modules/@11ty/eleventy/src/Engines
parent53d6ae2b5568437afa5e4995580a3fb679b7b91b (diff)
Changed from static to 11ty!
Diffstat (limited to 'node_modules/@11ty/eleventy/src/Engines')
-rw-r--r--node_modules/@11ty/eleventy/src/Engines/Custom.js339
-rw-r--r--node_modules/@11ty/eleventy/src/Engines/FrontMatter/JavaScript.js34
-rw-r--r--node_modules/@11ty/eleventy/src/Engines/Html.js33
-rw-r--r--node_modules/@11ty/eleventy/src/Engines/JavaScript.js240
-rw-r--r--node_modules/@11ty/eleventy/src/Engines/Liquid.js331
-rw-r--r--node_modules/@11ty/eleventy/src/Engines/Markdown.js100
-rwxr-xr-xnode_modules/@11ty/eleventy/src/Engines/Nunjucks.js482
-rw-r--r--node_modules/@11ty/eleventy/src/Engines/TemplateEngine.js206
-rw-r--r--node_modules/@11ty/eleventy/src/Engines/TemplateEngineManager.js193
-rw-r--r--node_modules/@11ty/eleventy/src/Engines/Util/ContextAugmenter.js67
10 files changed, 2025 insertions, 0 deletions
diff --git a/node_modules/@11ty/eleventy/src/Engines/Custom.js b/node_modules/@11ty/eleventy/src/Engines/Custom.js
new file mode 100644
index 0000000..17a0da1
--- /dev/null
+++ b/node_modules/@11ty/eleventy/src/Engines/Custom.js
@@ -0,0 +1,339 @@
+import TemplateEngine from "./TemplateEngine.js";
+import getJavaScriptData from "../Util/GetJavaScriptData.js";
+
+export default 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.setDefaultEngine(undefined);
+ }
+
+ 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;
+ }
+
+ get cacheable() {
+ // Enable cacheability for this template
+ if (this.entry?.compileOptions?.cache !== undefined) {
+ return this.entry.compileOptions.cache;
+ } else if (this.needsToReadFileContents()) {
+ return true;
+ } else if (this._defaultEngine?.cacheable !== undefined) {
+ return this._defaultEngine.cacheable;
+ }
+
+ return super.cacheable;
+ }
+
+ 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 compiledFn = 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 (compiledFn) {
+ // Bind defaultRenderer to render function
+ if ("then" in compiledFn && typeof compiledFn.then === "function") {
+ // Promise, wait to bind
+ return compiledFn.then((fn) => {
+ if (typeof fn === "function") {
+ return fn.bind({
+ defaultRenderer: defaultCompilationFn,
+ });
+ }
+ return fn;
+ });
+ } else if ("bind" in compiledFn && typeof compiledFn.bind === "function") {
+ return compiledFn.bind({
+ defaultRenderer: defaultCompilationFn,
+ });
+ }
+ }
+
+ return compiledFn;
+ }
+
+ 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) {
+ let lastModifiedFile = this.eleventyConfig.getPreviousBuildModifiedFile();
+ // 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 isRelevant = this.isFileRelevantTo(inputPath, lastModifiedFile, false);
+ let useCache = !isRelevant;
+
+ 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
+ // Note: `false` is the same as "raw" here.
+ return false;
+ }
+
+ static shouldSpiderJavaScriptDependencies(entry) {
+ if (entry.compileOptions && "spiderJavaScriptDependencies" in entry.compileOptions) {
+ return entry.compileOptions.spiderJavaScriptDependencies;
+ }
+
+ return false;
+ }
+}
diff --git a/node_modules/@11ty/eleventy/src/Engines/FrontMatter/JavaScript.js b/node_modules/@11ty/eleventy/src/Engines/FrontMatter/JavaScript.js
new file mode 100644
index 0000000..b91ba36
--- /dev/null
+++ b/node_modules/@11ty/eleventy/src/Engines/FrontMatter/JavaScript.js
@@ -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 it’s handled in Eleventy upstream.
+ return vm.getGlobalContext(data, {
+ reuseGlobal: true,
+ dynamicImport: true,
+ // addRequire: true,
+ });
+}
diff --git a/node_modules/@11ty/eleventy/src/Engines/Html.js b/node_modules/@11ty/eleventy/src/Engines/Html.js
new file mode 100644
index 0000000..a0f4101
--- /dev/null
+++ b/node_modules/@11ty/eleventy/src/Engines/Html.js
@@ -0,0 +1,33 @@
+import TemplateEngine from "./TemplateEngine.js";
+
+export default class Html extends TemplateEngine {
+ constructor(name, eleventyConfig) {
+ super(name, eleventyConfig);
+ }
+
+ get cacheable() {
+ return true;
+ }
+
+ async #getPreEngine(preTemplateEngine) {
+ return this.engineManager.getEngine(preTemplateEngine, this.extensionMap);
+ }
+
+ async compile(str, inputPath, preTemplateEngine) {
+ if (preTemplateEngine) {
+ let engine = await this.#getPreEngine(preTemplateEngine);
+ let fnReady = engine.compile(str, inputPath);
+
+ return async function (data) {
+ let fn = await fnReady;
+
+ return fn(data);
+ };
+ }
+
+ return function () {
+ // do nothing with data if preTemplateEngine is falsy
+ return str;
+ };
+ }
+}
diff --git a/node_modules/@11ty/eleventy/src/Engines/JavaScript.js b/node_modules/@11ty/eleventy/src/Engines/JavaScript.js
new file mode 100644
index 0000000..29b3b7c
--- /dev/null
+++ b/node_modules/@11ty/eleventy/src/Engines/JavaScript.js
@@ -0,0 +1,240 @@
+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 { EleventyImport } from "../Util/Require.js";
+import { augmentFunction, augmentObject } from "./Util/ContextAugmenter.js";
+
+class JavaScriptTemplateNotDefined extends EleventyBaseError {}
+
+export default class JavaScript extends TemplateEngine {
+ constructor(name, templateConfig) {
+ super(name, templateConfig);
+ this.instances = {};
+
+ this.config.events.on("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];
+ }
+ }
+ });
+ }
+
+ get cacheable() {
+ return false;
+ }
+
+ 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();
+ let cacheBust = !this.cacheable || !this.config.useTemplateCache;
+ mod = await EleventyImport(inputPath, isEsm ? "esm" : "cjs", {
+ cacheBust,
+ });
+ }
+
+ 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 (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));
+ };
+ }
+ }
+
+ static shouldSpiderJavaScriptDependencies() {
+ return true;
+ }
+}
diff --git a/node_modules/@11ty/eleventy/src/Engines/Liquid.js b/node_modules/@11ty/eleventy/src/Engines/Liquid.js
new file mode 100644
index 0000000..44fdab4
--- /dev/null
+++ b/node_modules/@11ty/eleventy/src/Engines/Liquid.js
@@ -0,0 +1,331 @@
+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");
+
+export default 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);
+ }
+
+ get cacheable() {
+ return 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, Boolean(this.config.libraryOverrides.liquid));
+
+ 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 *don’t* 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) {
+ if (!str) {
+ return [];
+ }
+
+ 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;
+ }
+
+ // Don’t 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);
+ };
+ }
+}
diff --git a/node_modules/@11ty/eleventy/src/Engines/Markdown.js b/node_modules/@11ty/eleventy/src/Engines/Markdown.js
new file mode 100644
index 0000000..ec1e1f6
--- /dev/null
+++ b/node_modules/@11ty/eleventy/src/Engines/Markdown.js
@@ -0,0 +1,100 @@
+import markdownIt from "markdown-it";
+
+import TemplateEngine from "./TemplateEngine.js";
+
+export default class Markdown extends TemplateEngine {
+ constructor(name, eleventyConfig) {
+ super(name, eleventyConfig);
+
+ this.markdownOptions = {};
+
+ this.setLibrary(this.config.libraryOverrides.md);
+ }
+
+ get cacheable() {
+ return 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, Boolean(this.config.libraryOverrides.md));
+ }
+
+ 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 || {},
+ );
+ }
+
+ // TODO use preTemplateEngine to help inform this
+ // needsCompilation() {
+ // return super.needsCompilation();
+ // }
+
+ async #getPreEngine(preTemplateEngine) {
+ if (typeof preTemplateEngine === "string") {
+ return this.engineManager.getEngine(preTemplateEngine, this.extensionMap);
+ }
+
+ return preTemplateEngine;
+ }
+
+ async compile(str, inputPath, preTemplateEngine, bypassMarkdown) {
+ let mdlib = this.mdLib;
+
+ if (preTemplateEngine) {
+ let engine = await this.#getPreEngine(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);
+ };
+ }
+ }
+ }
+}
diff --git a/node_modules/@11ty/eleventy/src/Engines/Nunjucks.js b/node_modules/@11ty/eleventy/src/Engines/Nunjucks.js
new file mode 100755
index 0000000..70cca17
--- /dev/null
+++ b/node_modules/@11ty/eleventy/src/Engines/Nunjucks.js
@@ -0,0 +1,482 @@
+import NunjucksLib from "nunjucks";
+import debugUtil from "debug";
+import { TemplatePath } from "@11ty/eleventy-utils";
+
+import TemplateEngine from "./TemplateEngine.js";
+import EleventyBaseError from "../Errors/EleventyBaseError.js";
+import { augmentObject } from "./Util/ContextAugmenter.js";
+import { withResolvers } from "../Util/PromiseUtil.js";
+
+const debug = debugUtil("Eleventy:Nunjucks");
+
+class EleventyNunjucksError extends EleventyBaseError {}
+
+export default 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);
+ }
+
+ // v3.1.0-alpha.1 we’ve moved to use Nunjucks’ internal cache instead of Eleventy’s
+ // get cacheable() {
+ // return false;
+ // }
+
+ #getFileSystemDirs() {
+ let paths = new Set();
+ paths.add(super.getIncludesDir());
+ paths.add(TemplatePath.getWorkingDir());
+
+ // Filter out undefined paths
+ return Array.from(paths).filter(Boolean);
+ }
+
+ #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 fsLoader = new NunjucksLib.FileSystemLoader(this.#getFileSystemDirs());
+ 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);
+
+ // Note that a new Nunjucks engine instance is created for subsequent builds
+ // Eleventy Nunjucks is set to `cacheable` false above to opt out of Eleventy cache
+ this.config.events.on("eleventy#templateModified", (templatePath) => {
+ // NunjucksEnvironment:
+ // loader.pathToNames: {'ABSOLUTE_PATH/src/_includes/components/possum-home.css': 'components/possum-home.css'}
+ // loader.cache: { 'components/possum-home.css': [Template] }
+ // Nunjucks stores these as Operating System native paths
+ let absTmplPath = TemplatePath.normalizeOperatingSystemFilePath(
+ TemplatePath.absolutePath(templatePath),
+ );
+ for (let loader of this.njkEnv.loaders) {
+ let nunjucksName = loader.pathsToNames[absTmplPath];
+ if (nunjucksName) {
+ debug(
+ "Match found in Nunjucks cache via templateModified for %o, clearing this entry",
+ templatePath,
+ );
+ delete loader.pathsToNames[absTmplPath];
+ delete loader.cache[nunjucksName];
+ }
+ }
+
+ // Behavior prior to v3.1.0-alpha.1:
+ // this.njkEnv.invalidateCache();
+ });
+
+ this.setEngineLib(this.njkEnv, Boolean(this.config.libraryOverrides.njk));
+
+ 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());
+ }
+
+ // Don’t 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) {
+ if (!str) {
+ return [];
+ }
+ const { parser, nodes } = NunjucksLib;
+ let obj = parser.parse(str, this._getParseExtensions());
+ if (!obj) {
+ return [];
+ }
+ 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) {
+ let { promise, resolve, reject } = withResolvers();
+
+ tmpl.render(data, (error, result) => {
+ if (error) {
+ reject(error);
+ } else {
+ resolve(result);
+ }
+ });
+
+ return promise;
+ };
+ }
+}
diff --git a/node_modules/@11ty/eleventy/src/Engines/TemplateEngine.js b/node_modules/@11ty/eleventy/src/Engines/TemplateEngine.js
new file mode 100644
index 0000000..234aa4e
--- /dev/null
+++ b/node_modules/@11ty/eleventy/src/Engines/TemplateEngine.js
@@ -0,0 +1,206 @@
+import debugUtil from "debug";
+import EleventyBaseError from "../Errors/EleventyBaseError.js";
+
+class TemplateEngineConfigError extends EleventyBaseError {}
+
+const debug = debugUtil("Eleventy:TemplateEngine");
+
+const AMENDED_INSTANCES = new Set();
+
+export default class TemplateEngine {
+ #extensionMap;
+ #engineManager;
+ #benchmarks;
+
+ constructor(name, eleventyConfig) {
+ this.name = name;
+
+ this.engineLib = null;
+
+ if (!eleventyConfig) {
+ throw new TemplateEngineConfigError("Missing `eleventyConfig` argument.");
+ }
+ this.eleventyConfig = eleventyConfig;
+ }
+
+ get cacheable() {
+ return false;
+ }
+
+ 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) {
+ throw new Error("Internal error: missing `extensionMap` in TemplateEngine.");
+ }
+ 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, isOverrideViaSetLibrary = false) {
+ this.engineLib = engineLib;
+
+ // Run engine amendments (via issue #2438)
+ // Issue #3816: this isn’t ideal but there is no other way to reset a markdown instance if it was also overridden by addLibrary
+ if (AMENDED_INSTANCES.has(engineLib)) {
+ return;
+ }
+
+ if (isOverrideViaSetLibrary) {
+ AMENDED_INSTANCES.add(engineLib);
+ }
+ debug(
+ "Running amendLibrary for %o (number of amendments: %o)",
+ this.name,
+ this.config.libraryAmendments[this.name]?.length,
+ );
+
+ for (let amendment of this.config.libraryAmendments[this.name] || []) {
+ // TODO it’d 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);
+ }
+}
diff --git a/node_modules/@11ty/eleventy/src/Engines/TemplateEngineManager.js b/node_modules/@11ty/eleventy/src/Engines/TemplateEngineManager.js
new file mode 100644
index 0000000..913a803
--- /dev/null
+++ b/node_modules/@11ty/eleventy/src/Engines/TemplateEngineManager.js
@@ -0,0 +1,193 @@
+import debugUtil from "debug";
+import EleventyBaseError from "../Errors/EleventyBaseError.js";
+
+const debug = debugUtil("Eleventy:TemplateEngineManager");
+
+class TemplateEngineManager {
+ constructor(eleventyConfig) {
+ if (!eleventyConfig || eleventyConfig.constructor.name !== "TemplateConfig") {
+ throw new EleventyBaseError("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);
+ }
+
+ 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 they’re parsed by a bundler.
+ if (extension === "md") {
+ promise = import("./Markdown.js").then((mod) => mod.default);
+ } else if (extension === "html") {
+ promise = import("./Html.js").then((mod) => mod.default);
+ } else if (extension === "njk") {
+ promise = import("./Nunjucks.js").then((mod) => mod.default);
+ } else if (extension === "liquid") {
+ promise = import("./Liquid.js").then((mod) => mod.default);
+ } else if (extension === "11ty.js") {
+ promise = import("./JavaScript.js").then((mod) => mod.default);
+ } else {
+ promise = this.getCustomEngineClass();
+ }
+
+ this.importCache[extension] = promise;
+
+ return promise;
+ }
+
+ async getCustomEngineClass() {
+ if (!this._CustomEngine) {
+ this._CustomEngine = import("./Custom.js").then((mod) => mod.default);
+ }
+ 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;
+ }
+
+ isEngineRemovedFromCore(name) {
+ return ["ejs", "hbs", "mustache", "haml", "pug"].includes(name) && !this.hasEngine(name);
+ }
+
+ async getEngine(name, extensionMap) {
+ // Bundled 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 engine 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;
diff --git a/node_modules/@11ty/eleventy/src/Engines/Util/ContextAugmenter.js b/node_modules/@11ty/eleventy/src/Engines/Util/ContextAugmenter.js
new file mode 100644
index 0000000..dd5fbc6
--- /dev/null
+++ b/node_modules/@11ty/eleventy/src/Engines/Util/ContextAugmenter.js
@@ -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 };