summaryrefslogtreecommitdiff
path: root/node_modules/@11ty/eleventy/src/Plugins/IdAttributePlugin.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/@11ty/eleventy/src/Plugins/IdAttributePlugin.js')
-rw-r--r--node_modules/@11ty/eleventy/src/Plugins/IdAttributePlugin.js110
1 files changed, 110 insertions, 0 deletions
diff --git a/node_modules/@11ty/eleventy/src/Plugins/IdAttributePlugin.js b/node_modules/@11ty/eleventy/src/Plugins/IdAttributePlugin.js
new file mode 100644
index 0000000..a55a13e
--- /dev/null
+++ b/node_modules/@11ty/eleventy/src/Plugins/IdAttributePlugin.js
@@ -0,0 +1,110 @@
+import matchHelper from "posthtml-match-helper";
+import { decodeHTML } from "entities";
+
+import slugifyFilter from "../Filters/Slugify.js";
+import MemoizeUtil from "../Util/MemoizeFunction.js";
+
+const POSTHTML_PLUGIN_NAME = "11ty/eleventy/id-attribute";
+
+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 idAttributePosthtmlPlugin(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(
+ 'You have more than one HTML `id` attribute using the same value (id="' +
+ id +
+ '") in your template (' +
+ pluginOptions.page.inputPath +
+ "). You can disable this error in the IdAttribute plugin with the `checkDuplicates: false` option.",
+ );
+ }
+ } 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
+ name: POSTHTML_PLUGIN_NAME,
+ },
+ );
+}
+
+export { IdAttributePlugin };