summaryrefslogtreecommitdiff
path: root/node_modules/@11ty/eleventy/src/GlobalDependencyMap.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/GlobalDependencyMap.js
parent53d6ae2b5568437afa5e4995580a3fb679b7b91b (diff)
Changed from static to 11ty!
Diffstat (limited to 'node_modules/@11ty/eleventy/src/GlobalDependencyMap.js')
-rw-r--r--node_modules/@11ty/eleventy/src/GlobalDependencyMap.js463
1 files changed, 463 insertions, 0 deletions
diff --git a/node_modules/@11ty/eleventy/src/GlobalDependencyMap.js b/node_modules/@11ty/eleventy/src/GlobalDependencyMap.js
new file mode 100644
index 0000000..7e169da
--- /dev/null
+++ b/node_modules/@11ty/eleventy/src/GlobalDependencyMap.js
@@ -0,0 +1,463 @@
+import debugUtil from "debug";
+import { TemplatePath } from "@11ty/eleventy-utils";
+
+import JavaScriptDependencies from "./Util/JavaScriptDependencies.js";
+import PathNormalizer from "./Util/PathNormalizer.js";
+import { TemplateDepGraph } from "./Util/TemplateDepGraph.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:"; // must match TemplateDepGraph key
+
+ #map;
+ #templateConfig;
+ #cachedUserConfigurationCollectionApiNames;
+
+ static isCollection(entry) {
+ return entry.startsWith(this.COLLECTION_PREFIX);
+ }
+
+ static getTagName(entry) {
+ if (this.isCollection(entry)) {
+ return entry.slice(this.COLLECTION_PREFIX.length);
+ }
+ }
+
+ static getCollectionKeyForEntry(entry) {
+ return `${GlobalDependencyMap.COLLECTION_PREFIX}${entry}`;
+ }
+
+ reset() {
+ this.#map = undefined;
+ }
+
+ setIsEsm(isEsm) {
+ this.isEsm = isEsm;
+ }
+
+ setTemplateConfig(templateConfig) {
+ this.#templateConfig = templateConfig;
+
+ // These have leading dot slashes, but so do the paths from Eleventy
+ this.#templateConfig.userConfig.events.once("eleventy.layouts", async (layouts) => {
+ await this.addLayoutsToMap(layouts);
+ });
+ }
+
+ get userConfigurationCollectionApiNames() {
+ if (this.#cachedUserConfigurationCollectionApiNames) {
+ return this.#cachedUserConfigurationCollectionApiNames;
+ }
+ return Object.keys(this.#templateConfig.userConfig.getCollections()) || [];
+ }
+
+ initializeUserConfigurationApiCollections() {
+ this.addCollectionApiNames(this.userConfigurationCollectionApiNames);
+ }
+
+ // For Testing
+ setCollectionApiNames(names = []) {
+ this.#cachedUserConfigurationCollectionApiNames = names;
+ }
+
+ addCollectionApiNames(names = []) {
+ if (!names || names.length === 0) {
+ return;
+ }
+
+ for (let collectionName of names) {
+ this.map.addConfigCollectionName(collectionName);
+ }
+ }
+
+ filterOutLayouts(nodes = []) {
+ return nodes.filter((node) => {
+ if (GlobalDependencyMap.isLayoutNode(this.map, node)) {
+ return false;
+ }
+ return true;
+ });
+ }
+
+ filterOutCollections(nodes = []) {
+ return nodes.filter((node) => !node.startsWith(GlobalDependencyMap.COLLECTION_PREFIX));
+ }
+
+ static removeLayoutNodes(graph, nodeList) {
+ return nodeList.filter((node) => {
+ if (this.isLayoutNode(graph, node)) {
+ return false;
+ }
+ return true;
+ });
+ }
+
+ removeLayoutNodes(normalizedLayouts) {
+ let nodes = this.map.overallOrder();
+ for (let node of nodes) {
+ if (!GlobalDependencyMap.isLayoutNode(this.map, node)) {
+ 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 don’t 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 });
+ this.#map = new TemplateDepGraph();
+ }
+
+ 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 [];
+ }
+
+ node = this.normalizeNode(node);
+
+ if (!this.map.hasNode(node)) {
+ return [];
+ }
+
+ // 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)
+ .map((entry) => GlobalDependencyMap.getTagName(entry))
+ .filter(Boolean);
+
+ 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 don’t 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 collectionKey = GlobalDependencyMap.getCollectionKeyForEntry(name);
+ if (!this.map.hasNode(collectionKey)) {
+ continue;
+ }
+ for (let node of this.map.dependantsOf(collectionKey)) {
+ if (!node.startsWith(GlobalDependencyMap.COLLECTION_PREFIX)) {
+ if (!GlobalDependencyMap.isLayoutNode(this.map, node)) {
+ templates.add(node);
+ }
+ }
+ }
+ }
+ return templates;
+ }
+
+ static isLayoutNode(graph, node) {
+ if (!graph.hasNode(node)) {
+ return false;
+ }
+ return graph.getNodeData(node)?.type === GlobalDependencyMap.LAYOUT_KEY;
+ }
+
+ getLayoutsUsedBy(node) {
+ node = this.normalizeNode(node);
+
+ if (!this.map.hasNode(node)) {
+ return [];
+ }
+
+ let layouts = [];
+
+ // include self, if layout
+ if (GlobalDependencyMap.isLayoutNode(this.map, node)) {
+ layouts.push(node);
+ }
+
+ this.map.dependantsOf(node).forEach((node) => {
+ // we only want layouts
+ if (GlobalDependencyMap.isLayoutNode(this.map, node)) {
+ 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;
+ }
+
+ if (includeLayouts) {
+ return this.map.dependenciesOf(node).filter(Boolean);
+ }
+
+ return GlobalDependencyMap.removeLayoutNodes(this.map, this.map.dependenciesOf(node));
+ }
+
+ #addNode(name) {
+ if (this.map.hasNode(name)) {
+ return;
+ }
+
+ this.map.addNode(name);
+ }
+
+ // node arguments are already normalized
+ #addDependency(from, toArray = []) {
+ this.#addNode(from); // only if not already added
+
+ 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) {
+ this.#addNode(to); // only if not already added
+ if (from !== to) {
+ this.map.addDependency(from, to);
+ }
+ }
+ }
+
+ addDependency(from, toArray = []) {
+ this.#addDependency(
+ this.normalizeNode(from),
+ toArray.map((to) => this.normalizeNode(to)),
+ );
+ }
+
+ addNewNodeRelationships(from, consumes = [], publishes = []) {
+ consumes = consumes.filter(Boolean);
+ publishes = publishes.filter(Boolean);
+
+ debug("%o consumes %o and publishes to %o", from, consumes, publishes);
+ from = this.normalizeNode(from);
+
+ this.map.addTemplate(from, consumes, publishes);
+ }
+
+ // 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;
+ }
+
+ getTemplateOrder() {
+ let order = [];
+ for (let entry of this.map.overallOrder()) {
+ order.push(entry);
+ }
+
+ return order;
+ }
+
+ 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 TemplateDepGraph();
+
+ // 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;