patrickarlt/acetate

View on GitHub
lib/helpers/link-helper.js

Summary

Maintainability
B
5 hrs
Test Coverage
const url = require("url");
const _ = require("lodash");
const {
  stripSlashes,
  ensureTrailingSlash,
  ensureLeadingSlash,
  normalizeUrl
} = require("../utils.js");
const PROTOCOL_REGEX = /^(.+:)?\/\//;
const HASH_REGEX = /^#/;

module.exports = function(acetate) {
  acetate.helper(
    "link",
    function(context, destinationUrl, text) {
      const currentUrl = context.options.currentUrl || context.page.url;
      const options = context.options;
      let finalUrl;
      let resolvedUrl;
      let isActive = false;

      if (
        PROTOCOL_REGEX.test(destinationUrl) ||
        HASH_REGEX.test(destinationUrl)
      ) {
        finalUrl = destinationUrl;
      } else {
        finalUrl = normalizeUrl(
          ensureTrailingSlash(ensureLeadingSlash(stripSlashes(destinationUrl)))
        );

        resolvedUrl = normalizeUrl(
          ensureTrailingSlash(
            ensureLeadingSlash(
              stripSlashes(url.resolve(currentUrl, destinationUrl))
            )
          )
        );
      }

      const hrefAttr = `href="${finalUrl}"`;
      const idAttr = options.id ? `id="${options.id}"` : "";

      if (currentUrl === resolvedUrl || currentUrl === finalUrl) {
        isActive = true;
      } else if (PROTOCOL_REGEX.test(destinationUrl)) {
        isActive = false;
      } else if (HASH_REGEX.test(destinationUrl)) {
        isActive = true;
      } else if (options.requireExactMatch) {
        isActive = currentUrl === resolvedUrl;
      } else {
        let parsedFinalUrl = url.parse(finalUrl);
        let parsedResolvedUrl = url.parse(resolvedUrl);
        isActive =
          (currentUrl.match(parsedResolvedUrl.pathname) ||
            currentUrl.match(parsedFinalUrl.pathname)) &&
          finalUrl !== "/";
      }

      const classes = isActive
        ? _([options.activeClass, options.class])
            .compact()
            .join(" ")
        : options.class;
      const classAttr = classes && classes.length ? `class="${classes}"` : "";

      delete options.id;
      delete options.activeClass;
      delete options.class;
      delete options.requireExactMatch;
      delete options.currentUrl;

      const attrs = _(options)
        .map((value, key) => `${key}="${value}"`)
        .reverse()
        .concat([classAttr, idAttr, hrefAttr])
        .reverse()
        .compact()
        .join(" ");

      return `<a ${attrs}>${text}</a>`;
    },
    {
      activeClass: "is-active",
      requireExactMatch: false
    }
  );
};