UNPKG

@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
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 };