@yankeeinlondon/link-builder
Version:
adds contextual classes and features to links in documents used in vite-plugin-md
657 lines (649 loc) • 19.5 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
// node_modules/.pnpm/fp-ts@2.13.1/node_modules/fp-ts/lib/function.js
var require_function = __commonJS({
"node_modules/.pnpm/fp-ts@2.13.1/node_modules/fp-ts/lib/function.js"(exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getEndomorphismMonoid = exports.not = exports.SK = exports.hole = exports.pipe = exports.untupled = exports.tupled = exports.absurd = exports.decrement = exports.increment = exports.tuple = exports.flow = exports.flip = exports.constVoid = exports.constUndefined = exports.constNull = exports.constFalse = exports.constTrue = exports.constant = exports.unsafeCoerce = exports.identity = exports.apply = exports.getRing = exports.getSemiring = exports.getMonoid = exports.getSemigroup = exports.getBooleanAlgebra = void 0;
var getBooleanAlgebra = function(B) {
return function() {
return {
meet: function(x, y) {
return function(a) {
return B.meet(x(a), y(a));
};
},
join: function(x, y) {
return function(a) {
return B.join(x(a), y(a));
};
},
zero: function() {
return B.zero;
},
one: function() {
return B.one;
},
implies: function(x, y) {
return function(a) {
return B.implies(x(a), y(a));
};
},
not: function(x) {
return function(a) {
return B.not(x(a));
};
}
};
};
};
exports.getBooleanAlgebra = getBooleanAlgebra;
var getSemigroup = function(S) {
return function() {
return {
concat: function(f, g) {
return function(a) {
return S.concat(f(a), g(a));
};
}
};
};
};
exports.getSemigroup = getSemigroup;
var getMonoid = function(M) {
var getSemigroupM = (0, exports.getSemigroup)(M);
return function() {
return {
concat: getSemigroupM().concat,
empty: function() {
return M.empty;
}
};
};
};
exports.getMonoid = getMonoid;
var getSemiring = function(S) {
return {
add: function(f, g) {
return function(x) {
return S.add(f(x), g(x));
};
},
zero: function() {
return S.zero;
},
mul: function(f, g) {
return function(x) {
return S.mul(f(x), g(x));
};
},
one: function() {
return S.one;
}
};
};
exports.getSemiring = getSemiring;
var getRing = function(R) {
var S = (0, exports.getSemiring)(R);
return {
add: S.add,
mul: S.mul,
one: S.one,
zero: S.zero,
sub: function(f, g) {
return function(x) {
return R.sub(f(x), g(x));
};
}
};
};
exports.getRing = getRing;
var apply = function(a) {
return function(f) {
return f(a);
};
};
exports.apply = apply;
function identity(a) {
return a;
}
exports.identity = identity;
exports.unsafeCoerce = identity;
function constant(a) {
return function() {
return a;
};
}
exports.constant = constant;
exports.constTrue = constant(true);
exports.constFalse = constant(false);
exports.constNull = constant(null);
exports.constUndefined = constant(void 0);
exports.constVoid = exports.constUndefined;
function flip(f) {
return function() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
if (args.length > 1) {
return f(args[1], args[0]);
}
return function(a) {
return f(a)(args[0]);
};
};
}
exports.flip = flip;
function flow(ab, bc, cd, de, ef, fg, gh, hi, ij) {
switch (arguments.length) {
case 1:
return ab;
case 2:
return function() {
return bc(ab.apply(this, arguments));
};
case 3:
return function() {
return cd(bc(ab.apply(this, arguments)));
};
case 4:
return function() {
return de(cd(bc(ab.apply(this, arguments))));
};
case 5:
return function() {
return ef(de(cd(bc(ab.apply(this, arguments)))));
};
case 6:
return function() {
return fg(ef(de(cd(bc(ab.apply(this, arguments))))));
};
case 7:
return function() {
return gh(fg(ef(de(cd(bc(ab.apply(this, arguments)))))));
};
case 8:
return function() {
return hi(gh(fg(ef(de(cd(bc(ab.apply(this, arguments))))))));
};
case 9:
return function() {
return ij(hi(gh(fg(ef(de(cd(bc(ab.apply(this, arguments)))))))));
};
}
return;
}
exports.flow = flow;
function tuple() {
var t = [];
for (var _i = 0; _i < arguments.length; _i++) {
t[_i] = arguments[_i];
}
return t;
}
exports.tuple = tuple;
function increment(n) {
return n + 1;
}
exports.increment = increment;
function decrement(n) {
return n - 1;
}
exports.decrement = decrement;
function absurd(_) {
throw new Error("Called `absurd` function which should be uncallable");
}
exports.absurd = absurd;
function tupled(f) {
return function(a) {
return f.apply(void 0, a);
};
}
exports.tupled = tupled;
function untupled(f) {
return function() {
var a = [];
for (var _i = 0; _i < arguments.length; _i++) {
a[_i] = arguments[_i];
}
return f(a);
};
}
exports.untupled = untupled;
function pipe2(a, ab, bc, cd, de, ef, fg, gh, hi) {
switch (arguments.length) {
case 1:
return a;
case 2:
return ab(a);
case 3:
return bc(ab(a));
case 4:
return cd(bc(ab(a)));
case 5:
return de(cd(bc(ab(a))));
case 6:
return ef(de(cd(bc(ab(a)))));
case 7:
return fg(ef(de(cd(bc(ab(a))))));
case 8:
return gh(fg(ef(de(cd(bc(ab(a)))))));
case 9:
return hi(gh(fg(ef(de(cd(bc(ab(a))))))));
default: {
var ret = arguments[0];
for (var i = 1; i < arguments.length; i++) {
ret = arguments[i](ret);
}
return ret;
}
}
}
exports.pipe = pipe2;
exports.hole = absurd;
var SK = function(_, b) {
return b;
};
exports.SK = SK;
function not(predicate) {
return function(a) {
return !predicate(a);
};
}
exports.not = not;
var getEndomorphismMonoid = function() {
return {
concat: function(first, second) {
return flow(first, second);
},
empty: identity
};
};
exports.getEndomorphismMonoid = getEndomorphismMonoid;
}
});
// src/link.ts
import { createBuilder } from "@yankeeinlondon/builder-api";
// node_modules/.pnpm/pathe@1.0.0/node_modules/pathe/dist/shared/pathe.fb014aa6.mjs
function normalizeWindowsPath(input = "") {
if (!input || !input.includes("\\")) {
return input;
}
return input.replace(/\\/g, "/");
}
var _UNC_REGEX = /^[/\\]{2}/;
var _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
var _DRIVE_LETTER_RE = /^[A-Za-z]:$/;
var normalize = function(path) {
if (path.length === 0) {
return ".";
}
path = normalizeWindowsPath(path);
const isUNCPath = path.match(_UNC_REGEX);
const isPathAbsolute = isAbsolute(path);
const trailingSeparator = path[path.length - 1] === "/";
path = normalizeString(path, !isPathAbsolute);
if (path.length === 0) {
if (isPathAbsolute) {
return "/";
}
return trailingSeparator ? "./" : ".";
}
if (trailingSeparator) {
path += "/";
}
if (_DRIVE_LETTER_RE.test(path)) {
path += "/";
}
if (isUNCPath) {
if (!isPathAbsolute) {
return `//./${path}`;
}
return `//${path}`;
}
return isPathAbsolute && !isAbsolute(path) ? `/${path}` : path;
};
var join = function(...arguments_) {
if (arguments_.length === 0) {
return ".";
}
let joined;
for (const argument of arguments_) {
if (argument && argument.length > 0) {
if (joined === void 0) {
joined = argument;
} else {
joined += `/${argument}`;
}
}
}
if (joined === void 0) {
return ".";
}
return normalize(joined.replace(/\/\/+/g, "/"));
};
function normalizeString(path, allowAboveRoot) {
let res = "";
let lastSegmentLength = 0;
let lastSlash = -1;
let dots = 0;
let char = null;
for (let index = 0; index <= path.length; ++index) {
if (index < path.length) {
char = path[index];
} else if (char === "/") {
break;
} else {
char = "/";
}
if (char === "/") {
if (lastSlash === index - 1 || dots === 1)
;
else if (dots === 2) {
if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
if (res.length > 2) {
const lastSlashIndex = res.lastIndexOf("/");
if (lastSlashIndex === -1) {
res = "";
lastSegmentLength = 0;
} else {
res = res.slice(0, lastSlashIndex);
lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
}
lastSlash = index;
dots = 0;
continue;
} else if (res.length > 0) {
res = "";
lastSegmentLength = 0;
lastSlash = index;
dots = 0;
continue;
}
}
if (allowAboveRoot) {
res += res.length > 0 ? "/.." : "..";
lastSegmentLength = 2;
}
} else {
if (res.length > 0) {
res += `/${path.slice(lastSlash + 1, index)}`;
} else {
res = path.slice(lastSlash + 1, index);
}
lastSegmentLength = index - lastSlash - 1;
}
lastSlash = index;
dots = 0;
} else if (char === "." && dots !== -1) {
++dots;
} else {
dots = -1;
}
}
return res;
}
var isAbsolute = function(p) {
return _IS_ABSOLUTE_RE.test(p);
};
var dirname = function(p) {
const segments = normalizeWindowsPath(p).replace(/\/$/, "").split("/").slice(0, -1);
if (segments.length === 1 && _DRIVE_LETTER_RE.test(segments[0])) {
segments[0] += "/";
}
return segments.join("/") || (isAbsolute(p) ? "/" : ".");
};
// src/link.ts
var import_function = __toESM(require_function(), 1);
// src/utils/keys.ts
function keys(obj, ...without) {
const v = without.length > 0 ? Object.keys(obj).filter((k) => !without.includes(k)) : Object.keys(obj);
return v;
}
// src/mdi.ts
function toDictionary(t, base) {
const tagName = t.tag;
if (t.attrs === null) {
return { tagName, _base: base };
}
return t.attrs.reduce((acc, [k, v]) => {
return __spreadProps(__spreadValues({}, acc), { [k]: v, tagName, _base: base });
}, {});
}
var MdLink = (o) => (md) => {
if (!o || !o.transform) {
throw new Error(
"link plugin requires a transform function to do it's job!"
);
} else {
const base = join("/", o.base || dirname(o.file));
md.renderer.rules.link_open = function(tokens, idx, options, env, self) {
var _a;
const link2 = tokens[idx];
const original = toDictionary(link2, base);
const transformed = o.transform(original);
link2.tag = transformed.tagName;
delete transformed.tagName;
for (const [k, v] of Object.entries(transformed)) {
const attrIndex = link2.attrIndex(k);
if (attrIndex < 0 && v !== void 0 && !k.startsWith("_")) {
link2.attrPush([k, v]);
} else if (attrIndex >= 0) {
if (v !== void 0 && !k.startsWith("_")) {
link2.attrSet(k, v);
} else {
link2.attrs = (_a = link2.attrs) == null ? void 0 : _a.filter((i) => i[0] !== k);
}
}
}
return self.renderToken(tokens, idx, options);
};
md.renderer.rules.link_close = function(tokens, idx, options, _env, self) {
const close = tokens[idx];
const priors = tokens.slice(0, idx).reverse();
let open;
let openIdx = -1;
for (const [idx2, t] of priors.entries()) {
if (t.type === "link_open" && !open) {
open = t;
openIdx = idx2;
}
}
const between = priors.slice(0, openIdx + 1).reverse();
close.tag = open ? open.tag : "a";
return self.renderToken(tokens, idx, options);
};
}
};
// src/link.ts
var staticRuleLookup = {
externalLinkClass: /^https?:/,
internalLinkClass: /^[./a-z]*[^:]*$/,
relativeLinkClass: /^[.a-z][./a-z]*[^:]*$/,
fullyQualifiedLinkClass: /^\/[.a-z]*[^:]*$/,
anchorTagClass: /^#/,
insecureClass: /^http:/,
fileClass: /^file:/,
mailtoClass: /^mailto:/,
imageClass: /\.(gif|jpg|jpeg|png|webp|avif|tiff|heif)$/,
documentClass: /\.(doc|pdf|txt|xls)$/
};
function isExternalLink(meta) {
return !!(meta.href && staticRuleLookup.externalLinkClass.test(meta.href));
}
function isInternalLink(meta) {
return !!(meta.href && staticRuleLookup.internalLinkClass.test(meta.href));
}
function strTransform(transformer, meta) {
if (!transformer) {
return void 0;
} else if (typeof transformer === "function") {
return transformer(meta);
} else {
return transformer;
}
}
var addClasses = (c) => (lnk) => {
const classes = keys(staticRuleLookup).flatMap((key) => {
if (!lnk.href || !staticRuleLookup[key].test(lnk.href)) {
return;
}
switch (typeof c[key]) {
case "function":
return c[key](lnk);
case "string":
return c[key];
case "undefined":
return;
default:
throw new Error(
`Unexpected property type for property ${key} [${typeof c[key]}]`
);
}
}).filter(Boolean);
if (lnk.href) {
for (const rc of c.ruleBasedClasses) {
const [rule, klass] = rc;
if (rule.test(lnk.href)) {
lnk.class = [lnk.class, klass].join(" ");
}
}
}
return __spreadProps(__spreadValues({}, lnk), { class: [lnk.class, ...classes].join(" ") });
};
var addTargetingAndRel = (c) => (lnk) => {
if (isExternalLink(lnk)) {
lnk.target = strTransform(c.externalTarget, lnk);
lnk.rel = strTransform(c.externalRel, lnk);
}
if (isInternalLink(lnk)) {
lnk.target = strTransform(c.internalTarget, lnk);
lnk.rel = strTransform(c.internalRel, lnk);
}
return lnk;
};
var cleanupAndCloseOut = (c) => (lnk) => {
if (isInternalLink(lnk)) {
if (lnk.href && lnk.href.trim().endsWith("index.md") && c.cleanIndexRoutes) {
lnk.href = lnk.href.replace(/index\.md\s*/, "");
} else if (lnk.href && lnk.href.trim().endsWith(".md") && c.cleanAllRoutes) {
{
lnk.href = lnk.href.replace(/(\S+)\.md$/, "$1");
}
}
const removal = new RegExp(`.*${c.rootDir}`);
if (isInternalLink(lnk)) {
lnk.href = join(lnk._base.replace(removal, ""), lnk.href);
}
if (lnk.tagName === "a" && c.useRouterLinks) {
lnk = __spreadProps(__spreadValues({}, lnk), {
to: lnk.href,
href: void 0,
tagName: "router-link",
class: [lnk.class, c.routerLinkClass].join(" ")
});
}
}
return lnk;
};
var link = createBuilder("link", "parser").options().initializer().handler(async (p, o) => {
const {
fileName,
viteConfig: { base }
} = p;
const options = __spreadValues({
rootDir: "src/pages",
externalLinkClass: "external-link",
internalLinkClass: "internal-link",
routerLinkClass: "router-link",
relativeLinkClass: void 0,
fullyQualifiedLinkClass: void 0,
anchorTagClass: "anchor-tag",
insecureClass: "insecure",
fileClass: "file-link",
mailtoClass: "mailto-link",
imageClass: "image-reference",
documentClass: "doc-reference",
ruleBasedClasses: [],
externalTarget: "_blank",
externalRel: "noreferrer noopener",
internalTarget: void 0,
internalRel: void 0,
useRouterLinks: true,
cleanIndexRoutes: true,
cleanAllRoutes: true,
postProcessing: (f) => f
}, o);
const [cleanup, targeting, classes] = [
cleanupAndCloseOut(options),
addTargetingAndRel(options),
addClasses(options)
];
const { postProcessing } = options;
const transform = (event) => {
return (0, import_function.pipe)(
event,
classes,
targeting,
cleanup,
postProcessing
);
};
p.parser.use(MdLink({ file: fileName, transform, base }));
return p;
}).meta({
description: "provides default classes for all links based on protocol, content-type, and whether internal or external; also converts <a> tags to <router-links> and cleans up relative paths.",
parserRules: [
{
ruleName: "link_open",
usage: "patches",
description: "allows attributes to modified on link DOM elements, including changing the tag's name from <a> to <router-link>"
},
{
ruleName: "link_close",
usage: "patches",
description: "used to make sure end tag has a matching tag name to start tag"
}
]
});
export {
link as default
};