summaryrefslogtreecommitdiff
path: root/node_modules/@11ty/eleventy/src/TemplateLayout.js
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/TemplateLayout.js
parent53d6ae2b5568437afa5e4995580a3fb679b7b91b (diff)
Changed from static to 11ty!
Diffstat (limited to 'node_modules/@11ty/eleventy/src/TemplateLayout.js')
-rw-r--r--node_modules/@11ty/eleventy/src/TemplateLayout.js240
1 files changed, 240 insertions, 0 deletions
diff --git a/node_modules/@11ty/eleventy/src/TemplateLayout.js b/node_modules/@11ty/eleventy/src/TemplateLayout.js
new file mode 100644
index 0000000..f8526f2
--- /dev/null
+++ b/node_modules/@11ty/eleventy/src/TemplateLayout.js
@@ -0,0 +1,240 @@
+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 layoutCache from "./LayoutCache.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 (!layoutCache.has(fullKey)) {
+ let layout = new TemplateLayout(key, extensionMap, eleventyConfig);
+
+ layoutCache.add(layout);
+ debugDev("Added %o to LayoutCache", key);
+
+ return layout;
+ }
+
+ return layoutCache.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();
+ return this.compile(rawInput);
+ }
+
+ // Do only cache this layout’s 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 LayoutCache after error.");
+ layoutCache.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);
+ }
+
+ // Don’t 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;