196 lines
4.7 KiB
JavaScript
196 lines
4.7 KiB
JavaScript
import path from "node:path";
|
||
import { TemplatePath, isPlainObject } from "@11ty/eleventy-utils";
|
||
|
||
class TemplatePermalink {
|
||
// `link` with template syntax should have already been rendered in Template.js
|
||
constructor(link, extraSubdir) {
|
||
let isLinkAnObject = isPlainObject(link);
|
||
|
||
this._isRendered = true;
|
||
this._writeToFileSystem = true;
|
||
|
||
let buildLink;
|
||
|
||
if (isLinkAnObject) {
|
||
if ("build" in link) {
|
||
buildLink = link.build;
|
||
}
|
||
|
||
// find the first string key
|
||
for (let key in link) {
|
||
if (typeof key !== "string") {
|
||
continue;
|
||
}
|
||
break;
|
||
}
|
||
} else {
|
||
buildLink = link;
|
||
}
|
||
|
||
// permalink: false and permalink: build: false
|
||
if (typeof buildLink === "boolean") {
|
||
if (buildLink === false) {
|
||
this._writeToFileSystem = false;
|
||
} else {
|
||
throw new Error(
|
||
`\`permalink: ${
|
||
isLinkAnObject ? "build: " : ""
|
||
}true\` is not a supported feature in Eleventy. Did you mean \`permalink: ${
|
||
isLinkAnObject ? "build: " : ""
|
||
}false\`?`,
|
||
);
|
||
}
|
||
} else if (buildLink) {
|
||
if (typeof buildLink !== "string") {
|
||
let stringToString = "toString" in buildLink ? `:\n\n${buildLink.toString()}` : "";
|
||
throw new Error(
|
||
"Expected permalink value to be a string. Received `" +
|
||
typeof buildLink +
|
||
"`" +
|
||
stringToString,
|
||
);
|
||
}
|
||
this.buildLink = buildLink;
|
||
}
|
||
|
||
if (isLinkAnObject) {
|
||
// default if permalink is an Object but does not have a `build` prop
|
||
if (!("build" in link)) {
|
||
this._writeToFileSystem = false;
|
||
this._isRendered = false;
|
||
}
|
||
}
|
||
|
||
this.extraPaginationSubdir = extraSubdir || "";
|
||
}
|
||
|
||
setUrlTransforms(transforms) {
|
||
this._urlTransforms = transforms;
|
||
}
|
||
|
||
get urlTransforms() {
|
||
return this._urlTransforms || [];
|
||
}
|
||
|
||
_addDefaultLinkFilename(link) {
|
||
return link + (link.slice(-1) === "/" ? "index.html" : "");
|
||
}
|
||
|
||
toOutputPath() {
|
||
if (!this.buildLink) {
|
||
// empty or false
|
||
return false;
|
||
}
|
||
let cleanLink = this._addDefaultLinkFilename(this.buildLink);
|
||
let parsed = path.parse(cleanLink);
|
||
|
||
return TemplatePath.join(parsed.dir, this.extraPaginationSubdir, parsed.base);
|
||
}
|
||
|
||
// Used in url transforms feature
|
||
static getUrlStem(original) {
|
||
let subject = original;
|
||
if (original.endsWith(".html")) {
|
||
subject = original.slice(0, -1 * ".html".length);
|
||
}
|
||
return TemplatePermalink.normalizePathToUrl(subject);
|
||
}
|
||
|
||
static normalizePathToUrl(original) {
|
||
let compare = original || "";
|
||
|
||
let needleHtml = "/index.html";
|
||
let needleBareTrailingSlash = "/index/";
|
||
let needleBare = "/index";
|
||
if (compare.endsWith(needleHtml)) {
|
||
return compare.slice(0, compare.length - needleHtml.length) + "/";
|
||
} else if (compare.endsWith(needleBareTrailingSlash)) {
|
||
return compare.slice(0, compare.length - needleBareTrailingSlash.length) + "/";
|
||
} else if (compare.endsWith(needleBare)) {
|
||
return compare.slice(0, compare.length - needleBare.length) + "/";
|
||
}
|
||
|
||
return original;
|
||
}
|
||
|
||
// This method is used to generate the `page.url` variable.
|
||
|
||
// remove all index.html’s from links
|
||
// index.html becomes /
|
||
// test/index.html becomes test/
|
||
toHref() {
|
||
if (!this.buildLink) {
|
||
// empty or false
|
||
return false;
|
||
}
|
||
|
||
let transformedLink = this.toOutputPath();
|
||
let original = (transformedLink.charAt(0) !== "/" ? "/" : "") + transformedLink;
|
||
|
||
let normalized = TemplatePermalink.normalizePathToUrl(original) || "";
|
||
for (let transform of this.urlTransforms) {
|
||
original =
|
||
transform({
|
||
url: normalized,
|
||
urlStem: TemplatePermalink.getUrlStem(original),
|
||
}) ?? original;
|
||
}
|
||
|
||
return TemplatePermalink.normalizePathToUrl(original);
|
||
}
|
||
|
||
toPath(outputDir) {
|
||
if (!this.buildLink) {
|
||
return false;
|
||
}
|
||
|
||
let uri = this.toOutputPath();
|
||
|
||
if (uri === false) {
|
||
return false;
|
||
}
|
||
|
||
return TemplatePath.addLeadingDotSlash(TemplatePath.normalize(outputDir + "/" + uri));
|
||
}
|
||
|
||
toPathFromRoot() {
|
||
if (!this.buildLink) {
|
||
return false;
|
||
}
|
||
|
||
let uri = this.toOutputPath();
|
||
|
||
if (uri === false) {
|
||
return false;
|
||
}
|
||
|
||
return TemplatePath.addLeadingDotSlash(TemplatePath.normalize(uri));
|
||
}
|
||
|
||
static _hasDuplicateFolder(dir, base) {
|
||
let folders = dir.split("/");
|
||
if (!folders[folders.length - 1]) {
|
||
folders.pop();
|
||
}
|
||
return folders[folders.length - 1] === base;
|
||
}
|
||
|
||
static generate(dir, filenameNoExt, extraSubdir, fileExtension = "html") {
|
||
let path;
|
||
if (fileExtension === "html") {
|
||
let hasDupeFolder = TemplatePermalink._hasDuplicateFolder(dir, filenameNoExt);
|
||
|
||
path =
|
||
(dir ? dir + "/" : "") +
|
||
(filenameNoExt !== "index" && !hasDupeFolder ? filenameNoExt + "/" : "") +
|
||
"index.html";
|
||
} else {
|
||
path = (dir ? dir + "/" : "") + filenameNoExt + "." + fileExtension;
|
||
}
|
||
|
||
return new TemplatePermalink(path, extraSubdir);
|
||
}
|
||
}
|
||
|
||
export default TemplatePermalink;
|